Translate

середа, 10 січня 2018 р.

Kubernetes. Part V: Configure And Use Ingress

Ця стаття вже п'ята з серії статей про Kubernetes. Перед її прочитанням рекомендую ознайомитись хоча б з базовими об'єктами кластеру та їх використанням для вирішення задач.

Сервіси Kubernetes надають можливість створення постійних точок входу до контейнерів додатків, що працюють в подах, проте IP адреси сервісів обираються з діапазону оверлейної мережі, і тому є видимими лише в межах кластеру. Тож у разі необхідності доступу до таких додатків ззовні існують наступні варіанти:

hostNetwork: true. Под, створений із такою опцією, матиме можливість бачити мережеві інтерфейси Kubernetes хосту, де його було запущено.

apiVersion: v1
kind: Pod
metadata:
  name: influxdb
spec:
  hostNetwork: true
  containers:
    - name: influxdb
      image: influxdb

Порт такого додатку буде прослуховуватись на всіх інтерфейсах вузла. Використання цієї опції не рекомендовано, адже в цьому випадку вичерпується простір портів хост-машини, що зі зростанням кількості працюючих додатків може з легкістю призвести до конфліктів. Більш того, у разі перестворення поду, він може "переселитись" на інший вузол, що додасть складності в його пошуках. Виключенням можуть слугувати поди утиліт, котрим дійсно необхідний вищезгаданий доступ задля управлінням мережею і т.п.

hostPort: integer. Ця опція дозволяє поду активувати лише один порт на всіх інтерфейсах вузла Kubernetes. Yaml для створення такого поду буде виглядати наступним чином:

apiVersion: v1
kind: Pod
metadata:
  name: influxdb
spec:
  containers:
    - name: influxdb
      image: influxdb
      ports:
        - containerPort: 8086
          hostPort: 8086

Його, як і попередній варіант, дуже не рекомендовано використовувати по тим же причинам.

nodePort. Сервіс, створений з даною опцією, активує порт з діапазону 30000-32767 на всіх вузлах Kubernetes, який в свою чергу проксується на порт додатку. Такий сервіс і под виглядатимуть так:

kind: Service
apiVersion: v1
metadata:
  name: influxdb
spec:
  type: NodePort
  ports:
    - port: 8086
      nodePort: 30000
  selector:
    name: influxdb
---

apiVersion: v1
kind: Pod
metadata:
  name: influxdb
  labels:
    name: influxdb
spec:
  containers:
    - name: influxdb
      image: influxdb
      ports:
        - containerPort: 8086

Після створення цих об’єктів, порт 30000 вузла Kubernetes буде переадресований в 8086 порт поду.

Цього варіанту могло би бути достатньо у разі використання окремого балансувальника попереду кластера, але це зовсім не зручно: у разі, наприклад, додавання нового вузла Kuberenetes чи додатка списки балансувальника необхідно оновити власноруч. Було б чудово звести таку роботу до мінімуму.

LoadBalancer. Опція для сервісів Kubernetes, що має сенс на cloud-платформах AWS, Azure, CloudStack, GCE та OpenStack. Якщо описати сервіс наступним чином:

kind: Service
apiVersion: v1
metadata:
  name: influxdb
spec:
  type: LoadBalancer
  ports:
    - port: 8086
  selector:
    name: influxdb

Kubernetes створить випадковий NodePort на внутрішній адресі оверлейної мережі (ClusterIP), потім запитає створення балансувальника cloud-платформи із зовнішньою IP-адресою і вказаним портом. Це буде виглядати так:

$ kubectl get svc influxdb
NAME       CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
influxdb   10.97.121.42   10.13.242.236   8086:30051/TCP   39s

Отже запит на IP-адресу балансувальника 10.13.242.236 і порт 8086 переадресується на внутрішню адресу кластера 10.97.121.42 і порт 30051, котрий вже після переадресується на адресу поду і порт додатка.

Мінусом цього способу є те, що, по-перше, bare-metal рішення та менш популярні cloud-провайдери не мають підтримки функціональності LoadBalancer, а, по-друге, виділення окремої IP-адреси на кожен додаток може коштувати не дешево.

UPD. Metallb надає можливість використання LoadBalancer опції у bare-metal інсталяціях. Для його роботи необхідний окремий діапазон IP-адрес, що не буде пересікатись із адресами інших вузлів в цій мережі.

MetalLB може працювати в 2-х режимах: Layer 2 та BGP (в ідеалі потребує роутера, що підтримує цей протокол). Кожна IP адреса буде виділятись одному сервісу з активованою опцією LoadBalancer-у. У режимі Layer 2 всі ці адреси лежатимуть на одному воркері/ноді Kubernetes, котрий виборов цю можливість. Це буде виглядати як купа IP-адрес призначені одному інтерфейсу. Тобто фактично це не забезпечує балансування між нодами, а лише процедуру failover.

Принцип роботи Layer 2 режиму MetalLB наступний. MetalLB Controller у разі зміни вузла, на котрому лежать IP-адреси LoadBalancer-ів (чи створення нового LoadBalancer-а) буде надсилати широкомовні ARP анонси (gratuitous ARP messages), що відбулись зміни і вузли, виходячи із отриманих анонсів, будуть змінювати власний локальний ARP-кеш (таблиця відповідністі MAC/Ethernet та IP адрес). Тобто ARP таблиця кожного вузла буде динамічно перебудовуватись, у разі міграції адреси на інший воркер, наприклад, через падіння попереднього.

Моє розуміння процесу може бути неточним. Ліпше за все переглянути офіційну документацію:

https://metallb.universe.tf/tutorial/
https://metallb.universe.tf/tutorial/layer2/
https://metallb.universe.tf/concepts/layer2/
https://metallb.universe.tf/concepts/

MetalLB наразі перебуває у статусі альфа.

Ingress. Це ресурс кластеру Kubernetes, додатковий об’єкт, що є дещо більшим ніж попередні варіанти. Окрім адресації трафіку на кінцеві поди, Ingress також має функціонал virtual хостингу (доступ до додатків Kubernetes по доменним іменам), балансування трафіку між подами, SSL-termination/sticky sessions та ін. Зупинимось детальніше на цьому варіанті.

Для роботи Ingress ресурсів необхідний відповідний контролер, який у разі bare-metal інсталяцій (така як наша з Kubespray чи Kubeadm) не встановлений по-замовчуванню. Є велика множина таких контролерів, серед яких хотілося б відзначити їх реалізації HAProxy (2 незалежних реалізації), Traefik та Nginx. У цій статті ми розглянемо лише останній варіант, а з повним списком можливих Ingress контролерів можна ознайомитись за посиланням https://github.com/kubernetes/ingress-nginx/blob/master/docs/catalog.md.

Для всіх наступних інструкцій я буду використовувати вже існуючий Kubernetes кластер версії 1.8.4, котрий залишився після роботи над попереднею статею. Цього разу ми ознайомимось з тим, як установити Nginx Ingress контролер та почнемо його використовувати.

Спочатку створимо новий неймспейс:

$ vim namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx

$ kubectl apply -f namespace.yaml
namespace "ingress-nginx" created

Опишемо деплоймент та сервіс для default backend. Ці поди в деплойменті будуть відповідати 404 кодом у разі невірних запитів до ingress контролеру. Для цілей високої доступності ми створимо 2 таких поди:

$ vim default-backend.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: default-http-backend
  labels:
    app: default-http-backend
  namespace: ingress-nginx
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: default-http-backend
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: default-http-backend
        # Any image is permissable as long as:
        # 1. It serves a 404 page at /
        # 2. It serves 200 on a /healthz endpoint
        image: gcr.io/google_containers/defaultbackend:1.4
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi
---

apiVersion: v1
kind: Service
metadata:
  name: default-http-backend
  namespace: ingress-nginx
  labels:
    app: default-http-backend
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: default-http-backend

$ kubectl apply -f default-backend.yaml
deployment "default-http-backend" created
service "default-http-backend" created

Через деякий час можна спостерігати новостворені поди:

$ kubectl get pods -n ingress-nginx -o wide
NAME                                    READY  STATUS    RESTARTS   AGE   IP            
default-http-backend-66b447d9cf-hcscc   1/1    Running   0          17s   10.233.118.47
default-http-backend-66b447d9cf-vjh9t   1/1    Running   0          17s   10.233.120.233

Додаткові конфігураційні налаштування для майбутнього контролера будуть представлені у вигляді configmap-ів:

$ vim configmaps.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app: ingress-nginx
data:
  enable-vts-status: 'true'
---

kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
---

kind: ConfigMap
apiVersion: v1
metadata:
  name: udp-services
  namespace: ingress-nginx

Єдине, що ми збираємось активувати - це додаткові метрики, що надає traffic status module. Проте за необхідності є можливість через configmap-и активувати і інші опції Nginx:

$ kubectl apply -f configmaps.yaml
configmap "nginx-configuration" created
configmap "tcp-services" created
configmap "udp-services" created

Ми проводимо налаштування для кластеру із вже активованим RBAC, тому перед виливкою контролера необхідно додатково задеплоїти ролі:

$ vim rbac.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress-serviceaccount
  namespace: ingress-nginx
---

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: nginx-ingress-clusterrole
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "extensions"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
        - events
    verbs:
        - create
        - patch
  - apiGroups:
      - "extensions"
    resources:
      - ingresses/status
    verbs:
      - update
---

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: nginx-ingress-role
  namespace: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      # Defaults to "<election-id>-<ingress-class>"
      # Here: "<ingress-controller-leader>-<nginx>"
      # This has to be adapted if you change either parameter
      # when launching the nginx-ingress-controller.
      - "ingress-controller-leader-nginx"
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - get
---

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: nginx-ingress-role-nisa-binding
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nginx-ingress-role
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx
---

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: nginx-ingress-clusterrole-nisa-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nginx-ingress-clusterrole
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx


$ kubectl apply -f rbac.yaml
serviceaccount "nginx-ingress-serviceaccount" created
clusterrole "nginx-ingress-clusterrole" created
role "nginx-ingress-role" created
rolebinding "nginx-ingress-role-nisa-binding" created
clusterrolebinding "nginx-ingress-clusterrole-nisa-binding" created

І, нарешті, задеплоїмо Ingress контролер. У цьому випадку варто звернути увагу на аргументи запуску бінарного файлу nginx-ingress-controller в однойменному контейнері, а саме, що було передано ім’я дефолтного бекенду, вказані імена configmap-ів та префікс анотацій. Звісно, що ці значення мають збігатись з попередньо створеними ресурсами.

$ vim nginx-ingress-controller_with_rbac.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx 
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ingress-nginx
  template:
    metadata:
      labels:
        app: ingress-nginx
      annotations:
        prometheus.io/port: '10254'
        prometheus.io/scrape: 'true'
    spec:
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.9.0
          args:
            - /nginx-ingress-controller
            - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --annotations-prefix=nginx.ingress.kubernetes.io
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
          - name: http
            containerPort: 80
          - name: https
            containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1

У свою чергу annotations-prefix має збігатись зі значенням annotations кожного майбутнього Ingress-a додатка. Суть в тому, що Kubernetes кластер може мати довільну кількість Ingress контролерів і annotations - це спосіб розмежовувати відповідальність між ними. Якщо ж кластер має лише один Ingress контролер - annotations змінну для Ingress-ів додатків можна не вказувати.

$ kubectl apply -f nginx-ingress-controller_with_rbac.yaml
deployment "nginx-ingress-controller" created

Знову ж, тут 2 репліки задля забезпечення високої доступності, тож падіння одного поду контролера не буде критичним.

$ kubectl get pods -n ingress-nginx | grep nginx-ingress-controller
nginx-ingress-controller-8dcfb95b9-58vsk   1/1       Running   0          22s
nginx-ingress-controller-8dcfb95b9-dz655   1/1       Running   0          22s

Усередині відповідних подів буде працювати nginx, конфігурувати який буде nginx-ingress-controller:

$ kubectl exec nginx-ingress-controller-8dcfb95b9-58vsk -n ingress-nginx -i -t -- /bin/bash
root@nginx-ingress-controller-8dcfb95b9-58vsk:/# ps aux | grep nginx
root         1  0.0  0.0   4048   748 ?        Ss   06:54   0:00 /usr/bin/dumb-init /nginx-ingress-controller --default-backend-service=ingress-nginx/default-http-backend --configmap=ingress-nginx/nginx-configuration --tcp-services-configmap=ingress-nginx/tcp-services --udp-services-configmap=ingress-nginx/udp-services --annotations-prefix=nginx.ingress.kubernetes.io
root         7  0.4  1.4  37156 25572 ?        Ssl  06:54   0:00 /nginx-ingress-controller --default-backend-service=ingress-nginx/default-http-backend --configmap=ingress-nginx/nginx-configuration --tcp-services-configmap=ingress-nginx/tcp-services --udp-services-configmap=ingress-nginx/udp-services --annotations-prefix=nginx.ingress.kubernetes.io
root        16  0.0  1.4  82304 25624 ?        S    06:54   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nobody      26  0.0  1.6 351504 29356 ?        Sl   06:54   0:00 nginx: worker process
nobody      27  0.0  1.6 351504 29332 ?        Sl   06:54   0:00 nginx: worker process

dumb-init - це система ініціалізації та нагляду за процесами в контейнер-середовищах.

Версію вилитого контролера можна дізнатись так https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md#detect-installed-version

Основне, що наразі цікавого можна побачити в конфігураційному файлі контролера /etc/nginx/nginx.conf - це умова переадресації на default backend поди:

...
upstream upstream-default-backend {
    # Load balance algorithm; empty for round robin, which is the default
    least_conn;
    keepalive 32;

    server 10.233.118.47:8080 max_fails=0 fail_timeout=0;
    server 10.233.120.233:8080 max_fails=0 fail_timeout=0;
}
...

server {
    server_name _ ;

    listen 80 default_server reuseport backlog=511;
    listen [::]:80 default_server reuseport backlog=511;
    set $proxy_upstream_name "-";
    listen 443  default_server reuseport backlog=511 ssl http2;
    listen [::]:443  default_server reuseport backlog=511 ssl http2;
    ...

    location / {

        set $proxy_upstream_name "upstream-default-backend";

        set $namespace      "";
        set $ingress_name   "";
        set $service_name   "";

        port_in_redirect off;
        client_max_body_size                    "1m";
        proxy_set_header Host                   $best_http_host;
        ...
        proxy_pass http://upstream-default-backend;
    }

    # health checks in cloud providers require the use of port 80
    location /healthz {
        access_log off;
        return 200;
    }
    ...
}

Отже у разі запиту будь-якого віртуального хосту (server_name _ ), окрім прямо описаного в nginx.conf, відбудеться адресація на upstream-default-backend upstream, котрий в свою же чергу покаже 404 помилку. Варто зауважити, що перераховані сервера upstream-а збігаються з адресами подів, котрі були вилиті як default-http-backend деплоймент.

Окрім того тут же описано повернення 200 коду на запит по шляху /healthz, що буде використовуватись у якості хелсчека для поду Kubernetes.

Трішки нижче в /etc/nginx/nginx.conf йде мова про активовану через configmap статистику nginx:

# default server, used for NGINX healthcheck and access to nginx stats
server {
    listen 18080 default_server reuseport backlog=511;
    listen [::]:18080 default_server reuseport backlog=511;
    set $proxy_upstream_name "-";
    ...

    location /nginx_status {
        set $proxy_upstream_name "internal";

        vhost_traffic_status_display;
        vhost_traffic_status_display_format html;
    }
    ...
}

Створимо новий сервіс для переадресації на локальні порти Ingress-у, адже інакше доступ до його ресурсів буде закритим:

$ vim ingress-service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    nodePort: 30080
    protocol: TCP
  - name: http-mgmt
    port: 18080
    nodePort: 32000
    protocol: TCP
  selector:
    app: ingress-nginx

$ kubectl apply -f ingress-service-nodeport.yaml
service "ingress-nginx" created

У випадку з підтримуваними cloud-провайдерами, можна використовувати опцію ExternalIP.

За посиланням http://адреса_вузла_kubernetes:32000/nginx_status з'явиться вже згадана сторінка статистики:

1. HTTP CONNECTION


Ingress-контролер налаштовано, і наразі виллємо простий додаток для демонстрації його роботи. У якості нього скористаємось готовим образом dockersamples/static-site. Його деплоймент та сервіс для Kubernetes матимуть наступний вигляд:

$ vim app1.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: app1
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
      - name: app1
        image: dockersamples/static-site
        env:
        - name: AUTHOR
          value: app1
        ports:
        - containerPort: 80
---

apiVersion: v1
kind: Service
metadata:
  name: appsvc1
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: app1

$ kubectl apply -f app1.yaml
deployment "app1" created
service "appsvc1" created

І тепер опишемо і створимо Ingress для цього додатку:

$ vim app1-ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: app1-ingress
spec:
  rules:
  - host: app1.com
    http:
      paths:
      - backend:
          serviceName: appsvc1
          servicePort: 80

Отже, всі запити, що будуть надіслані на віртуальний хост app1.com - будуть переадресовані на бекенд-сервіс appsvc1.

$ kubectl apply -f app1-ingress.yaml
ingress "app1-ingress" created

$ kubectl get ingress
NAME           HOSTS      ADDRESS   PORTS     AGE
app1-ingress   app1.com             80        27s

На цьому етапі перевіримо як оновився конфігураційний файл Ingress-контролера. З'явився новий upstream для запитів на домен app1.com:

upstream default-appsvc1-80 {
    # Load balance algorithm; empty for round robin, which is the default

    least_conn;
    keepalive 32;

    server 10.233.118.49:80 max_fails=0 fail_timeout=0;
    server 10.233.120.235:80 max_fails=0 fail_timeout=0;
}

Та власне запис сервера, що обслуговуватиме вже вищезгаданий домен:

server {
    server_name app1.com ;
    listen 80;
    listen [::]:80;
    set $proxy_upstream_name "-";

    location / {
        set $proxy_upstream_name "default-appsvc1-80";

        set $namespace      "default";
        set $ingress_name   "app-ingress";
        set $service_name   "";
        ...
        proxy_pass http://default-appsvc1-80;
    }
}

Знову ж адреси серверів в апстрімі співпадають з адресами подів додатку app1:

$ kubectl get pods -o wide
NAME                    READY     STATUS    RESTARTS   AGE       IP               NODE
app1-54cf69ff86-4jkzj   1/1       Running   0          1h        10.233.118.49    k8s-s1
app1-54cf69ff86-mjjcj   1/1       Running   0          1h        10.233.120.235   k8s-s2

Тобто адреса сервіса не бере участь у адресації, а трафік буде надсилатись прямо на кінцеві поди. Ім’я сервісу використовується лише задля побудови API-запитів по створенню коректних upstream-ів Nginx.

Маштабування додатку звісно також вплине на вигляд Nginx апстріму default-appsvc1-80.

Звісно резолвінг всіх доменів, що будуть приведені в цій статті, мають виконуватись в адреси вузлів (або вузла) Kubernetes. Я це робив через /etc/hosts.

Переглянемо роботу щойно створеного додатку за посиланням http://app1.com:30080/
30080 - це порт сервісу, котрий було відкрито як nodePort для переадресації на локальний порт інгресу.

Як я вже згадував, через анотації можна активувати додаткові опції інгреса. Наприклад, таким чином можна переадресувати url-додаток на адресу http://app-redirect.com:30080/redirect:

$ vim app1-ingress-redirect.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: app1-ingress-redirect
spec:
  rules:
  - host: app-redirect.com
    http:
      paths:
      - backend:
          serviceName: appsvc1
          servicePort: 80
        path: /redirect

З усім багатством можливих анотацій можна ознайомитись за посиланням https://github.com/kubernetes/ingress-nginx/blob/nginx-0.9.0/docs/user-guide/annotations.md. Проте, якщо і цього замало, варто редагувати Nginx-темплейт.

Перше, що варто усвідомити, це те, що ingress-nginx розвивається дуже активно. В статті розглянуто установку версії 0.9.0, проте нові версії можуть мати іншу процедуру інсталяції та конфігурації. Наприклад, останній Ingress-nginx (0.16.2) має зовсім інший формат анотацій для активації basic auth, редіректів і інших опцій. Тому не варто бездумно копіювати конфігураційні файли, а ліпше звернутися до останніх версій документації.

Другу річ, котру варто усвідомити - це що навіть Ingress-nginx (тобто Ingress контролер на основі Nginx) може мати різні реалізації. У статті наведено роботу із Ingress-nginx від Kubernetes, проте є, наприклад, від компанії Nginx Inc. Вони мають купу відмінностей і це варто розуміти, щоб не втратити даремно час на конфігурацію.

2. HTTPS (TLS) CONNECTION


Налаштування доступу до додатків по HTTPS-протоколу дещо відрізняється від варіанту без шифрування. Для цього згенеруємо самопідписний TLS-сертифікат та ключ, CN яких має збігатись із віртуальним доменом майбутнього додатка:

# openssl req -x509 -nodes -days 3650 -newkey rsa:4096 -keyout app2-tls.key -out app2-tls.crt -subj "/CN=app2.com/O=appsvc2"
Generating a 4096 bit RSA private key
...
writing new private key to 'app2-tls.key'
-----

Або ж використаємо вже згенеровану коректну пару і додамо їх як Kubernetes секрет:

$ kubectl create secret tls app2-tls-secret --key app2-tls.key --cert app2-tls.crt
secret "app2-tls-secret" created

У якості додатка задеплоїмо новий app2, з того ж образу dockersamples/static-site:

$ vim app2.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: app2
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
      - name: app2
        image: dockersamples/static-site
        env:
        - name: AUTHOR
          value: app2
        ports:
        - containerPort: 80
---

apiVersion: v1
kind: Service
metadata:
  name: appsvc2
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: app2

$ kubectl apply -f app2.yaml
deployment "app2" created
service "appsvc2" created

А вже при стоворенні Ingress-а для нього укажемо додані до секрету app2-tls-secret сертифікат та ключ:

$ vim app2-ingress-tls.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: app2-ingress-tls
spec:
  tls:
    - hosts:
      - app2.com
      # This assumes tls-secret exists and the SSL
      # certificate contains a CN for app2.com
      secretName: app2-tls-secret
  rules:
    - host: app2.com
      http:
        paths:
        - backend:
            # This assumes appsvc2 exists and routes to healthy endpoints
            serviceName: appsvc2
            servicePort: 80

$ kubectl apply -f app2-ingress-tls.yaml
ingress "app2-ingress-tls" created

Відредагуємо ingress-nginx сервіс і додамо до нього секцію https із новим портом переадресування 30443:

$ vim ingress-service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    nodePort: 30080
    protocol: TCP
  - name: http-mgmt
    port: 18080
    nodePort: 32000
    protocol: TCP
  - name: https
    port: 443
    nodePort: 30443
    protocol: TCP
  selector:
    app: ingress-nginx

Новий додаток має з'явитись за посиланням https://app2.com:30443/
Окрім нового upstream default-appsvc2-80, з'явився також новий сервер, що вже працюватиме зі згенерованим сертифікатом і ключем:

server {
    server_name app2.com ;

    listen 80;
    listen [::]:80;

    set $proxy_upstream_name "-";

    listen 443  ssl http2;
    listen [::]:443  ssl http2;

    # PEM sha: 91523b09cd85d2ffe0e638ce6a0f1329f4647677
    ssl_certificate                 /ingress-controller/ssl/default-app2-tls-secret.pem;
    ssl_certificate_key             /ingress-controller/ssl/default-app2-tls-secret.pem;

    ssl_trusted_certificate         /ingress-controller/ssl/default-app2-tls-secret-full-chain.pem;
    ...

    location / {

        set $proxy_upstream_name "default-appsvc2-80";

        set $namespace      "default";
        set $ingress_name   "app2-ingress-tls";
        set $service_name   "";
        ...

        # In case of errors try the next upstream server before returning an error
        proxy_next_upstream             error timeout invalid_header http_502 http_503 http_504;

        proxy_pass http://default-appsvc2-80;
    }
}

Отже, у нас є додатки, на які адресування відбувається лише у разі запитів на відповідні доменні імена та 30080/30443 порти. Але використання для http(s) портів відмінних від 80/443 зовсім не зручна практика та і не зрозуміло що робити із точкою входу для кожного домену додатку... Можна просто встановити зовнішній балансувальник (на кшталт HAProxy, Nginx), як радить стаття, попереду Kubernetes кластеру, а домени додатків на DNS-сервері прив'язувати до IP адреси балансувальника. У пулах балансувальника будуть описані адреси Kubernetes воркерів та nodePort сервісів до інгресу додатків.


Поcилання:
https://habr.com/company/southbridge/blog/358824/
https://kubernetes.io/docs/concepts/services-networking/ingress/
https://github.com/kubernetes/ingress-nginx
https://github.com/kubernetes/ingress-nginx/blob/master/deploy/README.md#mandatory-commands
https://github.com/kubernetes/ingress-nginx/blob/master/deploy/README.md#baremetal
https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/tls-termination
https://crondev.com/kubernetes-nginx-ingress-controller/
http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster/
https://daemonza.github.io/2017/02/13/kubernetes-nginx-ingress-controller/
https://medium.com/@cashisclay/kubernetes-ingress-82aa960f658e
https://docs.traefik.io/configuration/backends/kubernetes/
http://docs.traefik.io/user-guide/kubernetes/
https://medium.com/@patrickeasters/using-traefik-with-tls-on-kubernetes-cb67fb43a948
https://github.com/kubernetes/ingress-nginx/blob/master/docs/catalog.md
https://www.haproxy.com/blog/haproxy_ingress_controller_for_kubernetes/
https://www.shellhacks.com/create-csr-openssl-without-prompt-non-interactive/

Немає коментарів:

Дописати коментар