В 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. Попробуйте это и сообщите в комментариях, если это работает.
