Стадия жизненного цикла модуля: Общедоступная версия

У модуля есть требования для установки

Пример для AWS (Network Load Balancer)

При создании балансировщика используются все доступные зоны в кластере.

В каждой зоне балансировщик получает публичный IP. Если в зоне есть instance с Ingress-контроллером, A-запись с IP-адресом балансировщика из этой зоны автоматически добавляется к доменному имени балансировщика.

Если в зоне не остается instance с Ingress-контроллером, тогда IP автоматически удаляется из DNS.

В том случае, если в зоне всего один инстанс с Ingress-контроллером, при перезапуске пода IP-адрес балансировщика этой зоны будет временно исключен из DNS.

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
 name: main
spec:
  ingressClass: nginx
  inlet: LoadBalancer
  loadBalancer:
    annotations:
      service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

Пример для GCP / Yandex Cloud / Azure

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
 name: main
spec:
  ingressClass: nginx
  inlet: LoadBalancer

В GCP на узлах должна присутствовать аннотация, разрешающая принимать подключения на внешние адреса для сервисов с типом NodePort.

Пример для OpenStack

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
  name: main-lbwpp
spec:
  inlet: LoadBalancerWithProxyProtocol
  ingressClass: nginx
  loadBalancerWithProxyProtocol:
    annotations:
      loadbalancer.openstack.org/proxy-protocol: "true"
      loadbalancer.openstack.org/timeout-member-connect: "2000"

Пример для bare metal

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
  name: main
spec:
  ingressClass: nginx
  inlet: HostWithFailover
  nodeSelector:
    node-role.deckhouse.io/frontend: ""
  tolerations:
  - effect: NoExecute
    key: dedicated.deckhouse.io
    value: frontend

Пример для bare metal (при использовании внешнего балансировщика, например Cloudflare, Qrator, Nginx+, Citrix ADC, Kemp и др.)

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
  name: main
spec:
  ingressClass: nginx
  inlet: HostPort
  hostPort:
    httpPort: 80
    httpsPort: 443
    behindL7Proxy: true

Пример для bare metal (балансировщик MetalLB в режиме BGP LoadBalancer)

Доступно в следующих редакциях: EE, CSE Pro.

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
  name: main
spec:
  ingressClass: nginx
  inlet: LoadBalancer
  nodeSelector:
    node-role.deckhouse.io/frontend: ""
  tolerations:
  - effect: NoExecute
    key: dedicated.deckhouse.io
    value: frontend

В случае использования MetalLB его speaker-поды должны быть запущены на тех же узлах, что и поды Ingress–контроллера.

Контроллер должен получать реальные IP-адреса клиентов — поэтому его Service создается с параметром externalTrafficPolicy: Local (запрещая межузловой SNAT), и для принятия данного параметра MetalLB speaker анонсирует этот Service только с тех узлов, в которых запущены целевые поды.

Для этого примера конфигурация модуля metallb должна быть следующей:

metallb:
 speaker:
   nodeSelector:
     node-role.deckhouse.io/frontend: ""
   tolerations:
    - effect: NoExecute
      key: dedicated.deckhouse.io
      value: frontend

Пример для bare metal (балансировщик MetalLB в режиме L2 LoadBalancer)

Доступно в следующих редакциях: SE, SE+, EE, CSE Lite, CSE Pro.

  1. Включите модуль metallb:

    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: metallb
    spec:
      enabled: true
      version: 2
  2. Создайте ресурс MetalLoadBalancerClass:

    Metallb-балансировщики должны размещаться на тех же узлах, что и ingress-контроллеры. В типовых сценариях развертывания для этого используются frontend-узлы (для развертывания ingress-контроллеров и Metallb-балансировщиков на frontend-узлах используйте в их манифестах аннотацию node-role.deckhouse.io/frontend: "").

    apiVersion: network.deckhouse.io/v1alpha1
    kind: MetalLoadBalancerClass
    metadata:
      name: ingress
    spec:
      addressPool:
        - 192.168.2.100-192.168.2.150
      isDefault: false
      nodeSelector:
        node-role.deckhouse.io/frontend: ""
      type: L2
  3. Создайте ресурс IngressNginxController:

    apiVersion: deckhouse.io/v2
    kind: IngressNginxController
    metadata:
      name: main
    spec:
      ingressClass: nginx
      inlet: LoadBalancer
      loadBalancer:
        loadBalancerClass: ingress
        annotations:
          # Количество адресов, которые будут выделены из пула, объявленного в MetalLoadBalancerClass.
          network.deckhouse.io/l2-load-balancer-external-ips-count: "3"
      # Селектор и tolerations. Поды ingress-controller должны быть размещены на тех же узлах, что и поды MetalLB speaker.
      nodeSelector:
        node-role.deckhouse.io/frontend: ""
      tolerations:
      - effect: NoExecute
        key: dedicated.deckhouse.io
        value: frontend
        operator: Equal

    При создании ingress-контроллера также можно указать определенные IP-адреса из пула, которые будут ему присвоены. Для указания адресов, которые должны быть присвоены сервису, используйте аннотацию network.deckhouse.io/load-balancer-ips. Если желаемых адресов больше одного, то также должна присутствовать аннотация network.deckhouse.io/l2-load-balancer-external-ips-count, в которой необходимо указать количество выделяемых адресов из пула (оно не должно быть меньше количества адресов, перечисленных в network.deckhouse.io/load-balancer-ips). Пример использования аннотаций для присвоения сервису определенных адресов из пула.

  4. Платформа создаст сервис с типом LoadBalancer, которому будет присвоено заданное количество адресов:

    $ d8 k -n d8-ingress-nginx get svc
    NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP                                 PORT(S)                      AGE
    main-load-balancer     LoadBalancer   10.222.130.11   192.168.2.100,192.168.2.101,192.168.2.102   80:30689/TCP,443:30668/TCP   11s

Пример разделения доступа между публичной и административной зонами

Во многих приложениях один и тот же бэкенд обслуживает как публичную часть, так и административный интерфейс. Например:

  • https://example.com — публичная зона;
  • https://admin.example.com — административная зона, к которой доступ должен быть ограничен (ACL, mTLS, IP whitelist и т.д.).

При таком сценарии рекомендуем выносить административный трафик в отдельный Ingress-контроллер (при необходимости с отдельным Ingress-классом) и ограничивать доступ к нему с помощью параметра spec.acceptRequestsFrom.

Особенности использования одного Ingress-контроллера

Рассмотрим пример, когда для обслуживания запросов из публичной зоны и административного интерфейса используется один Ingress-контроллер.

Пример конфигурации Ingress-ресурсов для такого случая:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: admin-ingress
  annotations:
    nginx.ingress.kubernetes.io/whitelist-source-range: "1.2.3.4/32"
spec:
  ingressClassName: nginx # Ingress-ресурс для административного трафика связан с тем же Ingress-контроллером, что и Ingress-ресурс для публичного трафика.
  rules:
    - host: admin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public-ingress
spec:
  ingressClassName: nginx # Ingress-ресурс для публичного трафика связан с тем же Ingress-контроллером, что и Ingress-ресурс для административного трафика.
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 80

При включенной обработке и передаче заголовков X-Forwarded-* бэкенд может опираться на заголовок x-forwarded-host при принятии решений об авторизации. И для приведенного выше примера возможна ситуация, когда через Ingress-ресурс для обслуживания публичного трафика можно подключиться к административной зоне, используя x-forwarded-host. Поэтому при использовании этой опции вы должны быть уверены, что запросы к Ingress-контроллеру направляются только от доверенных источников.

Использование раздельных Ingress-контроллеров

Чтобы избежать ситуации, описанной выше (когда при включенной обработке и передаче заголовков X-Forwarded-* можно, например, через Ingress-ресурс для обслуживания публичного трафика подключиться к административной зоне, используя x-forwarded-host), рекомендуем:

  • настроить правила доступа на уровне Ingress-ресурсов,
  • использовать разные Ingress-контроллеры,
  • на Ingress-контроллерах ограничить, с каких адресов можно подключаться к ним.

Пример конфигурации Ingress-ресурсов для такого случая:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: admin-ingress
  annotations:
    nginx.ingress.kubernetes.io/whitelist-source-range: "1.2.3.4/32"
spec:
  ingressClassName: admin-nginx # Ingress-ресурс для административного трафика связан с отдельным Ingress-контроллером.
  rules:
    - host: admin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public-ingress
spec:
  ingressClassName: public-nginx # Ingress-ресурс для публичного трафика связан связан с отдельным Ingress-контроллером.
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 80

Пример Ingress-контроллера, который обслуживает административные Ingress-ресурсы и принимает подключения только из заданных подсетей:

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
  name: admin
spec:
  ingressClass: admin-nginx
  inlet: HostPort
  acceptRequestsFrom:
    - 1.2.3.4/32
    - 10.0.0.0/16
  hostPort:
    httpPort: 80
    httpsPort: 443
    behindL7Proxy: true

В этом примере:

  • Ingress-контроллер доступен на портах узлов через инлет HostPort;
  • Параметр acceptRequestsFrom разрешает подключение к контроллеру только из перечисленных подсетей;
  • Даже если внешний балансировщик или клиент может передавать свои значения заголовков X-Forwarded-*, решение о допуске соединения до контроллера принимается по реальному адресу подключения, а не по заголовкам.
  • Административные Ingress-ресурсы (в данном примере admin-ingress) обслуживаются этим контроллером согласно настроенному Ingress-классу.

Пример Ingress-контроллера, который обслуживает Ingress-ресурсы для публичного трафика:

apiVersion: deckhouse.io/v2
kind: IngressNginxController
metadata:
  name: public
spec:
  ingressClass: public-nginx
  inlet: HostPort
  hostPort:
    httpPort: 8080
    httpsPort: 8443
    behindL7Proxy: true

Пример использования cert-manager для заказа сертификатов

Модуль работает с сертификатами, которые выписывает cert-manager. Ingress-контроллер использует Secret, указанный в секции spec.tls Ingress-ресурса, поэтому главное требование — чтобы cert-manager создал этот Secret в пространстве имён Ingress-ресурса.

В примерах ниже используется ClusterIssuer с HTTP-01-solver. Создайте ClusterIssuer (или Issuer в нужном пространстве имён) согласно документации модуля cert-manager:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    email: admin@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

Замените адрес электронной почты и, при необходимости, ACME-сервер. При тестировании используйте staging-сервер ACME, чтобы не исчерпать лимиты Let’s Encrypt.

Для Issuer в пространстве имён используйте ту же секцию spec, укажите kind: Issuer и metadata.namespace; затем используйте аннотацию cert-manager.io/issuer или issuerRef.kind: Issuer, как показано ниже.

ingressClassName в solver должен совпадать с Ingress-классом контроллера, который обслуживает домен. В примерах ниже это nginx. Если контроллер использует другой класс, измените его и в solver, и в Ingress-ресурсе.

Для HTTP-01 домен должен указывать на Ingress-контроллер, а контроллер должен быть доступен по TCP-порту 80. Если контроллер доступен на нестандартном httpPort (см. описание httpPort в разделе inlet) или только на httpsPort, используйте DNS-01 либо пробросьте порт 80 до контроллера.

Заказ сертификата с помощью аннотации Ingress-ресурса

Добавьте аннотацию cert-manager.io/cluster-issuer в Ingress-ресурс и укажите секцию spec.tls:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  namespace: my-namespace
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - my-app.example.com
      secretName: my-app-tls
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80

cert-manager создаст ресурс Certificate на основе аннотации и секции spec.tls, выпишет сертификат и запишет его в Secret my-app-tls. Значения hosts в spec.tls — это DNS-имена, которые заказывает cert-manager; они должны совпадать с хостами в правилах Ingress-ресурса. Ingress-контроллер автоматически обнаружит Secret и начнёт обслуживать HTTPS.

Чтобы заказать сертификат у Issuer в пространстве имён, используйте аннотацию cert-manager.io/issuer: <имя-issuer>. В этом случае Issuer и Ingress-ресурс должны находиться в одном пространстве имён.

Заказ сертификата с помощью отдельного ресурса Certificate

Создайте ресурс Certificate, который ссылается на Issuer или ClusterIssuer и целевой Secret:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-app-tls
  namespace: my-namespace
spec:
  secretName: my-app-tls
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer
    group: cert-manager.io
  dnsNames:
    - my-app.example.com

Затем укажите тот же Secret в Ingress-ресурсе. Аннотация cert-manager в Ingress-ресурсе не нужна:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  namespace: my-namespace
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - my-app.example.com
      secretName: my-app-tls
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80

Этот способ удобен, если сертификат используется несколькими Ingress-ресурсами в одном пространстве имён или управляется отдельно от них. Ресурс Certificate и Ingress-ресурс должны находиться в одном пространстве имён, так как spec.tls.secretName ссылается на Secret в пространстве имён Ingress-ресурса. Для Issuer в пространстве имён укажите issuerRef.kind: Issuer; Issuer должен находиться в том же пространстве имён, что и Certificate.

В обоих случаях cert-manager создаёт Secret, который отслеживает Ingress-контроллер. При продлении сертификата cert-manager обновляет Secret, и контроллер автоматически перечитывает сертификат.

Параметр defaultSSLCertificate на уровне модуля — это отдельный резервный сертификат, для работы с cert-manager он не требуется.