https://github.com/amorozov87/kubernetes-traning
Kubernetes quick start #
K8S - opensource система оркестрации контейнеров
Основная задача - распределять (по нодам) и менеджить контейнеры с приложениями
Предоставляет:
- service discovery
- load balancing
- autoscaling (как приложения, так и самого кластера)
- HA
- декларативный механизм обновлений полезной нагрузки
Контроллер - демон, следящий за состоянием некоторого объекта, например приложения, которое задеплоили
Conceptions #
- nodes - узлы кластера
- master - нода, на которой задеплоен control plane
- worker - нода с бизнес приложениями
- namespace - виртуальное кластерное пространство внутри одного кластера; нужен для логического разделения задеплоенных приложений
- pods - базовая сущность кластера, абстракция над одним или несколькими контейнерами
- controllers
- controller manager - основной контроллер кластера, входящий в control plane
- operator - кастомный контроллер; пишется самостоятельно
- labels
- volumes
- jobs - одноразово запущенный контейнер, который будет рестартоваться только если процесс завершился с ненулевым кодом выхода (обычный контейнер будет рестартоваться при любом завершении процесса)
- kubectl - утилита управления кластером
Кластерная роль затрагивает весь кластер. Простая роль доступна только в том неймспейсе, в котором она создана.
Роль даёт права на выполнение каких-либо действий (внутри неймспейса)
Правили роли:
- verbs - действия, которые мы можем выполнять
- apiGroups - каждая сущность кластера имеет собственное API (пустое значение означает, все существующие группы)
- resources - ресурсы кластера, к которым можно применять перечисленные действия
Роли могут наследовать правила
Kubernetes cluster architecture #
- master - содержит весь control plane
- etcd - KV db, содержит весь стейт кластера; работает по принципу кворума, поэтому должно быть нечетное количество экземпляров
- API server - центральное звено, все запросы идет через него; для настройки HA требуется внешний балансер, так как kubelet не может смотреть в несколько API серверов
- Scheduler - отвечает за распределение подов по кластеру; *
- Controller Manager - следит за работой компонентов control plane; *
- worker node
- Container engine, например Docker
- Kubelet - агент, общается с API сервером и управляет подами на локальной ноде; ответственен за то, чтобы состояние рабочей нагрузки соответствовало тому, что указано для нее на API сервере
- Kubernetes proxy - отвечает за сетевое взаимодействие внутри кластера
* - умеют самостоятельно выбирать лидера в HA-режиме
Основная сетевая концепция Kubernetes - любой под должен быть доступен для любого другого пода напрямую, без участия NAT`а
Deployment exposed #
Namespace #
Создавался для разграничение энвайроментов с большим количеством пользователей.
Ресурсы уникальны в рамках неймспейса.
Обладает своими квотами.
kubectl completion -h # добавление автодополнения
kubectl get ns # список неймспейсов
kubectl create ns $NS_NAME # создание неймспейса
kubectl edit clusterrole $ROLE_NAME # редактирование роли
Deployment #
Сущность, которая отвечает за абстракцию над задеплоеным приложением
Предоставляет декларативные механизмы апдейтов для подов и replicaSet`ов
меняет актуальное состояние на декларированное
использование дейлойментов - best practice, так как при создании отдельных подов, отсутствует автоматический механизм контроля за его состоянием
при обновлении дейплоймента создается новый replicaSet и появляется возможность быстро откатиться к предыдущему состоянию
Deployment workflow:
YAML\JSON Template -> Deployment -> ReplicaSet -> Pod -> Containers
kubectl create deployment deploymentName --image=imageName
kubectl delete deployment deploymentName
Deployment use cases #
- развернуть replicaSet
- задекларировать новое состояние для подов
- откатиться на более раннюю версию деплоймента
- масштабировать дейлоймент
- приостановить деплоймент для применения множественных фиксов (вероятно не применяется на практике)
- просмотр статуса деплоймента
Pods and Containers #
базовый “строительный блок” кластера
абстракция над одним или несколькими контейнерами
всегда имеет уникальный в пределах кластера IP-адрес
контейнеры в рамках одного пода:
- всегда расположены на одной ноде
- имеют единый сетевой неймспейс, соответственно всегда общаются напрямую друг с другом
Init containers #
Специализированный контейнер, который стартует перед контейнером с приложением
- могут содержать и запускать дополнительные утилиты, которые не желательно включать в основной контейнер с приложением (например по соображениям безопасности)
- предоставляют механизм контролируемой задержки старта основного контейнера
Services (discovery) #
Это абстракция, которая определяет логический набор подов и политики доступа к ним. Поды определяются по лейблам.
Типы сервисов:
- Normal - имя сервиса резолвится в IP кластера
- Headless - резолвится напрямую в IP-адрес пода
Пример сервиса:
kind: Service
apiVersion: v1
metadata:
name: nginx-service
spec:
clusterIP: None # для создания headless сервиса
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
В качестве протоколов поддерживаются TCP (по умолчанию) и UDP
Сервис без селектора нужен для того чтобы K8S мог ссылаться на внешние объекты. Примеры использования:
- использование внешней БД в проде и внутренней в тесте
- использование сервиса в другом неймспейсе
- миграция нагрузки с K8S на сторонние бакенды
Для такого сервиса EndPoint не будет создан автоматически.
Способы открыть доступ к сервису:
- ClusterIP
- открывает доступ по внутреннему кластерному IP
- соответственно, в этом случае сервис доступен только внутри кластера
- дефолтный тип
- NodePort
- открывает доступ к сервису по адресу ноды на статическом порте
- с ним можно общаться снаружи
- ${NodeIP}:${NodePort}
- LoadBalancer
- открывает доступ к сервису снаружи с использованием LA, предоставляемого облачным провайдером
kubectl patch... # обновление ресурсов
Ingress #
- Предоставляет доступный извне URL
- Балансирует трафик (RR)
- Терминирует SSL-соединение
- предоставляет основанный на именах виртуальный хостинг
- требует наличия Ingress-контроллера (одна из имплементаций - NGINX)
Типы ингрессов:
- Single service ingress
- Simple fanout
- Name based virtual hosting
Running you app #
Dry run #
Опция kubectl, которая только печатает объект, который должен был бы быть отправлен.
kubectl create deployment deploymentName --image=imageName --dry-run=true
kubectl create deployment deploymentName --image=imageName --dry-run=true -o yaml > deployment.yaml # пример создания файла деплоймента
Using env vars #
При создании пода можно установить переменные окружения, которые будут переданы в контейнер.
spec:
containers:
...
env:
- name: DEMO_KUBERNETES
value: "Hello from k8s"
Также можно передавать в переменные окружения информацию о самом контейнере:
- metadata.name
- metadata.namespace
- metadata.labels
- metadata.annotations
- spec.nodeName
- spec.serviceAccountName
- status.hostIP
- status.podIP
spec:
containers:
...
env:
- name: MY_NODE_NAME
valueFrom:
fieldPath:
fieldPath: metadata.name
Commands and arguments #
Аналог ENTRYPOINT и CMD инструкций Docker. Задаются в полях command и args конфига. Не могут быть изменены после того, как Под был создан.
Перетирают дефолтные команды и аргументы, которые указаны при сборке образа.
Если указаны только аргументы, дефолтная команда из образа будет использована с новыми аргументами.
Description | Docker field | Kubernetes field |
---|---|---|
Команда, которая будет запущена контейнером | Entrypoint | command |
Аргументы, которые будут переданы команде | Cmd | args |
Image Entrypoint | Image Cmd | Container command | Container args | Command run |
---|---|---|---|---|
[/ep1] | [foo bar] | [ep-1 foo bar] | ||
[/ep1] | [foo bar] | [/ep-2] | [ep-2] | |
[/ep1] | [foo bar] | [zoo boo] | [ep-1 zoo boo] | |
[/ep1] | [foo bar] | [/ep-2] | [zoo boo] | [ep-2 foo bar] |
Scheduling #
Механизмы распределения Подов:
- все механизмы используют лейблы
- Node Selector (позволяет указать, что деплоймент должен быть размещен на ноде с определенным лейблом; устаревший)
- можно объединять несколько лейблов
spec:
nodeSelector:
labelName: labelValue
- Node affinity and anti-affinity (похож на первый, но более гибкий)
- позволяет операции над лейблами:
- And
- In
- NotIn
- Exists
- DoesNotExists
- Gt
- Lt
- правила бывают:
- soft/preferred - предпочтение отдаётся нодам с указанными лейблами, но если они недоступны, под будет задеплоен на любую доступную ноду
- hard/required - требуется полное соответствие лейблам
- все правила применяются только в момент деплоя пода, позднейшие изменения не будут иметь эффекта, пока под не будет передеплоен
- под может быть задеплоен на ноду, если одно из условий nodeSelectorTerms удовлетворено
- –--, если все условия matchExpressions были удовлетворены
- позволяет операции над лейблами:
spec:
...
spec:
affinity:
requiredDuringSchedulingInnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: lableName
operator: In
values:
- labelValue
- inter-pod affinity and inti-affinity
- аналогично, но с подами, а не с нодами
- вариант применения - исключить деплой реплик приложения на одну ноду
- Taints
- имеет:
- ключ
- значение
- эффект
- NoSchedule
- PreferNoSchedule
- NoExecute
- имеет:
- Tolerations
- имеет:
- ключ
- значение
- оператор
- Exists
- Equal
- эффект
Image pull policies #
- ifNotPresent(DEFAULT) - скачивает образ, если его нет в локальном кеше той ноды, куда приезжает приложение
- Always(:LATEST)
- Never
Application restarts #
- Always (default)
- OnFailure
- Never
Применяется ко всем контейнерам в поде. Поды рестартуют с экспоненциальной дажержкой (10с, 20с, 40с…)
Application logs #
kubectl logs ${podName}
--\\-- --previous # следует использовать в случае, если контейнер завалился
Если под имеет несколько контейнеров, то нужно указывать имя конкретного контейнера.
Если контейнер рестартует, k8s сохраняет один остановленный контейнер с логами.
Если под выселяется с ноды, то все относящиеся к нему контейнеры выселяются вместе с логами.
В логи пишется только stdout и stderr PID1
Application scaling #
Replica sets #
Может использовать независимо от других сущностей
Но рекомендуется использовать деплойменты, так как в этом случае, не нужно следить за жизнью контейнера
Описывает конкретное число подов с приложением, которое должно существовать в конкретный момент времени
Scaling #
- по дефолту стартует один под
- можно скейлить до нуля (выключение)
- пропорциональное обновление
Autoscaling #
kubectl sutoscale deployment podName
- предствален в виде отдельно ресурса и контроллера
- дефолтное значение задержки 30 сек
- собирает как ресурсные метрики, так и кастомные
- заданный процент потреблённых ресурсов высчитывается от requests (см. ниже); если это значение не задано, работать не будет
Compute resources managing #
containers:
...
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Updating an application #
Deployment update #
- Kubectl set image
kubectl run nginx --image=nginx:1.12 --replicas=3
kubectl rollout status deploy nginx
kubectl set image deploy nginx nginx=nginx:1.13
- Kubectl edit
kubectl edit deploy nginx
kubectl rollout status nginx
- Kubectl apply
kubectl get deploy nginx -o yaml > nginx_deploymetn.yaml
kubectl apply -f nginx_deployment.yaml
kubectl rollout status deploy nginx
- У k8s есть политика, говорящая сколько подов может быть недоступно в ходе апдейта
- –\–, может быть создано сверх желаемого значение
- раскатка деплоймента стриггерится только, если поменялся сам шаблон (изменение метадаты к этому не приводит)
- каждый раз создается новый ReplicaSet
Deployment rollouts and rollbacks #
- Rollout
kubectl apply -f nginx_deployment.yaml --record
kubectl rollout status deploy nginx
kubectl get deployments
- Update
kubectl set image deploy nginx nginx=nginx:1.13 --record=true
kubectl rollout status deploy nginx
kubectl get pods
- Rollback
kubectl rollout history deploy nginx
kubectl history deploy nginx --revision=2
kubectl rollout ubdo deploy nginx
The deployment lifecycle #
- Pregressing
- создается новый ReplicaSet
- поднимается новый ReplicaSet
- тушится старый ReplicaSet
- Complete
- все реплики обновлены до последней версии
- все реплики доступны
- ни одна старая реплика не запущена
- Failed
- insufficient quota
- insufficient permissions
- readiness probe failures
- image pull errors
- limit ranges
- application runtime misconfiguration
Dealing with storage #
Empty dir #
- создается, когда под добавляется на ноду
- существует, пока под запущен на ноде
- изначально пустой
- может быть смонтирован по тому же или отличающемуся пути
- когда под (по любой причине) удаляется с ноды, данные их emptyDir удалятся безвозвратно
spec:
containers:
...
volumeMounts:
- mountPath: /cashe
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
Host path #
- монтирует директорию с хостовой машины
- может вести себя по разному на различных нодах (поэтому не рекомендуется в проде)
- нужны привелегии для использования
Git repo #
- монтирует пустую директорию и клинирует в нее репу
Persistent volumes #
- абстракция над работой с хранилищем
- может быть запровиженен как автоматически, так и администратором
- PersistentVolumeClaim (PVC) - запрос на хранилище со стороны пользователя
- StorageClass - механизм автоматического привиженинга
Application configuration #
Configmap #
Объект k8s, предоставляющий механизм хранения KV данных.
Может быть использована как:
- аргумент командной строки
- переменна окружения
- файл в волюме
kubectl create configmap nginx-config --from-file=/path/to/dir # k - имя файла, v - содержимое файла
kubectl create configmap nginx-config --from-file=/path/to/file # k - имя файла, v - содержимое файла
kubectl create configmap nginx-config --from-file=${myKeyName}=/path/to/file
kubectl create configmap nginx-config --from-literal=someKey=someValue
Директивы –from-file и –from-literal можно совмещать.
Использование:
- env var:
- from a single ConfigMap
- from multiple ConfigMaps
- in pod commands:
- echo ${KEY_NAME} (правда нужно предварительно создать переменную окружения)
- volume:
- монтирование тома с данными, сохраненными в ConfigMap
- добавление ключей ConfigMap в конкретный путь на томе
Secrets #
Аналогично, но хранится в зашифрованном виде
Ограничения:
- должны быть созданы до пода
- должны быть созжаны в том же неймспейсе
- каждый секрет не может быть больше 1 мб
Jobs and daemons #
Jobs #
- отдельный ресурс со своим контроллером
- создает один или несколько подов, обеспечивая успешное выполнение указанной в образе команды
- как только задача успешно завершается, удаляются все поды, относящиеся к ней
- если задача завершается с ненулевым кодом ответа, под рестартуется (с экспоненциальной задержкой)
- поды могут выполнять задачу параллельно
Daemons sets #
- обеспечивают запуск одному экземпляру пода на каждой (или некоторых) нодах
- если в кластер будет добавлена новая нода, приложение приедет и на нее (с учетом всех ограничений шедулинга)
- удаление Daemon set приведет к удалению всех подов
Примеры использования:
- storage daemon (glusterfs, ceph, etc)
- агенты агрегатора логов
- мониторинг агенты
Stateful applications #
- StatefulSet - разрабатывался для обслуживания stateful приложений и распределенных систем
- предоставляет гарантии очередности старта подов
- каждый под представляет из себя уникальный объект
- спецификации подов такие же как в деплойменте, но поды не взаимозаменяемые
- каждый под имеет собственное не шареное хранилище
- каожый под имеет уникальное имя вида ${StatefulSetName}-${Ordinal}
Ограничения:
- удаление не удаляет хранилища
- требует Headless сервис, так как обычно распределенные приложения плохо работают с прокси
Политики управления подами:
- OrderedReady (default)
- Parallel
Стратегии обновления:
- On Delete
- Rolling Updates
- Partitions