Home » Devops » Работа с кластером Kubernetes

Работа с кластером Kubernetes

В прошлой статье я рассказал о том, как установить кластер Kubernetes на свое железо. Сегодня я покажу, как можно с ним работать - запускать различные контейнеры, устанавливать лимиты, регулировать запуск и восстановление и т.д. По сути, опишу быстрый старт и настройку в kubernetes - на конкретных примерах покажу, с чего начинать освоение kubernetes и как его использовать на практике.

Если у вас есть желание научиться администрировать системы на базе Linux, рекомендую познакомиться с онлайн-курсом «Linux для начинающих» в OTUS. Курс для новичков, для тех, кто с Linux не знаком. Подробная информация.

Цели статьи

  1. Рассказать об основных абстракциях в kubernetes.
  2. На примерах показать, как запускать, управлять контейнерами в кластере.
  3. Описать механизм отказоустойчивости, реализованный в кубернетис.
  4. Настроить обработку запросов в кластер из вне и запустить его в работу.
  5. Задеплоить реальное микросервисное приложение.

Введение

Сразу поделюсь ссылкой на официальную документацию по kubernetes. Она хорошо структурирована и наполнена. Пользоваться ей удобно. С ее помощью настраивать кластер проще. Напоминаю, что мы будем работать с кластером, который установили по предыдущей статье - установка kubernetes. Перед тем, как начать работать с кластером, расскажу об одной полезной возможности. Есть команда:

# kubectl completion bash

Она формирует конфиг для настройки автодополнения команд в bash. Вывод этой команды нужно добавить в ваш ~/.bashrc Можно это сделать автоматически.

# kubectl completion bash >> ~/.bashrc

Чтобы автодополнение работало, нужен пакет bash-completion.

# yum install bash-completion

Запуск контейнера nginx в kubernetes

Начнем с самого простого - создадим один pod, в котором запустим контейнер с последней версией nginx. POD в терминологии kubernetes - это набор контейнеров, объединенных между собой общим linux namespace. Самое важное тут то, что все контейнеры в одном поде связаны между собой сетью в рамках общего localhost. Они не могут использовать один и тот же порт. По сути, кубернетис работает не с докер контейнерами, а именно с подами.

Создаем файл pod-nginx.yaml следующего содержания:

---
apiVersion: v1
kind: Pod
metadata:
  name: pod-nginx
spec:
  containers:
  - image: nginx:1.16
    name: nginx
    ports:
    - containerPort: 80

Внимательно следите за пробелами в начале строк. Нужны именно пробелы, а не табуляция. В Yaml файлах очень важна структура. Запускаем получившийся pod.

# kubectl create -f pod-nginx.yaml
pod/pod-nginx created

Сразу сделаю пояснение насчет команды create. Вместо нее можно, а чаще всего и нужно, так как удобнее, использовать команду apply. Create создает новую абстракцию. Если вы ее измените и снова создадите с тем же именем, то получите ошибку. Вам придется сначала ее удалить, а потом создать заново. Команда apply сначала проверяет наличие абстракции, в данном случае pod-nginx и если ее нет, то создает. А если она уже есть, то просто перезапускает существующую с новыми параметрами.

Проверяем, что получилось.

# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
pod-nginx   1/1     Running   0          2m32s

В настоящий момент мы запустили в кластере kubernetes один контейнер с nginx. Существует команда edit, которая позволяет редактировать сущности кластера kubernetes в режиме реального времени. Применение изменений произойдет сразу же после сохранения изменений. Пример:

# kubectl edit pod pod-nginx

Запуск docker контейнера в kubernetes

Вы увидите полный конфиг пода, который использует кластер. В нем намного больше информации, нежели в вашем yaml файле.

Удалить созданный pod можно следующей командой:

# kubectl delete pod pod-nginx
pod "pod-nginx" deleted

Или сразу все поды:

# kubectl delete pod --all

Отказоустойчивость подов с помощью ReplicaSet

Теперь создадим несколько подов с nginx. Для этого нужно использовать другую абстракцию кубернетис. ReplicaSet следит за количеством подов по шаблону, который мы указываем. В этом шаблоне мы можем указать метку нашего приложения, в моем примере это my-nginx, и количество запущенных реплик.

---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.16
        name: nginx
        ports:
        - containerPort: 80

Запускаем replicaset.

# kubectl apply -f replicaset-nginx.yaml

Проверяем, что получилось.

# kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
replicaset-nginx   2         2         2       18m

Нам нужно было 2 реплики и мы их получили. С помощью edit мы можем на ходу редактировать replicaset, к примеру, добавляя количество реплик.

# kubectl edit replicaset replicaset-nginx

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

# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
pod-nginx                1/1     Running   0          15m
replicaset-nginx-f87qf   1/1     Running   0          22m
replicaset-nginx-mr4kw   1/1     Running   0          22m

# kubectl delete pod replicaset-nginx-f87qf
pod "replicaset-nginx-f87qf" deleted

# kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
replicaset-nginx   2         2         2       23m

# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
pod-nginx                1/1     Running   0          16m
replicaset-nginx-g4l58   1/1     Running   0          14s
replicaset-nginx-mr4kw   1/1     Running   0          23m

Я удалил replicaset-nginx-f87qf, вместо него тут же был запущен новый replicaset-nginx-g4l58. Наглядный пример одного из механизмов отказоустойчивости в кластере kubernetes на уровне подов. Кубернетис будет следить за количеством реплик на основе их label. Если вы через replicaset указали запустить 2 реплики приложения с меткой my-nginx, ни меньше, ни больше подов с этой меткой вы запустить не сможете. Если вы вручную запустите pod с меткой my-nginx, он будет тут же завершен, если у вас уже есть 2 пода с такими метками от replicaset.

Replicaset запускает поды на разных нодах. Проверить это можно, посмотрев расширенную информацию о подах.

# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
replicaset-nginx-cmfnh   1/1     Running   0          2m26s    10.233.67.6   kub-node-1   <none>           <none>
replicaset-nginx-vgxfl   1/1     Running   0          2m26s    10.233.68.4   kub-node-2   <none>           <none>

Таким образом, если у вас одна нода выйдет из строя, кластер автоматически запустит умершие поды на новых нодах. При этом, если нода вернется обратно с запущенными подами на ней, kubernetes автоматически прибьет лишние поды, чтобы их максимальное количество соответствовало информации из шаблона replicaset.

Удаляются replicaset так же как и поды.

# kubectl delete rs replicaset-nginx
replicaset.extensions "replicaset-nginx" deleted

Вместо длинного replicaset я использовал сокращение rs. Это бывает удобно для абстракций с длинными названиями. Для всех них кубернетис поддерживает сокращения.

Deployment - деплой в кластер

Переходим к следующей абстракции Kubernetes - Deployment. Они управляют наборами replicaset для непрерывного обновления подов. Покажу на примере, о чем идет речь. Допустим, у вас вышло обновление приложения в новом контейнере. Вам нужно не останавливая сервис выкатить обновление в прод. Если вы измените версию контейнера в шаблоне replicaset, автоматически он у вас не обновится. Да, если pod со старой версией контейнера упадет, новый будет создан уже с новой версией. Но все работающие поды останутся на старой версии.

Deployment как раз и нужен для управления репликасетами, задавая им стратегию обновления. У вас вышла новая версия контейнера, вы заменяете в текущем deployment версию контейнера и он по заранее настроенным правилам начинает перезапуск репликасетов и соответственно подов в них. Покажу на примере nginx, который мы откатим на предыдущую версию. Создаем yaml файл с deployment.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-nginx
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.16
        name: nginx
        ports:
        - containerPort: 80

Запускаем его.

# kubectl apply -f deployment-nginx.yaml 
deployment.apps/deployment-nginx created

Смотрим список подов и репликасетов.

# kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
deployment-nginx-785b6d8d9f-dr4cv   1/1     Running   0          55s
deployment-nginx-785b6d8d9f-m47tr   1/1     Running   0          55s
# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
deployment-nginx-785b6d8d9f   2         2         2       58s

Проверяем версию nginx в одном из подов.

# kubectl describe pod deployment-nginx-785b6d8d9f-dr4cv

kubectl describe pod

Теперь откатимся на предыдущую версию nginx 1.15. Для этого вносим изменения в Deployment.

# kubectl set image deployment deployment-nginx nginx=nginx:1.15

Проверяем список подов и репликасетов.

# kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
deployment-nginx-68d778658b-fz5lt   1/1     Running   0          11s
deployment-nginx-68d778658b-mpkg5   1/1     Running   0          10s
[root@kub-master-1 ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
deployment-nginx-68d778658b   2         2         2       15s
deployment-nginx-785b6d8d9f   0         0         0       5m57s

Название подов и репликасета изменились. Появился новый replicaset, а старый остался с погашенными подами, у него параметр replicas стал 0. Проверяем версию nginx в поде.

# kubectl describe pod deployment-nginx-68d778658b-fz5lt

Изменение версии контейнера в deployment

Версия nginx изменилась во всех подах. Стратегия обновления описана в шаблоне деплоймента в разделе strategy. В данном случае используется тип RollingUpdate, когда deployment постепенно уменьшает количество реплик старой версии и увеличивает реплики новой версии, пока они все не заменят старые. При этом replicaset с предыдущей версией контейнера осталась. Она не удаляется для того, чтобы можно было потом оперативно вернуться на предыдущую версию, если с новой будет что-то не так. Для этого достаточно будет по очереди погасить поды новой replicaset и запустить старые. Делается это следующим образом.

# kubectl rollout undo deployment deployment-nginx
deployment.extensions/deployment-nginx rolled back

Проверяем наши replicaset.

# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
deployment-nginx-68d778658b   0         0         0       12m
deployment-nginx-785b6d8d9f   2         2         2       18m

Видим, что был снова запущен предыдущий replicaset с прошлой версией контейнера. По-умолчанию хранятся 10 версий прошлых replicaset.

Проверки доступности Probes

С деплоем и перезапуском подов не все так просто. Есть тяжелые приложения, которые стартуют очень долго, либо зависят от других приложений. Прежде чем погасить старую версию, нужно убедиться, что новая уже запущена и готова к работе. Для этого существует liveness и readiness проверки. Покажу на примере. Берем предыдущий deployment и добавляем туда проверки.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-nginx
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.16
        name: nginx
        ports:
        - containerPort: 80
        readinessProbe:
          failureThreshold: 5
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          successThreshold: 2
          timeoutSeconds: 3
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 3
          initialDelaySeconds: 10

В данном примере используется проверка httpGet по корневому урлу на порт 80.

  • readinessProbe проверяет способность приложения начать принимать трафик. Она делает 5 проверок (failureThreshold). Если хотя бы 2 (successThreshold) из них будут удачными, считается, что приложение готово. Метод httpGet проверяет код ответа веб сервера. Если он 200 или 3хх, то считается, что все в порядке. Эта проверка выполняется до тех пор, пока не будет выполнено заданное на успех условие - successThreshold. После этого прекращается.
  • livenessProbe выполняется постоянно, следя за приложением во время его жизни. В моем примере проверка будет неудачной, если 3 (failureThreshold) проверки подряд провалились. При этом, если хотя бы одна (successThreshold) будет удачной, то счетчик неудачных сбрасывается. Параметр initialDelaySeconds задает задержку после старта пода для начала liveness проверок.

Вот еще один пример liveness проверки, но уже по наличию файла. Проверяется файл /tmp/healthy, если он существует, проверка удачна, если его нет, то ошибка.

    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

Создавать этот файл может само приложение во время работы.

Resources - настройка ресурсов

Расскажу, как ограничиваются выделяемые для подов вычислительные ресурсы. Речь идет про CPU и Оперативную память. Задать верхнюю планку использования ресурсов можно с помощью Limits. А с помощью Requests мы можем зарезервировать необходимые ресурсы для пода на ноде. Если в Requests у пода параметры выше, чем есть свободных ресурсов у ноды, то под не сможет приехать на эту ноду.

Важно понимать, что реквесты никак не следят за реальным использованием ресурсов. То есть это просто пожелание к ресурсам ноды, где будет размещаться под. При этом после размещения он сможет занять ресурсов больше, чем указано в Requests. Кластер kubernetes за этим не следит. Если реквесты вообще не указать, то под может приехать на ноду, где свободно очень мало ресурсов, а ему для работы надо больше. В итоге он будет падать. Таким образом, requests используются для планирования ресурсов кластера.

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

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-nginx
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.16
        name: nginx
        ports:
        - containerPort: 80
        readinessProbe:
          failureThreshold: 5
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          successThreshold: 2
          timeoutSeconds: 3
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 3
          initialDelaySeconds: 10
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 300m
            memory: 512Mi

Память выделяется в мегабайтах, а вот CPU в милли cpu, что равно 1/1000 от процессоронго ядра. То есть 1000 миллицпу это одно ядро процессора ноды. Запустим наш deployment и посмотрим на один из подов.

# kubectl describe pod deployment-nginx-79cd6dc79c-rlhtg

Настройка лимитов в кластере

Вот они, наши проверки и лимиты с реквестами.

Монтирование конфигов через ConfigMap

Абстракция kubernetes configmap придумана для того, чтобы отвязать конфиги контейнеров docker от deployment, чтобы не раздувать их размер. К примеру, нам нужно во все контейнеры с nginx положить конфиг default.conf. Мы для этого создаем configmap, в нем настраиваем желаемый конфиг и потом подключаем его в deployment. Показываю на примере. Создаем файл configmap.yaml.

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap-nginx
data:
  default.conf: |
    server {
        listen       80 default_server;
        server_name  _;

        default_type text/plain;

        location / {
            return 200 '$hostname\n';
        }
    }

В данном случае это простейший конфиг для nginx, который при обращении к серверу будет отдавать 200-й код ответа и имя сервера. Теперь подключаем его к нашему deployment из предыдущих примеров.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-nginx
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - image: nginx:1.16
        name: nginx
        ports:
        - containerPort: 80
        readinessProbe:
          failureThreshold: 5
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          successThreshold: 2
          timeoutSeconds: 3
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 3
          initialDelaySeconds: 10
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: config
          mountPath: /etc/nginx/conf.d/
      volumes:
      - name: config
        configMap:
          name: configmap-nginx

Мы создаем монтирование с именем config по пути /etc/nginx/conf.d/ и связываем это монтирование с configmap, который ранее создали. Теперь применяем сначала configmap, а затем deployment.

# kubectl apply -f configmap.yaml 
configmap/configmap-nginx created
# kubectl apply -f deployment-nginx-confmap.yaml 
deployment.apps/deployment-nginx configured

Проверяем, что получилось. Я подключусь к одному из подов и прочитаю там конфигурацию nginx.

Настройка configmap в kubernetes

Видим, что применился наш configmap. На втором поде будет то же самое.

Проброс порта в pod

А сейчас пробросим 80-й порт мастера в конкретный под и проверим, что nginx действительно работает в соответствии с установленным конфигом. Делается это следующим обарзом.

# kubectl port-forward deployment-nginx-848cc4c754-w7q9s 80:80
Forwarding from 127.0.0.1:80 -> 80
Forwarding from [::1]:80 -> 80

Перемещаемся в сосeднюю консоль мастера и там проверяем через curl.

# curl localhost:80
deployment-nginx-848cc4c754-w7q9s

Если сделать проброс в другой под и проверить подключение, вы получите в ответ на запрос curl на 80-й порт мастера имя второго пода. На практике, я не знаю, как можно использовать данную возможность. А вот для тестов в самый раз.

Проброс порта в kubernetes

Service - балансировка сети в kubernetes

Service в Kubernetes по своей сути это некий балансировщик на уровне L3. С помощью сервисов мы можем попасть в нужные нам поды, обратившись к ним по имени или по ip адресу. Перераспределение запросов идет по алгоритму round-robin. Давайте сделаем сервис к нашему deployment из предыдущих примеров.

---
apiVersion: v1
kind: Service
metadata:
  name: service-nginx
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: my-nginx
  type: ClusterIP

Параметр ClusterIP означает, что будет использоваться виртуальная сеть кластера. Доступ к сервису будет доступен только внутри кластера. Извне доступ закрыт. С помощью selector app my-nginx мы привязали сервис к приложению с именем my-nginx, которое мы использовали в предыдущих примерах. Применяем service в кластере.

# kubectl apply -f service.yaml 
service/service-nginx created

И смотрим, что получилось.

# kubectl get service
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes      ClusterIP   10.233.0.1     <none>        443/TCP   7d12h
service-nginx   ClusterIP   10.233.43.79   <none>        80/TCP    29s

Чтобы проверить работу сети в кластере, удобно запустить в нем специальный docker образ, где внутри собраны всякие сетевые утилиты, с помощью которых можно попинговать, посмотреть маршруты и т.д. Можете какой-то свой браз для этого использовать, или воспользоваться готовым - amouat/network-utils. Запустим его напрямую в кластере с параметрами, чтобы он удалился сразу после выхода из него. Ключи здесь такие же как в докере.

# kubectl run -t -i --rm --image amouat/network-utils testnet bash
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.

Нажимайте enter и попадете в контейнер. А дальше пробуйте пинговать.

Настройка service

После выхода из контейнера, он удалится.

# exit
exit
Session ended, resume using 'kubectl attach testnet-9d4b6c988-8lmv8 -c testnet -i -t' command when the pod is running
deployment.apps "testnet" deleted

Настройка Ingress

Описанные выше сервисы решают задачу взаимодействия внутри кластера kubernetes, но нам нужно еще и c внешними пользователями взаимодействовать. Для этого настроим Ingress, который мы установили ранее в виде отдельной роли на ноде. По своей сути это обычный nginx, который будет получать конфигурацию из yaml файла. Рисуем конфиг для ingress, который будет пробрасывать запросы из вне на сервис service-nginx, который мы создали на предыдущем шаге.

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-nginx
spec:
  rules:
  - host: nginx.cluster.local
    http:
      paths:
      - backend:
          serviceName: service-nginx
          servicePort: 80

Загружаем конфиг в кластер кубернетиса.

# kubectl apply -f ingress.yaml
ingress.extensions/ingress-nginx configured

Смотрим, что получилось.

# kubectl get ingress -o wide
NAME            HOSTS                 ADDRESS     PORTS   AGE
ingress-nginx   nginx.cluster.local   10.1.4.39   80      3m20s

10.1.4.39 - ip адрес ноды ingress. В принципе, это сразу же может быть внешний ip, который будет принимать на себя все запросы. Так как это nginx, должно быть безопасно и надежно. На практике, я не знаю, делают ли так, но не вижу каких-то весомых причин в типовых сценариях этого не делать. Чтобы проверить работу ingress, нам надо добавить dns запись.

10.1.4.39 nginx.cluster.local

Я просто в локальный hosts машины добавил и проверил.

Проверка работы Ingress

Если обновлять страничку, имя пода будет меняться в соответствии с настройкой балансировки. Она выполняется по алгоритму least_conn.

Более подробно настройку ingress контроллера я рассмотрел в отдельной статье.

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

Deploy реального приложения в кластер

Давайте теперь на реальном примере попробуем что-то запустить в кластере kubernetes. Я предлагаю для этого использовать демо магазин носков из этого репозитория - https://github.com/microservices-demo/microservices-demo. Там есть длиннющий yaml файл, который содержит в себе все необходимое (deployments, service и т.д.) для запуска магазина. Магазин состоит из множества компонентов, так что мы на практике убедимся, как легко и быстро можно деплоить сложные приложения в кластер.

Магазин настроен на работе в отдельном namespace - sock-shop. Его предварительно надо создать.

# kubectl create namespace sock-shop

Запускаем деплой всего проекта одной командой.

# kubectl apply -n sock-shop -f "https://raw.githubusercontent.com/microservices-demo/microservices-demo/master/deploy/kubernetes/complete-demo.yaml"

Наблюдать за поднятием подов можно командой в реальном времени.

# kubectl get pods -n sock-shop -w

После того, как они все станут Running можно проверять работу. В этом проекте не используется ingress, поэтому чтобы понять, как подключиться к магазину, надо провести небольшое расследование. Для начала посмотрим запущенные service.

# kubectl get service -n sock-shop -o wide

Список запущенных сервисов

Нас интересует тип NodePort, так как к нему можно подключаться из вне. Видим, что порт используется 30001 и имя приложения front-end. Посмотрим, где оно запущено.

# kubectl get pod -n sock-shop -o wide

Список pod в определенном namespace

Этот pod запущен на kub-node-2. Посмотрим ее ip.

# kubectl get node -o wide

IP адреса нод кластера

Ее ip адрес - 10.1.4.33. Значит для проверки магазина надо идти по урлу http://10.1.4.33:30001/

Deploy приложения в кластер kubernetes

Вот он, наш магазин. Для удобства, можем сами доделать доступ через ingress по доменному имени. Настраиваем конфиг ingress.

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-sock
  namespace: sock-shop
spec:
  rules:
  - host: sock-shop.cluster.local
    http:
      paths:
      - backend:
          serviceName: front-end
          servicePort: 80

Не забывайте указывать нужный namespace и правильное имя сервиса, для которого настраиваем ingress. Применяем конфиг.

# kubectl apply -f ingress-sock.yaml

Смотрим, что получилось.

# kubectl get ingress -n sock-shop -o wide
NAME           HOSTS                     ADDRESS     PORTS   AGE
ingress-sock   sock-shop.cluster.local   10.1.4.39   80      29s

Редактируем файл hosts и идем в браузер проверять.

Настройка ingress для магазина носков

Поздравляю, ваш кластер работает, а вы теперь администратор кластера Kubernetes и инженер yaml файлов :) У вас теперь будет большая дружба с лапшеподобным синтаксисом. Идите к руководству и требуйе прибавки к зарплате минимум на 30%.

Заключение

Не понравилась статья и хочешь научить меня администрировать? Пожалуйста, я люблю учиться. Комментарии в твоем распоряжении. Расскажи, как сделать правильно!

Я рассмотрел основные абстракции kubernetes, которые нужны для того, чтобы на нем хоть что-то запустить и начать работать. Кластер в таком виде уже способен принимать запросы из вне. Для полноты картины не хватает одного объемного раздела - фаловые хранилища. Эта отдельная большая тема, поэтому я решил ее не затрагивать здесь, а вынести в отдельную статью, которая будет следующей в этом цикле. В таком виде, как описано здесь, используются локальные диски нод кластера, где запущены контейнеры docker.

Если вам подходит такой формат работы - пользуйтесь. По большому счету, он вполне жизнеспособен, если у вас все полезные данные, к примеру, хранятся в самих контейнерах в реджистри и в базе данных, которая работает не в кластере, а медиаконтент на внешних CDN. В таком случае kubernetes будет работать как масштабируемый вычислительный кластер.

Для автоматического деплоя приложений в кластер k8s можно использовать helm. Я рассмотрел этот вопрос в отдельной статье - Работа с Helm 3 в Kubernetes.

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

Если у вас есть желание научиться администрировать системы на базе Linux, но вы с ними никогда не работали и не знакомы, то рекомендую начать с онлайн-курса «Linux для начинающих» в OTUS. Курс для новичков, для тех, кто с Linux не знаком. Цена за курс минимальная (символическая). Информация о курсе и цене.

Помогла статья? Подписывайся на telegram канал автора

Анонсы всех статей, плюс много другой полезной и интересной информации, которая не попадает на сайт.

Автор Zerox

Владимир, системный администратор, автор сайта. Люблю настраивать сервера, изучать что-то новое, делиться знаниями, писать интересные и полезные статьи. Открыт к диалогу и сотрудничеству. Если вам интересно узнать обо мне побольше, то можете послушать интервью. Запись на моем канале - https://t.me/srv_admin/425 или на сайте в контактах.

19 комментариев

  1. Аноним

    Огромное спасибо за статью! Все очень сжато, четко и понятно, с показательными примерами! Очень круто!

  2. Добрый день!
    А как получить доступ в к сервисам снаружи?
    Использую type=NodePort и IngressController, но что-то не хочет . . .

  3. Про пробы описаны не правильно

  4. Аноним

    Один момент. Readiness проба исполняется всё время работы пода, а не завершается.

  5. Коллеги, в статье не нашел информации. А как дать доступ для Jenkins? В Yutube вижу подключают secret file, беря в качестве него config. А как обстоит дело в случае kubespray?

    • Если не ошибаюсь, все необходимое для подключения к кластеру есть в директории /etc/kubernetes/ мастера, с которого была установка. С именем директории могу ошибаться, но она точно в /etc. Там и конфиги и сертификаты для подключения. Точно проверить нет кластера под рукой. Это что касается доступу к кластеру. Как именно jenkins к нему подключать, не знаю. Да и что значит дать доступ для Jenkins? Он что должен в кластере делать? Деплоить приложения?

      • Спасибо.
        >Да и что значит дать доступ для Jenkins? Он что должен в кластере делать? Деплоить приложения?
        Именно.

  6. Спасибо за цикл статей про k8s. Пользовался ими при настройке кластера. Есть один вопрос, который пока не удается решить. Может здесь кто подскажет.
    Каким образом передать свой домен в контейнер кубера, при деплое пода чтобы в файле /etc/hosts была запись типа такой:

    1.2.3.4 test.domain.com test

    Сейчас домен автоматом дописывает сам кубер, /etc/hosts:

    ip пода test.default-subdomain.default.svc.cluster.local test
    Если использовать инструкцию:
    apiVersion: v1
    kind: Pod
    metadata:
    name: test
    spec:
    hostname: test
    subdomain: default-subdomain
    hostAliases:
    - ip: «1.2.3.4»
    hostnames:
    - «test.domain.com»
    - «test»
    containers:
    ...
    То запись добавляется снизу и при выполнении в контейнере команды hostname -f выдаётся запись test.default-subdomain.default.svc.cluster.local а нужно что бы test.domain.com

  7. Спасибо автору за проделанную работу.

    Только у меня ingress-nginx почему-то с пустым IP адресом?

    # kubectl get ingress -o wide
    NAME HOSTS ADDRESS PORTS AGE
    ingress-nginx nginx.cluster.local 80 52m

    и сервис не обслуживается:

    # curl kub-ingress-1.cluster.local
    curl: (7) Failed connect to kub-ingress-1.cluster.local:80; Connection refused

    • Оказывается у меня почему-то отсутствует ingress-nginx-controller-xxxx как и сам ingress-nginx namespace.
      kube-system coredns-85578f88b8-6rmcm 1/1 Running 0 95s
      kube-system coredns-85578f88b8-h9mjr 1/1 Running 0 105s
      kube-system dns-autoscaler-7d4cfd5f55-cf2hs 1/1 Running 1 27h
      kube-system kube-apiserver-kub-master-1 1/1 Running 224 2d1h
      kube-system kube-apiserver-kub-master-2 1/1 Running 3 47h
      kube-system kube-apiserver-kub-master-3 1/1 Running 3 47h
      kube-system kube-controller-manager-kub-master-1 1/1 Running 0 2d1h
      kube-system kube-controller-manager-kub-master-2 1/1 Running 0 47h
      kube-system kube-controller-manager-kub-master-3 1/1 Running 0 47h
      kube-system kube-flannel-8vr5f 2/2 Running 3 27h
      kube-system kube-flannel-b6ktv 2/2 Running 3 27h
      kube-system kube-flannel-bnm7s 2/2 Running 3 27h
      kube-system kube-flannel-kstqb 2/2 Running 3 27h
      kube-system kube-flannel-r57jz 2/2 Running 2 27h
      kube-system kube-flannel-wfjrn 2/2 Running 10 27h
      kube-system kube-proxy-ch5kc 1/1 Running 0 3m50s
      kube-system kube-proxy-ct9b6 1/1 Running 0 3m50s
      kube-system kube-proxy-dz7ck 1/1 Running 0 3m50s
      kube-system kube-proxy-kkdnd 1/1 Running 0 3m50s
      kube-system kube-proxy-rtnth 1/1 Running 0 3m50s
      kube-system kube-proxy-zs8cd 1/1 Running 0 3m50s
      kube-system kube-scheduler-kub-master-1 1/1 Running 0 2d1h
      kube-system kube-scheduler-kub-master-2 1/1 Running 0 47h
      kube-system kube-scheduler-kub-master-3 1/1 Running 0 47h
      kube-system kubernetes-dashboard-556b9ff8f8-4dtt9 1/1 Running 1 27h
      kube-system nginx-proxy-kub-ingress-1 1/1 Running 3 27h
      kube-system nginx-proxy-kub-node-1 1/1 Running 1 27h
      kube-system nginx-proxy-kub-node-2 1/1 Running 1 27h
      kube-system nodelocaldns-26fws 1/1 Running 1 27h
      kube-system nodelocaldns-44n9h 1/1 Running 1 27h
      kube-system nodelocaldns-5pjst 1/1 Running 3 27h
      kube-system nodelocaldns-7k6nq 1/1 Running 1 27h
      kube-system nodelocaldns-9xj9k 1/1 Running 1 27h
      kube-system nodelocaldns-dw2ll 1/1 Running 1 27h

      Странно то, что деплой прошел без ошибок даже после повторного запуска...
      kub-ingress-1 : ok=322 changed=10 unreachable=0 failed=0 skipped=531 rescued=0 ignored=0
      kub-master-1 : ok=494 changed=29 unreachable=0 failed=0 skipped=976 rescued=0 ignored=0
      kub-master-2 : ok=428 changed=26 unreachable=0 failed=0 skipped=847 rescued=0 ignored=0
      kub-master-3 : ok=430 changed=26 unreachable=0 failed=0 skipped=845 rescued=0 ignored=0
      kub-node-1 : ok=320 changed=10 unreachable=0 failed=0 skipped=535 rescued=0 ignored=0
      kub-node-2 : ok=320 changed=10 unreachable=0 failed=0 skipped=533 rescued=0 ignored=0
      localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      Friday 10 April 2020 13:32:12 +0000 (0:00:00.388) 0:17:06.491 **********

      Кто-нибудь подскажет как это исправить?..

      • Ingress Controller можно отдельно установить через тот же Helm, если не ошибаюсь. Давно уже не занимался этим, подзабыл.

      • Не в обиду автору гайда, но если устанавливать по видео 09. Kubespray. Установка кластера. Вечерняя школа Слёрма по Kubernetes с youtube то ingress устанавливается нормально.

  8. Аноним

    Большое спасибо! Подчеркнул для себя некоторые моменты. Очень хорошо, что начали статьи по девопс.

    • Будут еще, есть наработки. Очень хочу про ci/cd написать, но по времени очень затратно. Не получается дописать и оформить статью.

  9. Отличная статья! Спасибо вам большое:)

  10. Замечательный мануал, помогли разложить все по полочкам, спасибо большое! Удивлен что здесь нет ни одного комментария!

    • Тема очень узкая и не популярная. Писать статьи трудно и долго, а смысла особо нет. Доведу цикл до логического конца, написав еще несколько статей и закончу. Проще про настройки сети в ubuntu, centos писать :)

      • Уверент что через год эту статью зачитают до дыр! ) Все только начинается!

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить комментарий" Я даю согласие на обработку персональных данных.
Используешь Telegram? Подпишись на канал автора →
This is default text for notification bar