В Altenar мы часто используем RabbitMQ как точку входа для наших продуктов. В данном случае речь пойдет о системе, которая рассчитывает спортивные данные и предоставляет их как внутренним системам организации, так и внешним пользователям за её пределами. При проектировании системы для компонента RabbitMQ были определены следующие требования:
Endpoint защищён SSL-сертификатом.
RabbitMQ размещён в Kubernetes. Мы используем RabbitMQ cluster operator и RabbitMQ messaging topology operator.
-
Для этого проекта мы используем Google Cloud. Как и в случае с Let’s Encrypt, вы можете использовать управляемый SSL-сертификат Google Managed SSL Certificate для любого публичного HTTP Load Balancer в Google Cloud. Однако у такого подхода есть несколько ограничений, основные из которых:
Работает только с HTTP Load Balancer.
Работает только с публичными Load Balancer.
Согласно документации RabbitMQ, существует два распространённых подхода для подключения клиентов:
Настроить RabbitMQ для обработки TLS-подключений.
Использовать прокси или load balancer (например, HAproxy) для выполнения TLS-терминации клиентских подключений и использовать обычные TCP-подключения к узлам RabbitMQ.
Мы планировали использовать Google Managed Certificate для TCP/SSL Proxy Load Balancer в Google Cloud. Однако из-за вышеупомянутых ограничений управляемые сертификаты не поддерживаются для TCP Load Balancer. С другой стороны, Google позволяет использовать SSL-сертификаты для нескольких типов Load Balancer, что мы решили изучить. В результате итоговый проект выглядел бы следующим образом:
Мы хотели создать стандартный HTTP-сервис заглушку, который был бы доступен на порту 443 и использовался для управления SSL-сертификатом и его автоматического обновления.
При этом мы планировали иметь отдельный endpoint, который бы использовал тот же IP-адрес и тот же SSL-сертификат.
Рассмотрим каждую часть решения подробнее.
GKE Кластер
В качестве основной хостинг-платформы мы используем GKE (Google Kubernetes Engine). Мы используем Google Terraform module для настройки приватного GKE-кластера, конфигурируем Workload Identity и устанавливаем AutoNEG controller в кластер.
Мы не будем углубляться в детали работы Workload Identity с AutoNEG в этой статье, так как они используются в соответствии с официальной документацией.
После выполнения всех подготовительных шагов мы разворачиваем сервис-заглушку “Hello World” в кластере и подключаем его к Google Load Balancer с помощью AutoNEG. Ниже представлен YAML-конфигурация для этого:
apiVersion: apps/v1
kind: Deployment
metadata:
name: gcp-tls-certificate-issuer
labels:
app: gcp-tls-certificate-issuer
annotations:
deployment.kubernetes.io/revision: '1'
spec:
replicas: 2
selector:
matchLabels:
app: gcp-tls-certificate-issuer
template:
metadata:
labels:
app: gcp-tls-certificate-issuer
spec:
containers:
- name: ok
image: assemblyline/ok:latest
ports:
- containerPort: 8888
protocol: TCP
imagePullPolicy: Always
securityContext:
capabilities:
drop:
- ALL
runAsUser: 1000
runAsGroup: 3000
runAsNonRoot: true
readOnlyRootFilesystem: true
restartPolicy: Always
terminationGracePeriodSeconds: 30
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
---
apiVersion: v1
kind: Service
metadata:
name: gcp-tls-certificate-issuer
labels:
app: gcp-tls-certificate-issuer
annotations:
cloud.google.com/neg: '{"exposed_ports": {"8888":{"name": "gcp-tls-certificate-issuer"}}}'
controller.autoneg.dev/neg: '{"backend_services":{"8888":[{"name":"envcode-rabbit-https-backend-service","max_connections_per_endpoint":10000}]}}'
spec:
ports:
- name: http
protocol: TCP
port: 8888
targetPort: 8888
selector:
app: gcp-tls-certificate-issuer
clusterIP: 10.10.12.130
clusterIPs:
- 10.10.12.130
type: ClusterIP
Обратите внимание на аннотацию в конфигурации Service. Именно через неё AutoNEG добавляет этот сервис в Google Load Balancer как backend.
Google Load Balancer
Следующая часть настраивается вне GKE и создаётся отдельно. Google Load Balancer — это не единый объект, а набор различных объектов, объединённых вместе. Ниже представлен код Terraform с комментариями:
resource "google_compute_managed_ssl_certificate" "rabbitmq" {
project = var.project
name = "${var.environment_name}-google-managed-certificate-rabbitmq"
managed {
domains = ["rabitmq.example.com."] # Replace with your domain
}
}
# reserved IP address
resource "google_compute_global_address" "default" {
project = var.project
name = "tcp-proxy-xlb-ip"
}
output "rabbitmq-ip" {
value = google_compute_global_address.default.address
}
# forwarding rule for TCP Loadbalanser
resource "google_compute_global_forwarding_rule" "default" {
project = var.project
name = "${var.environment_name}-tcp-global-loadbalancer"
provider = google
ip_protocol = "TCP"
load_balancing_scheme = "EXTERNAL"
port_range = "5671"
target = google_compute_target_ssl_proxy.default.id
ip_address = google_compute_global_address.default.id
}
# https://cloud.google.com/load-balancing/docs/ssl
# When you use Google-managed SSL certificates with SSL Proxy Load Balancing, the frontend port for traffic must be 443 to enable the Google-managed SSL certificates to be provisioned and renewed.
# forwarding rule for HTTPS Loadbalanser
resource "google_compute_global_forwarding_rule" "https" {
project = var.project
name = "${var.environment_name}-https-global-loadbalancer"
provider = google
ip_protocol = "TCP"
load_balancing_scheme = "EXTERNAL"
port_range = "443"
target = google_compute_target_ssl_proxy.https.id
ip_address = google_compute_global_address.default.id
}
resource "google_compute_target_ssl_proxy" "default" {
project = var.project
name = "${var.environment_name}-global-loadbalancer-tcp-proxy"
backend_service = google_compute_backend_service.default.id
ssl_certificates = [google_compute_managed_ssl_certificate.rabbitmq.id]
}
resource "google_compute_target_ssl_proxy" "https" {
project = var.project
name = "${var.environment_name}-global-loadbalancer-https-proxy"
backend_service = google_compute_backend_service.https.id
ssl_certificates = [google_compute_managed_ssl_certificate.rabbitmq.id]
}
# backend service For RabbitMQ Autoneg
resource "google_compute_backend_service" "default" {
project = var.project
name = "${var.environment_name}-tcp-backend-service"
protocol = "TCP"
port_name = "tcp"
load_balancing_scheme = "EXTERNAL"
timeout_sec = 10
health_checks = [google_compute_health_check.default.id]
session_affinity = "CLIENT_IP"
# We don't want TF to remove whatever was configured by AutoNEG
lifecycle {
ignore_changes = [backend]
}
}
# backend service For HTTPS Autoneg
resource "google_compute_backend_service" "https" {
project = var.project
#that's what you use in the service annotations
name = "${var.environment_name}-https-backend-service"
protocol = "TCP"
port_name = "tcp"
load_balancing_scheme = "EXTERNAL"
timeout_sec = 10
health_checks = [google_compute_health_check.https.id]
# We don't want TF to remove whatever was configured by AutoNEG
lifecycle {
ignore_changes = [backend]
}
}
resource "google_compute_health_check" "default" {
project = var.project
name = "tcp-proxy-health-check"
description = "Backend service for AutoNEG"
timeout_sec = 1
check_interval_sec = 1
tcp_health_check {
port = "5672" #use container port
}
}
resource "google_compute_health_check" "https" {
project = var.project
name = "https-proxy-health-check"
description = "Backend service for AutoNEG"
timeout_sec = 1
check_interval_sec = 1
tcp_health_check {
port = "8888" #use container port
}
}
Как вы можете видеть, мы создали один IP-адрес и один SSL-сертификат, а затем использовали их в двух правилах перенаправления (forwarding rules). Это позволило нам использовать управляемый SSL-сертификат для TCP Load Balancer.
Не забудьте настроить DNS и указать IP-адрес на правильное имя хоста, чтобы всё заработало.
Совет: В GKE есть deployment l7-default-backend
. Возможно, было бы достаточно создать сервис с аннотациями AutoNEG и направить его на pod-ы этого deployment. Попробуйте это и сообщите в комментариях, если это работает.