Быстрое введение в Kubernetes

1 апреля 2019

Kubernetes (часто сокращают до k8s) — открытая система оркестрации контейнеров, представленная компанией Google в 2014 году. Kubernetes реализует идею, ранее использованную во внутренней системе Google под названием Borg [PDF]. Если вкратце, идея состоят в том, что ваш деплоймент строится на базе контейнеров (например, Docker), а также описании того, сколько этих контейнеров нужно и какие ресурсы они используют. Kubernetes на базе этого описания и доступных физических машин разворачивает контейнеры и делает все возможное для поддержания требуемой конфигурации. В том числе, он перезапускает упавшие контейнеры, перемещает их для выделения ресурсов, необходимых новым контейнерам, и так далее.

Зачем это нужно?

Другими словами, используется декларативный подход — мы описываем, что требуется достичь, а не как. Из преимуществ данного подхода можно отметить следующие. Система сама себя восстанавливает в случае сбоев. У вас не болит голова о том, на какой физической машине запущен тот или иной контейнер, и куда его перенести, чтобы запустить новый тяжелый сервис. Система становится повторяемой. Если у вас все развернулось и работает в тестовом окружении, вы можете с хорошей долей уверенности сказать, что оно развернется в точно такую же систему и на продакшене. Наконец, система становится версионируемой. Если что-то пошло не так, вы можете достать из Git‘а старую конфигурацию и развернуть все в точности, как было раньше.

Стоит однако понимать, что кубер является просто инструментом, а не серебряной пулей. В частности, такие проблемы, как миграции схем баз данных или обеспечение обратной совместимости API, остаются на вас. Не следует сломя голову внедрять в проекте кубер, просто потому что сейчас так модно. Определитесь, какую конкретную проблему вы хотели бы решить, и является ли она настолько приоритетной, что решать ее нужно именно сейчас. Затем поднимите где-нибудь на стенде кубер, или воспользуйтесь услугами одной из компаний, предоставляющих его в качестве сервиса. Посмотрите, решает ли кубер вашу проблему, и не создает ли при этом парочку новых. В общем, совсем не факт, что в каждой IT-компании обязательно нужно использовать Kubernetes.

Цель этой заметки как раз заключается в том, чтобы помочь вам составить собственное мнение о кубере, и понять, нужен ли он вам.

Подготовка к эксперименту

Самый простой способ познакомиться с Kubernetes — это установить Docker Desktop. На момент написания этих строк Docker Desktop был доступен в виде пакетов только для Windows и MacOS. Как альтернативный вариант, вы можете зарегистрироваться в Digital Ocean по моей реферальной ссылке, чтобы воспользоваться Kubernetes as a Service. Ссылка дает некоторую сумму денег на счету, которой за глаза хватит, чтобы наиграться с кубером.

Также понадобится установить утилиту kubectl. Ее установка в разных системах описана здесь. Например, в MacOS достаточно сказать:

brew install kubectl

Если вы решили воспользоваться Docker Desktop, включите Kubnernetes в меню Preferences… → Kubernetes. Также рекомендую сразу увеличить количество ресурсов, выделенных под Docker и Kubernetes, так как по умолчанию ресурсов этих немного. Сделать это можно во вкладке Advanced.

Если же вы выбрали вариант с Digital Ocean, или используете любого другого провайдера Kubernetes в виде сервиса, то вам будет предложено скачать файл конфигурации. После этого нужно сказать:

export KUBECONFIG=/путь/до/вашего/kubeconfig.yaml

Независимо от того, какой кубер вы решили использовать, теперь команда:

kubectl get nodes

… должна выводить список доступных физических машин. Например:

NAME                   STATUS    ROLES     AGE       VERSION
quirky-dubinsky-ctnw   Ready     <none>    8m        v1.13.4

Поздравляю, можно переходить к следующим шагам. Для Docker Desktop и Digital Ocean они будут одинаковыми.

Эксперимент

В Kubernetes все описывается при помощи yaml-файлов. Если вы что-то меняете напрямую, а не через yaml, то почти наверняка используете Kubernetes неправильно. Это может обернуться для вас «неубиваемыми» контейнерами и разными другими странными эффектами, поэтому лучше так не делать.

В качестве примера рассмотрим описание простейшего deployment, состоящего из одного контейнера:

apiVersion: apps/v1
kind
: Deployment
metadata
:
  name
: nginx-deployment
spec
:
  selector
:
    matchLabels
:
      app
: nginx
  replicas
: 1
  template
:
    metadata
:
      labels
:
        app
: nginx
    spec
:
      containers
:
      - name
: nginx
        image
: nginx:1.15.9-alpine
        ports
:
        - containerPort
: 80
          protocol
: TCP

Здесь для примера я использовал образ Nginx, но вы с тем же успехом можете запускать любые другие образы. В том числе, собранные самостоятельно из Dockerfile.

Скажем куберу применить новую конфигурацию:

kubectl apply -f nginx.yaml

Вскоре мы увидим соответствующие изменения в списке деплойментов:

kubectl get deployments

Пример вывода:

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           2m

Также в списке подов появится наш первый под:

kubectl get pods

Пример вывода:

NAME                               READY     STATUS    RESTARTS   AGE
nginx-deployment-bff77b7f8-jbl4m   1/1       Running   0          2m

Pod в кубере является минимальной единицей развертывания. Часто в одном поде содержится один контейнер, но в общем случае в поде может содержаться и несколько контейнеров. В последнем случае эти контейнеры будут всегда разворачиваться вместе, на одной физической машине. То есть, невозможна ситуация, при которой одна половина пода развернулась на одной физической машине (ноде), а вторая — на второй физической машине.

У get pods доступны следующие полезные кючи:

# фильтрация подов по меткам
kubectl get pods -l app=nginx

# показывает внутренние IP подов
kubectl get pods -o wide

# вывод в JSON --- удобно в скриптах
kubectl get pods -o json

# чтобы не зависеть от jq:
kubectl get pods -o jsonpath='{.items[:].metadata.name}'

Бывает так, что в кубере что-то почему-то не стартует. Разобраться в причине обычно помогает describe:

kubectl describe deployments
kubectl describe pods

При желании можно зайти внутрь контейнера, сказав:

kubectl exec -it nginx-deployment-bff77b7f8-jbl4m -- /bin/sh

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

Под запущен, но сейчас с ним есть маленькая проблемка. К нему никак нельзя достучаться откуда-то снаружи. Чтобы исправить эту ситуацию, создадим следующий файл:

apiVersion: v1
kind
: Service
metadata
:
  name
: nginx-load-balancer
spec
:
  type
: LoadBalancer
  selector
:
    app
: nginx
  ports
:
    - protocol
: TCP
      port
: 80
      targetPort
: 80
      name
: http

Здесь объявлен сервис с типом LoadBalancer, который находит все поды с меткой app=nginx и прокидывает порт 80 данных подов наружу.

Применим конфигурацию:

kubectl apply -f loadbalancer.yaml

Сервис должен появиться в списке сервисов:

kubectl get svc

Пример вывода:

NAME                TYPE         CLUSTER-IP    EXTERNAL-IP PORT(S)
kubernetes          ClusterIP    10.245.0.1    <none>      443/TCP
nginx-load-balancer LoadBalancer 10.245.200.93 <pending>   80:30953/TCP

Спустя какое-то время <pending> сменится на реальный внешний IP-адрес. Открыв этот адрес в браузере, мы увидим «Welcome to nginx!». Если вы решили использовать Docker Desktop, то вместо внешнего адреса будет использован localhost.

Теперь допустим, что нагрузка на нашу систему подросла, и один под больше не справляется. Откроем nginx.yaml и увеличим число реплик:

...
spec
:
  selector
:
    matchLabels
:
      app
: nginx
  replicas
: 3 # <-- изменено
...

Говорим:

kubectl apply -f nginx.yaml

Если все было сделано правильно, то в get pods теперь будет три пода. При этом LoadBalancer продолжит работать, как ни в чем ни бывало, равномерно размазывая нагрузку по трем подам. Любые другие изменения с подами, такие, как обновление версии образа, происходят аналогично.

Информация о сервисах доступна изнутри контейнеров через переменные окружения. Единственный нюанс заключается в том, что сервис должен существовать на момент запуска контейнера. Таким образом, Kubernetes из коробки имеет service discovery, ставить Consul не нужно. Сервис можно сделать недоступным снаружи, убрав из описания строчку про LoadBalancer. Подробности по этой теме можно найти в документации о типах сервисов.

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

/ # apk upgrade
/ # apk add curl
...
/ # curl nginx-load-balancer 2>/dev/null | grep title
<title>Welcome to nginx!</title>
/ # curl nginx-load-balancer.default.svc.cluster.local 2>/dev/null | \
>     grep title
<title>Welcome to nginx!</title>

Наконец, удалим load balancer и поды:

kubectl delete -f loadbalancer.yaml
kubectl delete -f nginx.yaml

Если вы использовали Digital Ocean, то может иметь смысл удалить созданный кластер Kubernetes. В противном случае за него продолжат списываться деньги, несмотря на отсутствие запущенных подов.

Вот и все. Согласитесь, это было не так уж и сложно.

Заключение

Совершенно невозможно рассказать все о Kubernetes в рамках одной статьи.

За кадром остались такие вопросы, как поднятие своего кластера на голом железе, network policies, работа с volumes, ограничения по ресурсам, liveness probes, config maps, аффинити к нодам, работа с K8s API из разных языков программирования, и многое-многе другое. Заинтересованным читателям рекомендуется обращаться к соответствующим разделам документации, поскольку (1) документация у кубера классная, (2) именно она содержит наиболее актуальные сведения, (3) разделы, представляющие интерес, сильно зависят от решаемой вами задачи.

А используете ли вы Kubernetes, и если да, то каковы ваши впечатления от работы с ним?

Дополнение: Поднимаем кластер Kubernetes из одной ноды под Linux

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.