В 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, существует два распространённых подхода для подключения клиентов:

  1. Настроить RabbitMQ для обработки TLS-подключений.

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

Комментарии (0)