Знакомство с Kubernetes.

Часть 0: Что это?


Kubernetes — это проект с открытым исходным кодом для управления «флотом» контейнеров на нескольких хостах, объединенных в кластер. Данный инструмент призван обеспечить основные механизмы развертывания, обслуживания и масштабирования приложений, упакованных в контейнеры.

 
 

Разработка Kubernetes была начата в Google, которая уже более десяти лет использует похожий инструмент (закрытый и недоступный для сообщества) для оркестрации контейнеров — Borg. В данный момент Kubernetes передан под управление Cloud Native Computing Foundation (CNCF) и теперь поддерживается многими компаниями, в том числе Microsoft, RedHat, IBM и Docker.

Если вы используете контейнеры (docker/rkt — здесь под контейнером мы понимаем упакованное приложение со всеми необходимыми для работы зависимостями), то рано или поздно возникает вопрос с развертыванием, обновлением, масштабированием контейнеров, балансировкой траффика между ними, обнаружением сервисов и т. д. Именно эти (и некоторые другие) проблемы и призван решить Kubernetes.

Взаимодействуя с API можно описать желаемое состояние вашего приложения (например, количество реплик или размещение на определенном хосте) и Kubernetes сделает все необходимое для обеспечения соответствия текущей инфраструктуры желаемой — будет запускать поды (группы из одного или нескольких контейнеров), реплицировать их, «поднимать», если некоторые поды «упадут» и т.д..

Kubernetes довольно гибкий, кластер можно поднять практически в любом месте. Например, Kubernetes присутствует в Docker for Mac, его легко установить и запустить на компьютере под управлением Windows и тем более Linux. Естественно, Kubernetes работает на Google Compute Engine (GKE) и с некоторыми особенностями на виртуалках других облачных провайдеров (AWS, Azure).

Кластер Kubernetes состоит из одной или нескольких нод (напомню, нода может быть физической или виртуальной машиной). Если в кластере есть несколько нод, то они объединены в кластерную сеть. Вне зависимости ок количества нод, все кластеры Kubernetes имеют одни и те же компоненты (components).

Компонентами кластера могут быть:

  • kube-apiserver — ключевой компонент системы. Он предоставляет JSON REST API, использующий HTTP в качестве транспорта и используется для организации внешнего и внутреннего доступа к функциям Kubernetes;
  • etcd — легкий распределённый движок хранилища ключ-значение (используется для хранения конфигурационных данных приложений в кластере);
  • kube-scheduler — компонент системы (один из контроллеров), который выбирает, на какой ноде должен запускаться какой под, исходя из доступности ресурсов;
  • kube-controller-manager — часть мастер-ноды, запускающая основные контроллеры Kubernetes (Node Controller, Replication Controller, Endpoints Controller, Service Account & Token Controllers);
  • cloud-controller-manager — часть мастер-ноды, запускающая контроллеры, необходимые для взаимодействия с облачными провайдерами (Node Controller, Route Controller, Service Controller, Volume Controller);
  • kubelet — отвечает за статус подов на ноде (запуск, останов и управление контейнерами);
  • kube-proxy — компонент, ответственный за маршрутизацию входящего трафика на конкретные контейнеры, работающие в пределах пода.

Наилучший способ познакомиться с работой в кластере под управлением Kubernetes — установить его. Самый простой способ — «поиграть» с кубернетесом на сайте Play with Kubernetes.
Второй по сложности вариант — установить Minikube — утилиту командной строки для настройки и запуска однонодового кластера Kubernetes в виртуальной машине на локальном компьютере.
Также, если есть в наличии один (или несколько) компьютеров (или виртуальных машин) и желание «покопаться» поглубже, можно настроить свой кластер самостоятельно.

Часть 2: Терминология

Архитектура Kubernetes гораздо проще, чем кажется на первый взгляд — вы не взаимодействуете напрямую с нодами, на которых запущено приложение. Вся работа с кластером Kubernetes осуществляется через панель управления (dashboard), API или с помощью инструмента командной строки (kubectl), который в свою очередь взаимодействует с API Kubernetes.

Далее мы будем использовать именно kubectl — для распространения информации о желаемом состоянии приложения в кластере (проще говоря о развертывании / удалении) или для сбора данных о текущем состоянии инфраструктуры.

В кластере Kubernetes может быть два типа узлов — мастеры (masters) и ноды (nodes). На мастерах — управляющих узлах кластера — запускается API-сервер (kube-apiserver), планировщик (kube-scheduler), менеджер контроллеров (kube-controller-manager) и хранилище etcd.

На нодах — рабочих узлах кластера — запускаются еще два дополнительных компонента, а именно kube-proxy и kubelet. Первый используется для маршрутизации траффика, а второй — за получение инструкций от управляющего узла и приведение подов на данном рабочем узле в желаемое состояние. Кроме тога, на рабочих нодах могут запускаться (и запускаются) дополнительные плагины (например flannel), для организации сетевого взаимодействия или мониторинга и сбора метрик. Контейнеры (docker/rkt и т.д.) с приложением запускаются на рабочих узлах (нодах).

Схематично кластер Kubernetes выглядит примерно так:

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

  • Pods — минимальная сущность (юнит) для развертывания в кластере;
  • ReplicaSets (ранее Replication Controller) — гарантирует, что в определенный момент времени будет запущено нужно кол-во контейнеров;
  • Deployments — обеспечивает декларативные (declarative) обновления для Pods и ReplicaSets;
  • StatefulSets — используется для управления приложениями с сохранением состояния;
  • DaemonSet — гарантирует, что определенный под будет запущен на всех (или некоторых) нодах;
  • Jobs (в том числе CronJob) — создает один (или несколько) подов и гарантирует, что после выполнения команды они будут успешно завершены (terminated);
  • Labels and Selectors — пары ключ/значение, которые присваиваются объектам (например, подам). С помощью селекторов пользователь может идентифицировать объект;
  • Namespaces — виртуальные кластеры размещенные поверх физического;
  • Services — абстракция, которая определяет логический набор подов и политику доступа к ним;
  • Annotations — добавление произвольных неидентифицирующих метаданных к объектам;
  • ConfigMaps — позволяет переопределить конфигурацию запускаемых подов;
  • Secrets — используются для хранения конфиденциальной информации (пароли, токены, ssh-ключи).

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

Часть 3: Поды (Pods)

Детальнее разберемся с подами (Pods) — наименьшей функциональной единицей для развертывания в объектной модели Kubernetes.

Pod (переводится с английского как стручок) состоит обычно из одного или нескольких контейнеров, хранилища (storage), отдельного IP-адреса и опций, которые определяют, как именно контейнер(ы) должны запускаться. Также Pod представляет собой некий запущенный процесс в кластере Kubernetes.

Чаще всего в Pod'ах используются docker-контейнеры (что и не удивительно), но можно использовать и другие контейнеры (например, rkt от CoreOS).

Важно Kubernetes управляет Pod'ами, а не контейнерами напрямую.

В кластере Kubernetes Pod'ы используются двумя способами:

  • Pod из одного контейнера — наиболее распространенный случай использования. В этом варианте Pod можно представить как обертку вокруг одного контейнера;
  • Pod из нескольких контейнеров, работающих сообща — контейнеры в данном случае образуют отдельную единицу (сервис) и используют общие ресурсы — например, файлы из одного хранилища (storage). При этом, отдельный контейнер Pod'а может обновлять эти файлы.

Каждый Pod предназначен для запуска одного экземпляра конкретного приложения. Если есть необходимость горизонтального масштабирования, то можно запустить несколько экземпляров Pod'а — в терминологии Kubernetesэто называется репликацией. Реплицированные Pod'ы создаются и управляются как единая группа абстракцией, которая называется контроллер (Controller). Подробнее контроллеры рассматриваются в отдельной статье.

Pod'ы спроектированы для поддержки множества взаимодействующих процессов (например, контейнеров), которые образуют отдельный сервис (единицу обслуживания). Контейнеры внутри Pod'а автоматически размещаются и управляются на одной и той же физической (или виртуальной) ноде кластера. Контейнеры в Pod'е могут совместно использовать ресурсы и зависимости, взаимодействовать друг с другом и определять, когда и как они будут завершаться.

К слову, группировка и запуск нескольких контейнеров в Pod'е считается «продвинутым» вариантом использования, и применять этот шаблон нужно только при реальной необходимости. Например, у вас может быть контейнер с web-сервером, использующий файлы из общего (относительно Pod'а) хранилища, и отдельный контейнер, который обновляет эти файлы из удаленного источника. Визуально это может выглядеть так:

Pod'ы предоставляют запущенным внутри контейнерам два типа общих ресурсов: сеть и хранилище.

Сеть

Каждому Pod'у присваивается уникальный IP-адрес. Внутри Pod'а каждый контейнер использует общее пространство имен (namespace) сети, включая IP-адрес и сетевые порты. Между собой внутри Pod'а контейнеры взаимодействуют через localhost. При взаимодействии с объектами, находящимися за пределами Pod'а, контейнеры «договариваются» между собой и координируют использование общих сетевых ресурсов (таких как порты).

Хранилище

Pod может определить набор общих томов (volumes) для хранения данных. Контейнеры внутри Pod'а могут работать с этими томами и, таким образом, обмениваться данными между собой. Благодаря использованию томов, можно сохранить данные, если один из контейнеров Pod'а (которому нужны эти данные для корректной работы) должен быть перезапущен. Время жизни томов совпадает с временем жизни самого Pod'а.

Что касается работы в кластере Kubernetes, то редко когда приходится создавать и запускать отдельные Pod'ы вручную (варианты запуска при ознакомлении с Kubernetes мы в расчет не берем). Связано это с тем, что Pod'ы изначально проектировались как эфемерные объекты, минимальные единицы, «строительные блоки» для сущностей более высокого уровня (например, контроллеров). Когда Pod создается (не важно, вручную вами или контроллером), он по плану (за это отвечает scheduler) запускается на одной из нод кластера Kubernetes. На этой ноде Pod остается до тех пор, пока:

  • не завершится процесс внутри Pod'а (например, если это одноразовая задача);
  • Pod не будет удален вручную;
  • Pod не будет «выселен» с ноды из-за нехватки ресурсов;
  • нода не выйдет из строя.

Примечание Перезапуск контейнера внутри Pod'а не следует путать с перезапуском самого Pod'а.

Сами по себе Pod'ы не являются «самолечащимися» (self-healing) объектами. Если запуск Pod'а запланирован на ноде, которая вышла из строя, или сама операция запуска потерпела крах, то Pod удаляется. Точно также, при нехватке ресурсов на ноде (когда Pod'ы «выселяются») или при переводе ноды в режим обслуживания, Pod'ы не будут запущены на других узлах и удалятся. Для обеспечения запуска на других нодах (и управления Pod'ами в целом) в Kubernetes используются абстракции более высокого уровня, называемые контроллерами — в основном именно с ними и приходится иметь дело.

Контроллер может управлять несколькими экземплярами Pod'ов — в том числе обеспечивать репликацию и самовосстановление в пределах кластера (при выходе из строя ноды, идентичный экземпляр Pod'а будет запущен на другой ноде). Примером контроллера, который содержит в себе один или несколько экземпляров Pod'а могут быть:

  • Deployment
  • StatefulSet
  • DaemonSet

Для обеспечения такого функционала контроллеры используют шаблоны Pod'ов (Pod Templates). Шаблон Pod'а — спецификация, которая используется контроллерами и другими объектами (например, заданиями — Jobs/CronJob) для запуска настоящего, реального Pod'а. Ниже приведен пример простой спецификации (ее еще иногда называют манифестом), описывающей контейнер, который напечатает сообщение при запуске и «уснет» на час:

Предложенный манифест (спецификацию) можно сохранить в файле single-example-pod.yml и запустить Pod с помощью утилиты командной строки kubectl:

Больше информации о Pod'ах можно найти в официальной документации по Kubernetes.

Часть 4: Реплики (ReplicaSet)

ReplicaSet гарантирует, что определенное количество экземпляров подов (Pods) будет запущено в кластере Kubernetes в любой момент времени. Давайте разберемся!

 

Сразу стоит сказать, что деплоймент (Deployment), который мы рассмотрим в следующей статье, представляет собой абстракцию более высокого уровня, которая управляет ReplicaSets и предоставляет расширенный функционал управления контейнерами. Разработчики Kubernetes советуют использовать именно Deployments, если не нужна специфическая настройка оркестрации (или если вообще не требуется обновление подов). При использовании Deployments, вам не нужно беспокоиться об управлении создаваемыми ReplicaSets — деплоймент все сделает за вас.

Фактически это значит, что вам (возможно) и не придется манипулировать объектами ReplicaSet: достаточно будет деплоймента с описанием свойств приложения в разделе spec.

ReplicaSet — это следующее поколение Replication Controller. Единственная разница между ReplicaSet и Replication Controller — это поддержка селектора. ReplicaSet поддерживает множественный выбор в селекторе, тогда как Replication Controller поддерживает в селекторе только выбор на основе равенства.

Как и для всех других API-объектов Kubernetes, для определения ReplicaSet в yaml-файле нужны поля apiVersionkind и metadata. Кроме того, в ReplicaSetтакже должна присутствовать секция .spec.

Далее, в секции .spec обязательно должна присутствовать вложенная секция .spec.template — шаблон пода (Pod). Он имеет точно такой же формат, как описание пода (Pod) без секций apiVersion и kind. Кроме обязательных полей, в секции .spec.template при описании ReplicaSet нужно указывать соответствующие метки (.spec.template.metadata.labels) и политику перезапуска (единственным допустимым значением для .spec.template.spec.restartPolicyявляется Always, которое соответствует значению по умолчанию).

Секция .spec.selector описывает селектор меток. ReplicaSet управляет всеми подами (Pods) с метками, которые соответствуют описанному селектору — неважно, создавались ли эти поды самим ReplicaSet, другим процессом (например, деплойментом) или администратором вручную. Такой подход позволяет изменять ReplicaSet, не затрагивая запущенные поды в кластере.

При описании ReplicaSet стоит указать, сколько экземпляров подов должно работать одновременно (а иначе какой смысл в использовании ReplicaSet), установив параметр .spec.replicas. Если этого не сделать, то будет запущен только один экземпляр пода — .spec.replicas по умолчанию равно единице.

ReplicaSet может сам иметь метки — они должны быть установлены в разделе .metadata.labels. Как правило, их нужно устанавливать по аналогии с установкой меток в секции .spec.template.metadata.labels, но сами метки могут быть разными, потому что .metadata.labels не влияют на поведение ReplicaSet.

Пример описания ReplicaSet:

Предложенный манифест можно сохранить в файл frontend.yaml и запустить в кластере Kubernetes с помощью утилиты командной строки:

Просмотреть детальное состояние запущенного выше ReplicaSet можно с помощью команды kubectl describe rs/frontend, результат выполнения команды будет примерно следующим:

Для удаления ReplicaSet и всех его подов, нужно использовать команду kubectl delete .... При этом утилита kubectl выполнит масштабирование количества реплик до нуля, дождется удаления каждого пода и удалит сам ReplicaSet. При использовании REST API все три шага нужно выполнять отдельно и в правильной очередности.

Есть возможность удалить ReplicaSet, не затрагивая ни один из его подов, для этого следует использовать kubectl delete с дополнительным параметром --cascade=false.

Поды (Pods) могут быть удалены из набора ReplicaSet путем изменения их меток — этот вариант может пригодиться при отладке, восстановлении данных и т. д. Поды, которые удаляются таким образом, будут автоматически заменены (при условии, что количество реплик в наборе ReplicaSet также не изменяется).

ReplicaSet можно легко масштабировать (вверх или вниз), просто меняя значение поля .spec.replicas.

Стоит отметить, что наборы ReplicaSet могут использоваться для горизонтального масштабирования подов (автоскейлинга, HPA). Пример настройки такого масштабирования (в зависимости от использования CPU) для созданного ранее ReplicaSet будет выглядеть так:

Сохраняем предложенный манифест в файл hpa-rs.yaml и запускаем его в кластере Kubernetes:

Альтернативы использования ReplicaSet:

  • Deployment (рекомендуемый);
  • Job (для подов, которые должны «завершаться» самостоятельно после выполнения определенной задачи);
  • DaemonSet (для запуска подов на каждой ноде кластера).

Часть 5: Развертывания (Deployments)

Контроллер развертывания (Deployment controller) предоставляет возможность декларативного обновления для объектов типа поды (Pods) и наборы реплик (ReplicaSets). Давайте разберемся!

 

Достаточно описать желаемое состояние [подов/реплик] в объекте Deployment, после чего контроллер развертывания изменит текущее состояние объектов на желаемое в контролируемом режиме. Стоит отметить, что при манипуляциях с развертываниями (Deployments) нам не нужно беспокоиться об управлении наборами реплик (ReplicaSets) — все необходимое будет выполнено непосредственно контроллером развертывания.

Как и для всех других API-объектов Kubernetes, для определения Deployment в yaml-файле нужны поля apiVersionkind и metadata. Кроме того, в Deploymentтакже должна присутствовать секция .spec.

Как мы уже говорили в прошлой статье, в секции .spec обязательно должна присутствовать вложенная секция .spec.template — шаблон пода (Pod). Он имеет точно такой же формат, как описание пода (Pod) без секций apiVersionи kind. Кроме обязательных полей, в секции .spec.template при описании Deployment нужно указывать соответствующие метки (.spec.template.metadata.labels) и политику перезапуска (единственным допустимым значением для .spec.template.spec.restartPolicy является Always, которое соответствует значению по умолчанию).

В поле .spec.replicas указываем, сколько экземпляров подов должно быть запущено одновременно — если этого не сделать, то будет запущен только один экземпляр пода — .spec.replicas по умолчанию равно единице.

Можно указать .spec.selector — это необязательное поле, которое определяет селектор меток для подов, предназначенных для этого развертывания. В таком случае, .spec.selector должен соответствовать значению .spec.template.metadata.labels или будет отклонен API Kubernetes.

В поле .spec.strategy необходимо определить стратегию обновления старых подов (Pods) новыми. Допустимые значения для данного поля — Recreateили RollingUpdate (значение по умолчанию).

Если выбрать стратегию Recreate (.spec.strategy.type==Recreate), то перед стартом новых подов будут удалены все старые.

При стратегии обновления RollingUpdate поды будут обновляться плавно, по очереди (дополнительно контролировать этот процесс можно с помощью параметров maxUnavailable и maxSurge).

Параметр .spec.strategy.rollingUpdate.maxUnavailable — необязательное поле, указывающее максимальное количество подов, которые могут быть недоступны в процессе обновления. Значение может быть абсолютным числом (например, 5) или процентом от желаемого количества подов (Pods) (например, 10%). Абсолютное число рассчитывается из процента путем округления. Значение этого параметра не может быть установлено в 0 и по умолчанию равно 25%.

Параметр .spec.strategy.rollingUpdate.maxSurge — необязательное поле, указывающее максимальное количество подов (Pods), которое может быть создано сверх желаемого количества подов (Pods), описанного в развертывании. Значение может быть абсолютным числом (например, 5) или процентом от желаемого количества подов (например, 10%). Значение этого параметра не может быть установлено в 0 и по умолчанию равно 25%.

Ниже представлен пример развертывания (Deployment):

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

  • создается развертывание (Deployment) с именем nginx-deployment (имя указано в поле metadata: name);
  • развертывание создает три экземпляра пода (количество указано в поле replicas);
  • в поле селектора указано, как развертывание (Deployment) обнаружит, какими подами (Pods) нужно управлять. В этом примере просто выбираем одну метку, определенную в шаблоне Pod‘а (app: nginx);
  • описание шаблона Pod‘а в поле template: spec «требует» запустить docker-контейнер nginx, из образа nginx версии 1.7.9 (образ будет взят с Docker Hub). Данному поду будет присвоена метка app: nginx;
  • развертывание открывает 80-й порт контейнера, так что контейнер может отправлять и принимать трафик.

Сохраним данный манифест в файл nginx-deployment.yaml и запустим его в кластере Kubernetes с помощью команды:

Примечание. Параметр --record нам весьма пригодится для хранения истории изменений развертывания.

Если сразу же запустить команду kubectl get deployments, то скорее всего результат будет следующим:

Когда вы с помощью данной команды хотите получить состояние развертываний (Deployments) в кластере, вам доступны следующие поля:

  • NAME — список имен развертываний в кластере;
  • DESIRED — отображает желаемое количество экземпляров пода (определяется при создании развертывания);
  • CURRENT — отображает количество экземпляров пода в настоящее время;
  • UP-TO-DATE — отображает количество экземпляров пода, которые были обновлены для достижения желаемого состояния;
  • AVAILABLE — отображает количество экземпляров пода, которые доступны пользователям;
  • AGE — отображает время с момента запуска развертывания.

Чтобы увидеть текущий статус (прогресс) развертывания, можно использовать команду kubectl rollout status deployment/nginx-deployment. Вывод будет примерно таким:

Через несколько секунд (нужно ведь подождать, пока скачается docker-образ) еще раз проверяем состояние развертываний в кластере с помощью kubectl get deployments:

Как видим, данное развертывание создало три экземпляра пода (как мы и писали в желаемом состоянии) — «под капотом» был также создан набор реплик (ReplicaSet) — убедиться в этом можно с помощью команды kubectl get rs:

Имя набора реплик формируется автоматически и выглядит как [DEPLOYMENT-NAME]-[POD-TEMPLATE-HASH-VALUE], hash-значение генерируется при создании развертывания. Узнать какие метки были автоматически добавлены каждому поду можно командой kubectl get pods --show-labels, вывод может выглядеть так:

Для обновления развертывания (например, изменения версии docker-образа на 1.9.1) можно воспользоваться такой командой:

или изменить сам манифест развертывания (меняем значение .spec.template.spec.containers[0].image):

или (предпочтительный вариант) — изменить файл с манифестом развертывания и применить изменения:

Как и раньше, наблюдаем за процессом обновления и получаем интересующую нас информацию командами kubectl rollout status deployment/nginx-deploymentkubectl get deployments и kubectl get rs (ниже приведен только результат последней команды):

Как видим, новый (ориентируемся по времени) набор реплик (ReplicaSet) находится в желаемом состоянии, в то время как в старом наборе количество экземпляров пода равно нулю.

Детальное описание развертывания получаем командой kubectl describe deployments:

Рассмотрим пример отката (возвращения к предыдущему состоянию) развертывания (Deployment). Допустим, при обновлении мы ошиблись и указали неверную версию docker-образа — nginx:1.91 вместо nginx:1.9.1:

Обновление застопорится (ожидаемо, ведь такого docker-образа нет):

Состояние набора реплик будет выглядеть примерно так:

Состояние подов будет таким:

Примечание. Дополнительную информацию о состоянии развертывания можно также получить используя kubectl describe deployment, но уже и так ясно — необходим откат.

Для возврата на предыдущую (работоспособную) версию развертывания необходимо сначала проверить историю изменений (узнать номер ревизии):

Благодаря параметру --record мы можем увидеть изменения, которые применяли в каждой из ревизий. Узнать больше подробностей о конкретной ревизии развертывания можно указав ее номер, например так:

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

Для отката на конкретную ревизию необходимо указать ее номер в параметре --to-revision:

Для масштабирования (увеличения/уменьшения количества подов) можно использовать команду:

хотя все же лучше вносить изменения в файл с манифестом развертывания (Deployment) и применять их командой kubectl apply.

К развертываниям также применимо горизонтальное масштабирование подов (HPA) — если такое включено в вашем кластере Kubernetes — и применить его можно так:

На этом все, еще больше информации можно найти в официальной документации по Kubernetes.

Часть 6: Сборщик мусора (Garbage Collector)

Задача сборщика мусора (Garbage Collector) в Kubernetes заключается в удалении определенных объектов, которые больше не имеют владельца. Давайте разберемся, что это значит!

 

Некоторые сущности (объекты) в Kubernetes могут быть владельцами других объектов. Например, набор реплик (ReplicaSet) является владельцем нескольких подов (Pods). В данном примере объекты-поды будут зависимыми от объекта-владельца (набора реплик). Каждый зависимый объект имеет поле metadata.ownerReferences, которое указывает на объект-владелец.

Чаще всего, Kubernetes автоматически устанавливает значение поля ownerReference. Начиная с версии 1.8, значение ownerReference автоматически устанавливается для объектов, созданных с помощью ReplicationControllerReplicaSetStatefulSetDaemonSetDeploymentJob и CronJob.

Есть возможность самостоятельно указать отношения между объектами-владельцами и зависимыми объектами вручную, установив поле ownerReference при описании манифеста в yaml-файле.

Рассмотрим конкретный пример. Конфигурационный файл (манифест) для набора реплик из трех элементов выглядит так:

Сохраним данное описание в файле my-repset.yaml и запустим его в кластере Kubernetes командой:

Получим информацию о запущенных подах в кластере (вывод сильно сокращен):

При удалении объекта-владельца есть возможность автоматически удалять зависимые объекты — такое удаление называется каскадным удалением. Существует также два режима каскадного удаления: фоновое (background) и приоритетное (foreground). Если при удалении объекта-владельца зависимые объекты не удаляются автоматически, то они становятся осиротевшими (orphaned).

При приоритетном (foreground) каскадном удалении объект-владелец сначала переходит в состояние «удаление в процессе» (deletion in progress). В этом состоянии выполняется следующее:

  • объект по-прежнему можно увидеть через REST API;
  • устанавливается значение deletionTimestamp объекта;
  • поле metadata.finalizers содержит значение «foregroundDeletion».

После перехода объекта-владельца в состояние «удаление в процессе» сборщик мусора (Garbage Collector) удаляет зависимые объекты. Как только будут удалены все блокирующие зависимости (объекты с ownerReference.blockOwnerDeletion=true), сборщик мусора удалит и объект-владелец.

При фоновом (background) каскадном удалении Kubernetes немедленно удаляет объект-владелец, а сборщик мусора (Garbage Collector) удаляет зависимые объекты в фоновом режиме.

Управлять политикой каскадного удаления можно изменяя поле propagationPolicy в аргументе deleteOptions при удалении объекта. Допустимые значения данного поля — «Orphan», «Foreground» или «Background». Пример удаления зависимых объектов в фоновом режиме:

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

Оставить «осиротевшие» зависимости можно так:

Утилита kubectl также поддерживает каскадное удаление. Чтобы автоматически удалить зависимые объекты с помощью kubectl нужно использовать параметр --cascade со значением true (установлено по умолчанию). Не удалять (оставить) зависимые объекты в кластере можно так:

Часть 7: Образы (Images)

Перед тем, как описывать в манифесте какого-либо объекта Kubernetes образ контейнера, его необходимо создать и разместить в реестре образов. Давайте разберемся!

 

Примечание. В данной статье будем говорить прежде всего о docker-образах и docker-registry.

Поле image в описании контейнера поддерживает тот же синтаксис, что и команда docker, включая частные реестры (private registries) и тэги.

По умолчанию для скачивания docker-образов установлена политика IfNotPresent, которая заставляет Kubelet пропускать скачивание образа, если он уже существует локально. Если необходимо всегда скачивать docker-образы перед запуском контейнеров, изменить данную политику можно следующими вариантами:

  • установить для поля imagePullPolicy контейнера значение Always;
  • использовать тэг :latest при описании образа;
  • включить контроллер AlwaysPullImages.

Если не задать в явном виде тэг docker-образа, то он будет автоматически установлен в :latest, а политика скачивания образа будет иметь вид imagePullPolicy=Always.

Примечание. Следует избегать использования тэга :latest — подробнее здесь.

При использовании частных реестров образов (private registries) могут потребоваться учетные данные для скачивания образов. Такие данные (ключи) можно предоставлять несколькими способами, в зависимости от типа реестра:

  • средствами GCR (Google Container Registry);
  • средствами AWS EC2 Container Registry (ECR);
  • средствами ACR (Azure Container Registry);
  • настраивая узлы кластера для аутентификации в частных реестрах;
  • скачивая образы до запуска подов;
  • указывая ImagePullSecrets в описании пода.

Рассмотрим каждый вариант более детально.

GCR
Kubernetes имеет встроенную поддержку Google Container Registry (GCR) при работе c GCE/GKE. Если используется кластер в Google Compute Engine или Google Kubernetes Engine, достаточно указать полное имя образа, например, gcr.io/my_project/image:tag. Все поды кластера будут иметь доступ к данному реестру образов. Kubelet получит доступ к GCR с помощью служебной учетной записи (instance’s Google service account) с правами read_only.

ECR
Kubernetes имеет встроенную поддержку реестра контейнеров AWS EC2, если узлы кластера являются экземплярами AWS EC2. В таком случае достаточно использовать полное имя образа — например, ACCOUNT.dkr.ecr.REGION.amazonaws.com/imagename:tag в описании пода. Все пользователи кластера с правами запуска подов получают также права на скачивание docker-образов из ECR.

Kubelet (начиная с версии v1.2.0) будет проверять и периодически обновлять учетные данные ECR. Для этого ему необходимы следующие разрешения:

  • ecr:GetAuthorizationToken
  • ecr:BatchCheckLayerAvailability
  • ecr:GetDownloadUrlForLayer
  • ecr:GetRepositoryPolicy
  • ecr:DescribeRepositories
  • ecr:ListImages
  • ecr:BatchGetImage

ACR
При работе с Azure Container Registry можно получить доступ к реестру образов либо с учетными данными администратора, либо с помощью стандартной аутентификации Docker. Второй вариант предполагает использование инструмента командной строки azure-cli. Создать реестр и сгенерировать учетные данные можно следуя документации, после чего можно использовать учетные данные для входа в систему:

DOCKER_USER: главный администратор или администратор службы
DOCKER_PASSWORD: пароль главного администратора или администратора службы
DOCKER_REGISTRY_SERVER: $ {some-registry-name} .azurecr.io
DOCKER_EMAIL: $ {some-email-address}

Настройка узлов кластера для аутентификации в частных реестрах
Docker хранит ключи для частных реестров образов в файле $HOME/.dockercfgили $HOME/.docker/config.json. Необходимо разместить этот файл в домашнем каталоге пользователя root на всех узлах кластера с запущенным kubelet. Для этого:

  • На рабочем компьютере нужно выполнить docker login [server] для всех наборов учетных записей / реестров, которые вы планируете использовать. Эти команды актуализируют конфигурационный файл $HOME/.docker/config.json.
  • Проверьте данный файл ($HOME/.docker/config.json) в текстовом редакторе и убедитесь, что в нем есть все необходимые данные.
  • Получите все имена или ip-адреса узлов кластера Kubernetes:

или

  • Скопируйте конфигурационный файл $HOME/.docker/config.json на все узлы кластера, например так:
  • Проверьте доступ в частному реестру — например, создайте манифест для пода, использующий образ из реестра.

Скачивая образы до запуска подов
Не самый оптимальный вариант, но все же — если вы хотите использовать локально размещенные docker-образы (например, если хотите сэкономить время на скачивании образов из реестра или нет учетных данных для доступа к реестру), то убедитесь, что эти образы есть на всех узлах кластера.

Указывая ImagePullSecrets в описании пода
С помощью следующей команды (с заменой соответствующих значений на ваши) генерируем секретную переменную (создание секретов рассмотрим в одной из следующих статей):

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

Альтернативный вариант описания секретной переменной — создаем манифест для объекта secret следующего вида:

Убедитесь, что:

  • имя элемента данных в поле data установлено в .dockerconfigjson;
  • значением для параметра .dockerconfigjson установлена строка, полученная как результат кодирования файла $HOME/.docker/config.json(см. выше) в base64;
  • тип (type) объекта установлен в kubernetes.io/dockerconfigjson.

Примечание. Команда для кодирования строки выглядит так: cat ~/.docker/config.json | base64.

Теперь, при описании манифеста для каждого пода, использующего частный реестр образов, не забудьте добавить поле imagePullSecrets:

Часть 8: Переменные окружения

В данной статье рассмотрим какие ресурсы и переменные окружения доступны контейнерам и как переопределять переменные окружения для контейнера при запуске пода (Pod) в кластере Kubernetes. Давайте разберемся!

 

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

  • файловая система, представляющая собой комбинацию docker-образа (слои) и одного или нескольких томов;
  • информация о самом контейнере;
  • информация о других объектах в кластере.

Информация о самом контейнере обычно включает в себя:

  • имя хоста (hostname) — это имя пода (Pod), в котором работает контейнер. Доступно через команду hostname или вызов функции gethostname;
  • пространство имен пода (Pod) — доступно как переменные окружения;
  • пользовательские переменные окружения из манифеста (описания) пода (Pod) — поговорим о них позже;
  • переменные окружения, указанные статически в docker-образе (например, при сборке).

Информация о других объектах в кластере Kubernetes доступна контейнеру тоже в качестве переменных среды. Например, служба с именем foo, внутри контейнера с именем bar будет отображаться в переменных окружения так:

При создании пода (Pod), можно установить переменные окружения для контейнеров, которые работают в данном поде. Для этого нужно добавить в файл конфигурации (манифест) поле env или envFrom, например так:

Сохраним предложенный манифест в файле envars.yaml и запустим под в кластере Kubernetes:

Проверим список запущенных подов:

Результат предыдущей команды будет примерно таким:

Подключаемся к запущенному контейнеру:

Выводим на экран переменные окружения с помощью команды printenv(вывод сокращен):

Примечание. Стоит помнить, что переменные, установленные с помощью env или envFrom переопределяют значения переменных установленных в docker-образе.

Чуть больше информации о переменных окружения можно получить здесь. Кроме того, в отдельных статьях мы также рассмотрим использование секретов (Secrets) и конфигов (ConfigMaps) — как частных случаев установки переменных окружения внутри docker-контейнеров.

Часть 9: Метки и селекторы

Метки (labels) представляют собой пары ключ/значение, которые назначаются объектам (например, подам) в кластере Kubernetes. Эти метки предназначены для указания идентифицирующих атрибутов объектов, осмысленных и имеющих отношение к пользователям.

 

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

Метки могут прикрепляться к объектам во время создания или же добавляться (и изменяться) в любое время. Каждый объект в кластере Kubernetes может иметь собственный набор ключей/значений. Каждый ключ должен быть уникальным для данного объекта:

В зависимости от конкретных требований проекта, подхода к разработке и тестированию, используемых технологий, бюджета (и прочих факторов) для доставки приложения конечному пользователю у вас может быть несколько датацентров/кластеров, разные наборы нод в кластере (отличающиеся, например по CPU), несколько различных окружений, множество микросервисов и т. д. С помощью меток можно «задокументировать» запущенные в кластере объекты в понятном для пользователя формате, а также использовать метки в селекторах, что существенно упрощает управление инфраструктурой. Пример наиболее часто используемых меток:

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

Корректные имена ключей в метках состоят из двух сегментов: опционального префикса и имени, разделенных символом /. Имя — обязательная часть — должно начинаться и заканчиваться буквенно-цифровым символом ([a-z0-9A-Z]), может содержать в себе дефисы (-), подчеркивания (_) и точки (.). Максимально допустимая длина имени ключа — 63 символа.

Опциональный префикс (если определен) должен быть DNS-поддоменом (разрешается использование точек) не длиннее 253 символов и заканчиваться символом /. Например, префикс kubernetes.io/ зарезервирован для основных компонентов Kubernetes.

Значения меток, так же как и имена ключей, должны начинаться и заканчиваться буквенно-цифровым символом ([a-z0-9A-Z]), содержать в себе дефисы (-), подчеркивания (_) и точки (.). Максимально допустимая длина значения метки — 63 символа.

С использованием селектора меток (label selector) в кластере Kubernetesпользователь может идентифицировать набор объектов. Селектор — это примитив основной группировки в Kubernetes. В данный момент API поддерживает два вида селекторов — основанных на точном соответствии (equality-based) и на соответствии набору (set-based). Если в селекторе указывается несколько соответствий, то они должны быть перечислены через запятую — в данном случае для их объединения будет использоваться логический оператор AND (&&).

Селектор, основанный на точном соответствии поддерживает только три возможных оператора сравнения — === и != — первые два из них означают равенство, третий — неравенство. Например:

В данном примере селектор выберет все ресурсы, у который ключ environmentсоответствует значению production, а также те ресурсы, у которых ключ tierне равен frontend (в том числе ресурсы, у которых нет ключа tier).

Селектор, основанный на соответствии набору также поддерживает три возможных оператора — innotin и exists (последний используется только для проверки наличия/отсутствия ключа). Например:

В данном примере, первая строка позволит выбрать все ресурсы, у которых есть ключ environment со значениями production или qa. Вторая строка выбирает ресурсы у которых есть ключ tier и любые значения кроме frontendи backend. Третья строка служит для выбора всех ресурсов, у которых в метках есть ключ partition (значения не проверяются), а четвертая — наоборот, выбирает ресурсы которые не имеют ключа partition.

Есть возможность комбинировать оба типа селекторов, например:

Несколько примеров использования селекторов с утилитой kubectl:

Набор подов для сервиса (service) определяется с помощью селектора. Аналогично, набор подов, которыми управляет Replication Controller также определяется с помощью селектора. Для этих двух объектов может быть использован только селектор на основе точного соответствия (equality-based) — об этом мы уже упоминали в статье Знакомство с Kubernetes. Часть 4: Реплики (ReplicaSet). Выглядеть это может примерно так:

Для более новых объектов Kubernetes, например, (JobDeploymentReplica SetDaemon Set) может быть использован селектор на основе соответствия набору (set-based). Например:

На этом с метками и селекторами все, в следующей статье рассмотрим неймспейсы (namespaces) в Kubernetes.

Часть 11: Сервисы (services)

Поды в кластере Kubernetes смертны — они создаются (рождаются), но когда под по какой-либо причине умирает, то он не воскресает. И хотя каждый под при создании получает свой собственный IP-адрес, этот адрес нельзя назвать постоянным и стабильным вследствие «смертности» подов. Давайте разберемся!

 
 

К примеру, ReplicaSet может динамически изменять количество подов в кластере (путем масштабирования), при этом новые поды могут быть запущены на других узлах (нодах) кластера — в этом случае IP-адрес пода наверняка изменится.

Это порождает проблему: если некий набор подов (назовем их backends) предоставляют функционал другому набору подов (назовем их frontends) внутри кластера Kubernetes, то как frontend-поды узнают адреса backend-подов и взаимодействуют с ними?

Сервис в Kubernetes — это абстракция, определяющая логический набор подов и политику доступа к ним (иногда такой набор подов еще называют микросервисом). Как правило, этот набор подов определяется на основе меток (присваиваются в момент создания подов) и селекторов.

Например, backend — это 3 реплики подов, занимающихся обработкой изображений. Все три пода абсолютно идентичны с функциональной точки зрения (так как это реплики), поэтому frontend не должен беспокоиться на какой именно backend-под попадет запрос — это работа сервиса.

Простейший пример сервиса может выглядеть так:

Согласно данной спецификации, будет создан объект «сервис» (service) с именем my-service, перенаправляющий запросы на порт 9376 каждого пода, у которого присутствует метка app=MyApp. Этому сервису также будет назначен отдельный IP-адрес.

Согласно указанному селектору, в кластере непрерывно будут проверяться поды на наличие метки app=MyApp, а результат такой проверки будет публиковаться (с помощью POST) в объект Endpoints с таким же именем — my-service.

Стоит отметить, что сервис может сопоставлять входящий порт (port:) с любым портом назначения (targetPort:). Если параметр targetPort не указан, то по умолчанию будет использоваться тот же порт, что и в параметре port. Еще одна интересная особенность — targetPort может содержать строку с именем порта в подах (а сам номер порта, присвоенный этому имени, может быть разным для каждого пода).

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

  • внешний кластер баз данных в production-окружении, локальная БД для тестового окружения;
  • совершенно иной сервис в другом неймспейсе или кластере;
  • часть бекендов, запущенных вне кластера Kubernetes (например, если вы только переносите свою инфраструктуру).

В любом из предложенных вариантов следует создать сервис без селекторов, например:

и, так как селекторов нет, соотвествующий объект Endpoints не создастся автоматически. Его необходимо создать вручную, тем самым указав связь между сервисом и конечной точкой (бекендом), например так:

Примечание. IP-адрес в данном случае не может находиться в диапазонах 127.0.0.0/8, 169.254.0.0/16 и 224.0.0.0/24.

Для многих сервисов обычным делом является использование (открытие) нескольких портов. В таком случае, все что необходимо — присвоить имена всем портам, например:

В Kubernetes существует несколько вариантов предоставления доступа к сервисам, которые называются типы (ServiceTypes):

  • ClusterIP: предоставляет доступ к сервису на внутреннем IP-адресе кластера (сервис доступен только внутри кластера). Тип ClusterIPиспользуется по умолчанию;
  • NodePort: предоставляет доступ к сервису на IP-адресе каждого узла (ноды) кластера, на статическом порту (из диапазона 30000-32767). Автоматически создастся и сервис типа ClusterIP, на который будут маршрутизироваться запросы с NodePort. Взаимодействовать с сервисом можно также из-за пределов кластера, используя в качестве адреса <NodeIP>:<NodePort>;
  • LoadBalancer: предоставляет доступ к сервису используя балансировщик (load balancer) облачного провайдера. При этом автоматически создаются сервисы типа NodePort и ClusterIP, на которые будут маршрутизироваться запросы с балансировщика;
  • ExternalName: особый случай — сопоставляет имя сервиса с содержимым поля externalName (например, foo.bar.example.com), возвращая CNAME запись. Никакого проксирования не происходит.

Больше информации о сервисах можно найти в официальной документацииили в статье Understanding kubernetes networking: services.

Часть 12: Аннотации

Для добавления произвольных, неидентифицирующиих метаданных к создаваемым в кластере Kubernetes объектам можно использовать аннотации. Клиенты (в том числе инструменты и библиотеки) могут получать и использовать эти данные. Давайте разберемся!

 
 

В Kubernetes для добавления метаданных к объектам используются метки и аннотации. Метки обычно используются для выбора объектов селекторами или для поиска наборов объектов, которые соответствуют определенным условиям. Аннотации же, напротив, не используются для выбора или идентификации объектов. В аннотациях можно использовать символы, запрещенные для использования в метках, аннотации могут быть структурированными или нет, а также отличаться размером (количеством символов).

Как и метки, аннотации представляют собой пары ключ/значение, и в общем виде могут выглядеть так:

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

Несколько примеров информации, которую можно (а иногда и нужно) добавлять в аннотации:

  • поля, создаваемые декларативной конфигурацией (declarative configuration layer). Такие поля, прикрепленные в виде аннотаций, позволяют отличать их от значений по умолчанию или от автоматически генерируемых полей;
  • версия билда, релиза, информация о docker-образе (временные метки, имя ветви из репозитория, номер PR и т.д.);
  • информация о логировании, мониторинге, аналитике и пр.;
  • информация об используемых библиотеках или утилитах, которые могут использоваться в целях отладки;
  • информация о пользователе или системе (к примеру, URL-адреса связанных объектов или список зависимых микросервисов);
  • метаданные для ускоренного отката на предыдущую работоспособную версию приложения (например, конфиги);
  • контактные данные ответственных за данный объект лиц.

Стоит отметить, что при создании объектов в кластере Kubernetes с помощью команды kubectl apply -а <имя_файла>, в аннотации к объекту автоматически создастся ключ kubectl.kubernetes.io/last-applied-configuration:, содержащий, как можно догадаться, последнюю успешно примененную конфигурацию (в json-формате).

Например, если сервис описан в файле auth0-svc.yaml следующим образом:

и создаем его командой:

то с помощью команды:

можно получить не только описание сервиса (в том числе, с полями по умолчанию), но и последнюю удачно развернутую версию из аннотации:

Часть 13: Конфигмапы (ConfigMap)

Использование конфигмапов (ConfigMaps) позволяет разделять конфигурационные файлы и контейнеры с приложениями, избавляя от необходимости упаковывать конфиги в docker-образ. В данной статье рассмотрим несколько примеров использования конфигмапов — давайте разберемся!

 
 

Для создания объекта ConfigMap в кластере Kubernetes используется команда:

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

Создание конфигмапа (ConfigMap) из каталога подразумевает создание объекта из одного или нескольких файлов, расположенных в одной директории. Например, создаем каталог и скачиваем в него два файла с параметрами:

Создаем конфигмап из каталога:

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

Проверим содержимое конфигмапа (секция data:), вывод сокращен:

Создание конфигмапа из файла тоже не должно вызывать сложностей:

Проверка:

К слову, есть возможность несколько раз указывать параметр --from-file для данной команды — при этом будет создан конфигмап из нескольких файлов, например:

Есть возможность создавать конфигмапы из файлов с переменными (env-file). Эти файлы должны содержать информацию в формате ключ=значение (по одному на строку), пустые строки и строки, начинающиеся с символа #игнорируются. Например, создадим в нашем каталоге файл с именем game-env-file.properties и следующим содержимым:

Теперь создадим из этого файла конфигмап командой:

Cодержимое конфигмапа:

Стоит отметить, что при передаче нескольких параметров --from-env-file для создания конфигмапа, будет использован только последний из указанных файлов.

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

Обратите внимание на название ключа:

Еще одна возможность — создавать конфигмапы указывая параметры прямо в командной строке с помощью --from-literal:

Проверим:

Напоследок — самый удобный (на мой взгляд) вариант создания конфигмапов — описание их в отдельном yaml-файле (по аналогии с другими объектами Kubernetes). Создадим два таких файла:

Теперь мы можем использовать созданные выше конфигмапы при описании пода (Pod):

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

Можно создать конфигмап с несколькими парами ключ=значение, например:

Такой конфигмап в описании пода будет выглядеть несколько проще:

Еще одна важная (и самая востребованная) особенность — возможность монтировать конфигмапы в контейнеры в качестве томов. Пример с использованием созданного ранее конфигмапа special-config:

При старте данного пода команда ls /etc/config/ выведет на экран следующее:

Можно указывать конкретный путь для монтирования данных из конфигмапа. Например, сущность special.level из конфигмапа с именем special-config может быть смонтирована в /etc/config/keys внутри контейнера:

При запуске пода команда cat /etc/config/keys выведет на экран:

Напоследок стоит отметить, что смонтированные внутрь контейнера конфигмапы будут обновляться автоматически (за исключением монтирования как subPath). Период обновления, как указано в документации, составляет kubelet sync period + ttl of ConfigMaps cache in kubelet.

Часть 14: Секреты (Secrets)

В кластере Kubernetes объекты типа секрет (secret) предназначены для хранения конфиденциальной информации, такой как пароли, OAuth-токены или ssh-ключи. Давайте разберемся!

Представление конфиденциальной информации в виде секрета является более безопасным и гибким, чем добавление такой информации в открытом виде при описании контейнера или сборке docker-образа.

Объекты типа секрет могут быть созданы как пользователем, так и системой. Для использования секрета, под (pod) должен на него ссылаться — чаще всего как на файл, находящийся на примонтированном томе. Кроме того, kubeletможет использовать секреты при скачивании docker-образов из реджистри.

Kubernetes автоматически создает необходимые ему секреты, которые содержат учетные данные для доступа к API, и автоматически модифицирует созданные поды для использования этого секрета.

Объекты типа секрет могут быть созданы с помощью команды kubectl create secret. Рассмотрим пример — допустим, для подключения к БД из пода необходимы логин и пароль, которые находятся в отдельных файлах:

Убедиться, что секрет успешно создан можно так:

Стоит отметить, что ни get, ни describe не отобразят содержимое данного секрета на экране пользователя — это сделано из соображений безопасности.

Еще один вариант создания секрета — сначала описать его в формате yaml (или json), и только после этого создать секрет. Для этого, каждое используемое значение должно быть закодировано в base64:

Создаем yaml-файл со следующим содержимым:

Здесь поле data это map, ключи которого могут содержать буквенно-цифровые последовательности и символы -_ и ., а значения — необходимые данные, закодированные в base64.

Теперь можно создать секрет с использованием команды kubectl create:

Получим информацию о только что созданном секрете с помощью kubectl get secret (вывод сокращен):

Раскодировать значение из секрета можно, например, так:

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

Если в поде существует несколько контейнеров, которые должны использовать данный секрет, то секцию volumeMounts: следует добавить в описание каждого из них. Есть возможность указания конкретного пути монтирования внутрь контейнера значения отдельного ключа из секрета:

В этом примере значение username будет доступно по пути /etc/foo/my-group/my-username вместо /etc/foo/username, а password доступен не будет.

Примечание. Можно задавать права доступа к объектам типа секрет (и даже к отдельным ключам).

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

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

Еще один частный случай использования секретов — параметр imagePullSecrets, который содержит пароль для доступа к docker-реджистри. Подробнее о нем можно почитать здесь.

Больше информации о работе с секретами можно найти в официальной документации.

Часть 15: Установка и настройка kubectl

Kubectl — консольная утилита, без которой даже нельзя представить работу с кластером Kubernetes. В предыдущих статьях цикла мы неоднократно использовали данную утилиту для создания и управления объектами в кластере, получения информации и обновления интересующих нас ресурсов. Давайте разберемся с установкой и настройкой kubectl!

Существует множество способов установки консольной утилиты (в том числе и в зависимости от используемой операционной системы). Например, для «классической» установки kubectl в Ubuntu или Debian, следует воспользоваться следующими командами:

Для Ubuntu также существует вариант установки утилиты с помощью пакетного менеджера snap:

В операционных системах CentOS, RHEL или Fedora установка будет выглядеть так:

Для MacOS можно воспользоваться любым из следующих способов установки утилиты kubectl:

или

Даже пользователи ОС Windows могут себе позволить установку kubectl:

Помимо приведенных выше команд, установить kubectl можно и как часть Google Cloud SDK:

И воспользоваться для установки утилитой curl:

  • MacOS:
  • Linux:
  • Windows:

Для доступа к кластеру Kubernetes утилите kubectl необходим конфигурационный файл (по умолчанию расположен в ~/.kube/config), который создается автоматически. Подробнее о настройке доступа можно узнать здесь.

В kubectl есть отличное встроенное автодополнение для оболочек bash и zsh. Настроить его несложно — для начала нужно установить пакет bash-completion:

  • Linux:
  • MacOS:

И добавляем автодополнение в оболочку:

  • Linux:
  • MacOS:

Если вы в качестве оболочки используете zsh, то необходимо отредактировать файл ~/.zshrc, добавив в него следующие строки:

В случае использования Oh-My-Zsh, то в файле ~/.zshrc нужно отредактировать строку с плагинами, например:

Больше полезной информации об утилите kubectl и примеров можно найти по ссылке.

Часть 16: Различия в Replication Controller, Replica Set и Deployments


Как инструмент оркестрации контейнеров, Kubernetes предусматривает управление несколькими экземплярами (репликами) контейнеров. На сегодняшний день существует несколько способов организации репликации — в данной статье мы рассмотрим три варианта: Replication ControllersReplica Sets и Deployments. Давайте разберемся!

Прежде всего, нужно знать зачем нужна репликация контейнеров в кластере Kubernetes. Чаще всего, это нужно для:

  • надежности. C несколькими экземплярами приложения вы избавляетесь от проблем в случае отказа одного (или нескольких) из них;
  • балансировки. Наличие нескольких экземпляров приложения позволяет распределять траффик между ними, предотвращая перегрузку одного инстанса (под / контейнер) или ноды;
  • масштабирования. При возрастающей нагрузке на уже существующие экземпляры приложения Kubernetes позволяет легко увеличивать количество реплик по необходимости.

Репликация также хорошо подходит в случае работы с микросервисами, облачными (cloud-native) и мобильными приложениями.

Replication Controller — традиционный (изначальный) способ организации репликации в кластере Kubernetes. В данный момент он заменяется более «свежим» вариантом — Replica Sets, но не будет лишним понять, что это и как работает.

С помощью объекта Replication Controller можно создать несколько экземпляров подов и отслеживать их состояние. Если один (или несколько) подов завершаются с ошибкой, то контроллер создаст и запустит новый экземпляр (чтобы количество подов всегда соответствовало желаемому). Кроме того, Replication Controller предоставляет возможность масштабирования подов.

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

Далее создадим объект Replication Controller в кластере на основе описания выше:

Получим детальное описание созданного объекта:

Как видно, все три реплики запущены (ожидаемо). Увидеть состояние подов можно с помощью команды:

Перед рассмотрением Replica Sets «убираем» за собой:

Как мы уже ранее говорили, Replica Sets — это более современная версия все того же Replication Controller, которая отличается поддержкой множественного выбора в селекторе. Создадим файл rs.yaml с таким содержимым:

Здесь все практически то же самое, что и в описании Replication Controller, за исключением того, что мы используем
matchLabels. Но можно пойти дальше и заменить секцию matchLabels на такую:

Создаем объект в кластере командой:

Смотрим описание:

Как видим, Replica Sets действительно очень похожа не Replication Controller. Еще одно важное отличие, о котором мы не упомянули — команда rolling-update будет работать для Replication Controller, но не для Replica Sets.

«Убираем» за собой:

Вариант репликации подов с использованием объекта Deployment — еще более высокий уровень абстракции. На самом деле, под капотом, Deployment будет самостоятельно создавать необходимые Replica Sets (следовательно, предоставлять тот же функционал, но с возможностью rolling-update/rollback).

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

Запускаем объект в кластере:

Просмотр деталей:

Часть 17: Введение в YAML


В предыдущих статьях цикла мы создавали объекты в кластере Kubernetesнесколькими способами — исключительно с помощью командной строки или с использованием манифестов в формате JSON или YAML. В этой статье подробнее остановимся на описании манифестов с помощью YAML-синтаксиса — как наиболее удобного (на мой взгляд).

YAML (Yet Another Markup Language или YAML Ain’t Markup Language) — расшифровка зависит от вашего восприятия — это простой для восприятия человеком, структурированный формат описания конфигурации.

Использование YAML для описания k8s-манифестов предоставляет целый ряд преимуществ, например:

  • удобство — больше не нужно добавлять все параметры в командной строке при создании объекта в кластере;
  • поддержка: YAML-файлы могут храниться в системе контроля версий, благодаря чему легко можно отслеживать их изменения;
  • гибкость: с помощью YAML-манифестов можно создавать гораздо более сложные структуры, чем только в командной строке.

По сути, YAML — это надмножество (superset) JSON, следовательно любой валидный (правильный) JSON-файл также является валидным файлом YAML. То есть, если вы ничего не слышали о YAML-формате, но хорошо знаете JSON — у вас уже есть все необходимое для старта. Ни в коем случае не призываю целиком и полностью отказываться от JSON — могут быть ситуации, когда он более удобен.

К счастью, для создания корректного описания манифеста в YAML-формате достаточно знать только два типа структур:

  • списки (lists);
  • мапы (maps).

Этого достаточно. Конечно, это могут быть мапы списков (или списки мап), но наличия этих двух структур вполне хватит.

Начнем со знакомства с мапами (maps). Мапы позволяют связывать (маппить) пары ключ-значение — это именно то, что нужно при описании конфигурационных файлов. Например, у вас может быть файл конфигурации, который выглядит следующим образом:

Здесь первая линия (три дефиса) — это разделитель (нужен, если вы планируете использовать несколько разных структур в одном файле), далее описаны два ключа (apiVersion и kind) и их значения (v1 и Podсоответственно).

В формате JSON это выглядело бы так:

Можно описывать более сложные структуры, у которых значением для определенного ключа может быть еще одна мапа:

Как видим, у ключа metadata значением будут еще две мапы (с ключами nameи labels), а у ключа labels, в свою очередь, значением будет мапа с ключом app. Можно создавать такие структуры любого уровня вложенности — все зависит от ваших потребностей.

YAML-процессор «понимает» связанность этих элементов благодаря форматированию — обратите внимание на отступы. В этом примере для отступов используются 2 пробела, но на самом деле количество пробелов не имеет значения — главное соблюдать уровни отступов. Например, ключи name и labels находятся на одном уровне отступов, следовательно процессор будет считать, что они являются значением для ключа одной и той же мапы; точно также YAML-процессор знает, что app — это значение для ключа labels.

Небольшой совет: не стоит использовать в YAML-файле в качестве отступов табуляцию.

Итак, если второй пример преобразовать в JSON, то получим следующее:

Что касается YAML-списков, то по сути это просто перечисление объектов, например:

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

Ну и конечно же элементами списка могут быть мапы:

Эквивалент в JSON-формате:

Используя описанный в YAML-файле манифест, можно создать в Kubernetesобъект под (Pod). Для этого нужно выполнить команду:

Что касается YAML-формата, то всю необходимую информацию можно найти здесь.

Часть 18: PodPresets


В этой статье рассмотрим использование PodPresets — объектов, с помощью которых можно добавлять определенную информацию в поды во время их создания. Информация может включать в себя секреты, тома и переменные окружения. Давайте разберемся!

Для указания подов (Pods), к которым должен применяться API ресурс PodPresets используются селекторы меток. Использование PodPresetsпозволяет значительно сократить размеры манифестов и избавиться от копипасты.

В Kubernetes существует контроллер (admission controller) PodPreset, который, если включен, применяет PodPresets к запросам на создание подов. При этом выполняются следующие действия:

  • проверяются все доступные PodPresets;
  • проверяется соответствие селекторов меток любого PodPreset меткам создаваемого контейнера;
  • делается попытка добавить информацию (настройки), определенную в PodPreset, в создаваемый под;
  • при ошибке добавления информации из PodPreset под создается без каких-либо вложенных ресурсов из PodPreset и записывается сообщение об ошибке;
  • при успешном добавлении информации из PodPreset в описание пода также добавляется аннотация (чтобы понимать, что под модифицирован с помощью PodPreset). Аннотации выглядят так podpreset.admission.kubernetes.io/podpreset-<pod-preset name>: "<resource version>".

Каждому поду (Pod) можно сопоставить ноль или более PodPreset; каждый PodPreset может быть применен к нулю или нескольким подам. При добавлении из PodPreset данных, содержащихся в EnvEnvFrom и VolumeMountsизменяется спецификация КАЖДОГО контейнера в поде; при добавлении данных, содержащихся в Volume, меняется спецификация пода.

Для использования PodPreset необходимо:

  • включить API settings.k8s.io/v1alpha1/podpreset. Например, добавлением параметра settings.k8s.io/v1alpha1=true к опции --runtime-config API-сервера;
  • включить контроллер PodPreset, например добавив значение PodPreset в параметр --enable-admission-plugins API-сервера;
  • создать объект PodPreset в соответствующем неймспейсе.

Рассмотрим конкретный пример. Создадим файл preset.yaml следующего содержимого:

Создадим данный объект в кластере Kubernetes:

Проверим наличие объекта:

Теперь при создании подов с меткой role: frontend к ним будет добавляться информация из данного PodPreset. Проверим данное утверждение — создаем файл pod.yaml с таким содержимым:

Создаем под:

Убедимся, что под создан и запущен:

Получим подробное описание пода с помощью команды:

Как и ожидалось, к поду была добавлена информация из PodPreset.

Более сложный пример (с использованием ConfigMap). Описание пода выглядит следующим образом:

Описание конфигмапа (ConfigMap) выглядит так:

Содержимое файла с описанием PodPreset будет теперь таким:

После создания пода и добавления к его спецификации информации с PodPreset он будет выглядеть так:

Чуть больше информаци об использовании PodPreset можно найти тут.

https://letsclearitup.com.ua