Возвращение к основам: доступ к стручкам Kubernetes

Kubernetes — это колоссальный зверь. Вам необходимо понять множество различных концепций, прежде чем он начнет приносить пользу. Когда все будет готово, вы, вероятно, захотите открыть доступ к некоторым стручкам за пределами кластера. Kubernetes предоставляет различные способы сделать это: Я опишу их в этом посте.

Настройка

Для демонстрации я буду использовать Kind:

kind — это инструмент для запуска локальных кластеров Kubernetes с помощью «узлов» контейнеров Docker. kind был разработан в первую очередь для тестирования самого Kubernetes, но может быть использован для локальной разработки или CI.

Я буду использовать кластер из двух узлов:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30800             # 1
    hostPort: 30800                  # 1
- role: worker                       # 2
- role: worker                       # 2
Войти в полноэкранный режим Выход из полноэкранного режима
  1. Перенаправление портов, чтобы справиться с уровнем Docker VM на Mac (см. ниже).
  2. Два узла
kind create cluster -- config kind.yml
Вход в полноэкранный режим Выход из полноэкранного режима

Далее нам нужен контейнер. Он не должен просто запускаться и останавливаться: Используем последний образ Nginx, доступный на момент написания этой статьи.

В Kind мы должны предварительно загрузить изображения, чтобы они были доступны.

docker pull nginx:1.23
kind load docker-image nginx:1.23
Вход в полноэкранный режим Выход из полноэкранного режима

Наконец, я псевдоним kubetcl на k:

alias k=kubectl
Войти в полноэкранный режим Выйти из полноэкранного режима

Отсутствие внешнего доступа по умолчанию

По умолчанию доступ к внешней части кластера не предоставляется.

k create deployment nginx --image=nginx:1.23        # 1
Войти в полноэкранный режим Выйти из полноэкранного режима
  1. Создание развертывания одной капсулы

Давайте проверим, все ли в порядке:

k get pods
Войдите в полноэкранный режим Выйти из полноэкранного режима
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6c7985744b-c7cpl   1/1     Running   0          67s
Войти в полноэкранный режим Выход из полноэкранного режима

У стручка есть IP, но мы не можем связаться с ним за пределами кластера.

k get pod nginx-6c7985744b-c7cpl --template '{{.status.podIP}}'
Войти в полноэкранный режим Выход из полноэкранного режима
10.244.1.2
Войти в полноэкранный режим Выход из полноэкранного режима

Давайте подтвердим IP, запустив оболочку внутри самого стручка:

k exec -it nginx-6c7985744b-c7cpl -- /bin/bash
Войти в полноэкранный режим Выйти из полноэкранного режима
hostname -I
Войти в полноэкранный режим Выход из полноэкранного режима
10.244.1.2
Войти в полноэкранный режим Выход из полноэкранного режима

Мы не можем успешно пропинговать этот IP за пределами кластера; это внутренний IP.

Внутренние IP-адреса не являются стабильными

Мы создали развертывание. Следовательно, если мы удалим одиночную капсулу, Kubernetes обнаружит это и создаст новую, благодаря своим возможностям самовосстановления.

k delete pod nginx-6c7985744b-c7cpl
k get pods
Вход в полноэкранный режим Выход из полноэкранного режима
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6c7985744b-c6f92   1/1     Running   0          71s
Войти в полноэкранный режим Выход из полноэкранного режима

Давайте проверим его новый IP:

k exec -it nginx-6c7985744b-c6f92 -- /bin/bash
hostname -I
Войти в полноэкранный режим Выйти из полноэкранного режима
10.244.2.2
Войти в полноэкранный режим Выход из полноэкранного режима

Kubernetes создал новый pod, но его IP отличается от IP удаленного pod. Мы не можем полагаться на этот IP для связи между pod и pod. Действительно, мы никогда не должны напрямую использовать IP-адрес стручка.

Для решения этой проблемы Kubernetes предоставляет объект Service. Сервисы представляют собой стабильный интерфейс перед стручками. Kubernetes управляет связками между сервисом и его pod(s). Он связывает новые стручки и отвязывает удаленные.

Давайте откроем существующее развертывание с помощью сервиса:

k expose deployment nginx --type=ClusterIP --port=8080
Войти в полноэкранный режим Выход из полноэкранного режима

ClusterIP: Выставляет Service на внутреннем IP-адресе кластера. Выбор этого значения делает Service доступным только изнутри кластера. Это тип ServiceType по умолчанию.

— Службы публикации (ClusterIP)

k get svc
Войти в полноэкранный режим Выход из полноэкранного режима
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP    9m47s
nginx        ClusterIP   10.96.93.97   <none>        8080/TCP   4s
Войти в полноэкранный режим Выход из полноэкранного режима

С этого момента можно получить доступ к капсуле через ClusterIP службы.

Все настроено для доступа внутри кластера. Снаружи это пока невозможно. Так почему же мы будем использовать ClusteIP? Это очень полезно для сервисов, которые вы не хотите открывать внешнему миру: базы данных, узлы ElasticSearch, узлы Redis и т.д.

Доступ к стручкам

Доступ к стручкам извне кластера — вот когда все становится интересным.

Сначала нам нужно удалить существующее развертывание и службу.

k delete deployment nginx
k delete svc nginx
Вход в полноэкранный режим Выход из полноэкранного режима

Самый простой способ разрешить внешний доступ — изменить тип службы на NodePort.

NodePort: Открывает службу на IP каждого узла через статический порт (NodePort). Служба ClusterIP, к которой направляется Служба NodePort, создается автоматически. Вы сможете связаться со службой NodePort извне кластера, запросив <NodeIP>:<NodePort>.

— Службы публикации (NodePort)

Я хочу, чтобы pod возвращал свой IP и имя хоста для демонстрации. Мы должны перейти от командной строки к специальному файлу манифеста Kubernetes, потому что нам нужно настроить Nginx. В результате мы получим то же состояние, что и при использовании командной строки, с добавленной конфигурацией Nginx:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.23
        volumeMounts:                                                     # 1
          - name: conf
            mountPath: /etc/nginx/nginx.conf
            subPath: nginx.conf
            readOnly: true
      volumes:                                                            # 1
        - name: conf
          configMap:
            name: nginx-conf
            items:
              - key: nginx.conf
                path: nginx.conf
---
apiVersion: v1                                                            # 1
kind: ConfigMap
metadata:
  name: nginx-conf
data:
  nginx.conf: |
    events {
        worker_connections  1024;
    }
    http {
        server {
            location / {
                default_type text/plain;
                return 200 "host: $hostnamenIP:   $server_addrn";
            }
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  type: NodePort                                                          # 2
  ports:
    - port: 80
      nodePort: 30800
Вход в полноэкранный режим Выход из полноэкранного режима
  1. Переопределить конфигурацию по умолчанию для возврата имени хоста и IP-адреса

Давайте применим конфигурацию:

k apply -f deployment.yml
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что я работаю на Mac; следовательно, контейнер VM находится вокруг Docker, как в Windows. По этой причине Kind должен перенаправить VM на хост. Пожалуйста, ознакомьтесь с документацией о том, как этого добиться.

После того как Kubernetes запланировал pod, мы можем получить к нему доступ через настроенный порт:

curl localhost:30800
Войти в полноэкранный режим Выйти из полноэкранного режима
host: nginx-b69d8877c-p2s79
IP:   10.244.2.2
Войти в полноэкранный режим Выйти из полноэкранного режима

Путь запроса выглядит следующим образом (несмотря на слой VM на Mac/Windows):

  • Запрос curl идет к любому узлу.

Обратите внимание, что при настройке облачного провайдера вы можете адресовать запрос любому узлу Kubernetes, на котором находится часть развертывания pod. При локальной настройке мы выбираем localhost и позволяем уровню VM нацеливаться на узел.

  • Узел видит порт 30800 и пересылает запрос службе NodePort с соответствующим портом.
  • Сервис пересылает запрос в pod, переводя порт с 30800 на 80.

Теперь давайте увеличим количество стручков в нашем развертывании до двух:

k scale deployment nginx --replicas=2
k get pods -o wide
Войдите в полноэкранный режим Выход из полноэкранного режима

Kubernetes сбалансирует кластер таким образом, чтобы каждый pod располагался на отдельном узле:

NAME                    READY   STATUS    RESTARTS   AGE    IP           NODE           NOMINATED NODE   READINESS GATES
nginx-b69d8877c-w7db4   1/1     Running   0          129m   10.244.2.2   kind-worker    <none>           <none>
nginx-b69d8877c-z5kqs   1/1     Running   0          38m    10.244.1.2   kind-worker2   <none>           <none>
Войти в полноэкранный режим Выход из полноэкранного режима

На какой узел/под будет отправляться запрос?

while true; do curl localhost:30800; done
Войти в полноэкранный режим Выйти из полноэкранного режима
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
host: nginx-b69d8877c-z5kqs
IP:   10.244.1.2
host: nginx-b69d8877c-z5kqs
IP:   10.244.1.2
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
Войти в полноэкранный режим Выйти из полноэкранного режима

Служба балансирует запросы между всеми доступными узлами.

Абстракция балансировки нагрузки

NodePort позволяет обращаться к любому узлу кластера. LoadBalancer — это фасад над кластером, который выполняет… балансировку нагрузки. Это абстрактный объект, предоставляемый Kubernetes; каждый облачный провайдер реализует его по-разному, в зависимости от своих особенностей, хотя поведение одинаково.

LoadBalancer: Выставляет службу на внешний рынок, используя балансировщик нагрузки облачного провайдера. NodePort и ClusterIP Сервиса, к которым маршрутизируется внешний балансировщик нагрузки, создаются автоматически.

— Службы публикации (LoadBalander)

Во-первых, нам нужна реализация LoadBalancer. Kind имеет встроенную интеграцию с MetalLB:

MetalLB — это реализация балансировщика нагрузки для пустых кластеров Kubernetes, использующая стандартные протоколы маршрутизации.

— MetalLB

Нет смысла пересказывать отличную документацию Kind о том, как установить MetalLB. Мы можем обновить манифест соответствующим образом:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 30800
Войти в полноэкранный режим Выйти из полноэкранного режима

Давайте посмотрим на сервисы:

k get svc
Вход в полноэкранный режим Выйти из полноэкранного режима
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          4h37m
nginx        LoadBalancer   10.96.216.126   127.0.0.240   8080:31513/TCP   82m     # 1
Войти в полноэкранный режим Выход из полноэкранного режима
  1. У него есть внешний IP!

К сожалению, как я уже упоминал выше, на Mac (и Windows) Docker работает в виртуальной машине. Следовательно, мы не можем получить доступ к «внешнему» IP с хоста. Читатели с соответствующими системами Linux должны получить к нему доступ.

В зависимости от облачного провайдера, LoadBalancer может предоставлять дополнительные проприетарные возможности.

Ingress, когда вам нужна маршрутизация

Ingress фокусируется на маршрутизации запросов к сервисам в кластере.
Он разделяет некоторые аспекты с LoadBalancer:

  • Он перехватывает входящий трафик
  • Это зависит от реализации, и реализации предлагают различные возможности, например, Nginx, Traefik, HAProxy и т.д.

Однако это не Service.

Ingress открывает HTTP и HTTPS маршруты извне кластера к сервисам внутри кластера. Маршрутизация трафика контролируется правилами, определенными на ресурсе Ingress.

— Что такое Ingress?

Установка Ingress во многом зависит от реализации. Единственным общим фактором является то, что она включает CRDs.

Для демонстрации я буду использовать контроллер Apache APISIX Ingress. Я не буду пересказывать инструкции по установке. Единственное отличие заключается в установке NodePort в заданное значение:

helm install apisix apisix/apisix 
  --set gateway.type=NodePort 
  --set gateway.http.nodePort=30800 
  --set ingress-controller.enabled=true 
  --namespace ingress-apisix 
  --set ingress-controller.config.apisix.serviceNamespace=ingress-apisix
Войти в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что хотя в документации упоминается Minikube, она применима к любому локальному кластеру, включая Kind.

Следующие службы должны быть доступны в пространстве имен ingress-apisix:

NAME                        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
apisix-admin                ClusterIP   10.96.98.159   <none>        9180/TCP            22h
apisix-etcd                 ClusterIP   10.96.80.154   <none>        2379/TCP,2380/TCP   22h
apisix-etcd-headless        ClusterIP   None           <none>        2379/TCP,2380/TCP   22h
apisix-gateway              NodePort    10.96.233.74   <none>        80:30800/TCP        22h
apisix-ingress-controller   ClusterIP   10.96.125.41   <none>        80/TCP              22h
Вход в полноэкранный режим Выход из полноэкранного режима

Для демонстрации у нас будет два сервиса: каждый из них будет иметь базовое развертывание одного стручка. Запрос /left попадет на один сервис и вернет left; /right, right.

Давайте обновим топологию соответствующим образом:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: left
  labels:
    app: left
spec:
  replicas: 1
  selector:
    matchLabels:
      app: left
  template:
    metadata:
      labels:
        app: left
    spec:
      containers:
      - name: nginx
        image: nginx:1.23
        volumeMounts:
          - name: conf
            mountPath: /etc/nginx/nginx.conf
            subPath: nginx.conf
            readOnly: true
      volumes:
        - name: conf
          configMap:
            name: left-conf
            items:
              - key: nginx.conf
                path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
  name: left
spec:
  selector:
    app: left
  ports:
    - port: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: left-conf
data:
  nginx.conf: |
    events {
        worker_connections  1024;
    }
    http {
        server {
            location / {
                default_type text/plain;
                return 200 "leftn";
            }
        }
    }
Войти в полноэкранный режим Выход из полноэкранного режима

Приведенный выше фрагмент описывает только путь left; аналогичная конфигурация должна быть и для пути right.

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

apiVersion: apisix.apache.org/v2beta3            # 1
kind: ApisixRoute                                # 1
metadata:
  name: apisix-route
spec:
  http:
  - name: left
    match:
      paths:
      - "/left"
    backends:
    - serviceName: left                          # 2
      servicePort: 80                            # 2
  - name: right
    match:
      paths:
        - "/right"
    backends:
    - serviceName: right                         # 3
      servicePort: 80                            # 3
Войти в полноэкранный режим Выйти из полноэкранного режима
  1. Использовать CRD ApisixRoute, созданный при установке
  2. Перенаправить запрос на службу left
  3. Переадресовать запрос на службу right.

Вот как это должно выглядеть. Обратите внимание, что я решил представить только левый путь и один узел, чтобы не перегружать диаграмму.

Чтобы проверить, что все работает, давайте снова выполним скручивание.

curl localhost:30800
Войти в полноэкранный режим Выход из полноэкранного режима
{"error_msg":"404 Route Not Found"}
Войти в полноэкранный режим Выход из полноэкранного режима

Это хороший знак: APISIX отвечает.

Теперь мы можем попробовать скрутить путь right, чтобы убедиться, что он будет перенаправлен на соответствующий блок.

curl localhost:30800/right
Вход в полноэкранный режим Выход из полноэкранного режима
right
Вход в полноэкранный режим Выход из полноэкранного режима

/left, работает также.

Заключение

В этой заметке я описал несколько способов доступа к стручкам вне кластера: NodePort и LoadBalancer сервисы и Ingress. Что касается Ingress, вы могли заметить, что объект ApisixRoute является проприетарным CRD. Чтобы избежать этого, Kubernetes стремится предоставить абстракцию; CNCF работает над проектом Gateway API.

Я опишу его в одном из следующих постов.

Полный исходный код этой заметки можно найти на GitHub:

ajavageek / k8s-access

Идем дальше:

  • Сервисы, балансировка нагрузки и сетевое взаимодействие
  • Службы Kubernetes простое визуальное объяснение
  • Kubernetes NodePort vs LoadBalancer vs Ingress? Когда что использовать?
  • Понимание Kubernetes LoadBalancer vs NodePort vs Ingress
  • В чем разница между типами служб ClusterIP, NodePort и LoadBalancer в Kubernetes?
  • Обзор контроллера Kubernetes Ingress
  • Начало работы с Apache APISIX Ingress Controller
  • FAQ по Apache APISIX Ingress Controller

Первоначально опубликовано на A Java Geek 7 августаth, 2022

Оцените статью
devanswers.ru
Добавить комментарий