Ingress
Links
Ingress Controller
Объект служит в роли прокси и балансировщика L7.
Создание минимального ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress-controller
spec:
replicas: 1
selector:
matchLabels:
name: nginx-ingress
template:
metadata:
labels:
name: nginx-ingress
spec:
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
args:
- /nginx-ingress-controller
- --configmap=$ (POD_NAMESPACE)/nginx_configuraruin
env: # nginx требует 2 переменные для конфигурации
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
Для конфигурации ingress на nginx также нужен ConfigMap. В него закладывается конфигурация nginx, которая в обычном варианте nginx как reverse-proxy вписывалась в config самого nginx:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-configuration
Также необходимо создать Service, который публикует ingress вовне:
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
- port: 443
targetPort: 443
protocol: TCP
name: https
selector:
name: nginx-ingress
И нужен Service Account для аутентификации:
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccounts
Ingress Resource
Набор правил ingress называются Ingress Resource.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-rules
spec:
backend:
serviceName: app-service
servicePort: 80
Определение нахождения правил ingress resource:
kubectl get ingress -A # найти Ingress Resource среди Namespaces
Редактирование правил ingress resource:
kubectl edit ingress <имя ingress resource> -n <namespace>
Nginx rewrite rules
Настройка rewrite-target
нужна, чтобы правильно транслировать сетевой путь.
# Без rewrite-target:
http://<ingress-service>:<ingress-port>/watch --> http://<watch-service>:<port>/path
# С rewrite-target типа replace("/path","/"):
http://<ingress-service>:<ingress-port>/watch --> http://<watch-service>:<port>/
Для включения правил rewrite, нужно добавить в манифест annotations:
1. apiVersion: extensions/v1beta1
2. kind: Ingress
3. metadata:
4. name: test-ingress
5. namespace: critical-space
6. annotations: # применение rewrite правил
7. nginx.ingress.kubernetes.io/rewrite-target: /
8. spec:
9. rules:
10. - http:
11. paths:
12. - path: /pay
13. backend:
14. serviceName: pay-service
15. servicePort: 8282
Kubernetes Security
Kubernetes Security
Link: https://habr.com/ru/companies/vk/articles/730158/
Корректная конфигурация кластера k8s
- K8s CIS Benchmark => kube-bench, проверка конфигов узлов кластера;
- Ограничить сетевой доступ к k8s API (white lists, VPN);
- Каждый пользователь имеет уникальный ID при доступе к k8s API; после развертывания кластера никто не должен в нем аутентифицироваться как system:masters:
- если кто-то украдет у меня этот kubeconfig, придется перевыпускать все сертификаты в кластере, потому что отозвать доступ у группы system:masters без этого невозможно.
- Настроить RBAC. Например, некий оператор в кластере получает список всех подов. В этом случае не нужно выдавать ему возможность видеть все секреты и тем более редактировать их.
Сканирование образов
- Разбор уязвимостей: не каждая из них ведёт к эксплуатации;
- Использовать общие базовые образы - при нахождении в таких уязов, их моно заменить и перезапустить CI/CD;
- Уменьшение числа зависимостей; Инструменты debug не должны оказываться в образах для prod;
- Собираем артефакт отдельно и копируем в итоговый контейнер, который будет использоваться в кластере.
Сетевая безопасность
- CNI с сетевыми политиками; Если stage + prod в 1 кластере, разрабы попадают в stage, а из него - в prod, если нет сетевой изоляции; “политики как код” - развёртываются вместе с приложениями из Git-репозитория; Внедрять политики со старта, пока всё просто;
- Ingress + egress политики настроены для всех компонентов кластера;
- Все соединения должны быть явно разрешены.
Контроль над запускаемыми приложениями
- Pod Security Admisson закрывает базовые потребности
- Pod Security Standards запрещают пускать поды от root, использовать host network и т.д.
- Если этого недостаточно, тогда:
- Точки контроля:
- Запрет на использование образов с DockerHub;
- Обязательно указывать priorityClass;
Аудит и регистрация событий
- Самый простой из коробки - аудит Kubernetes API;
- Логирование в файл или stdout, отправка в SIEM и анализ.
- Часть команд (пример: crictl exec) не отобразятся в логе Kubernetes API. Нужен сквозной аудит хостовая ОС + k8s узел. Например, с помощью Falco:
- системные вызовы на хосте;
- аудит-лог от Kubernetes API с помощтю Webhook backend;
- проверка потока событий с помощю сконфигурированных правил;
- отправка алертов в случае нарушений правил.
- Технология eBPF в ядре - для событий хоста + контейнеров одновременно.
Расширение защиты
- Аутентификация и авторизация
- Аудит RBAC
- Управление секретами
- Защита цепочек поставок:
- Запрет запуска образов с уязами;
- Запуск только подписанных образов (cosign).
- Режим обучения для создания политик;
- Авто-реагирование на события аудита.
История ИБ K8s
- 2016 - на kubelet не было механизма аутентификации. Надо было через SSH-Tunneling защищать. 120 вариаций k8s в 2024. В 1 до сих пор осталась эта проблема;
--insecure-port=8080
- даёт cluster-admin без авторизации. Можно нацелить kubectl на него и получить все права. Убрали только в 1.20; Облачный пров повесил этот порт на container network, клиенты не могли его выключить;
- Irrevocable credentials. Нет поддержки по удалению клиентских сертификатов; Заказчик даёт аудитору k8s файл такого серта, из группы system:masters, и далее в течение года он действителен; irrevocable secrets - вплоть до 1.25, никогда не протухают, убиваются только вместе с service account. Они рано или поздно утекают - в git repo, в тикете к поддержке и т.д. Взамен пришли expiring token requests;
- RBAC появился не сразу. Было
--authorization-mode=AlwaysAllow
. В 1 вариации k8s до сих пор так осталось.
- Helm 2 + служба Tiller (у которой по gRPC TCP44134 нет аутентфикации, и он обычно cluster-admin), которую можно найти по DNS и компрометировать кластер.
Что сегодня
- До сих пор можно выдавать client certs, а также long-lived tokens - до 1 года;
По-умолчанию нигде (кроме OpenShift) вот это всё не включено сразу:
- Pod Security Admission GA 1.25
- Validating Admission Policy GA 1.30
- Внешние опции (Kyverno, OPA и т.д.)
“Unpatchable 4”
CVE-2020-8554
Перехват трафика в multi-tenant кластерах. Кроме тех, где Cillium работает БЕЗ kube-proxy (потому что этот баг зависит от kube-proxy); Либо с помощью Kyverno и т.д. заблокировать создание clusterIP на внешних service with external ip.
CVE-2020-8561
Server-Side-Forgery (SSRF).
ValidatingAdmissionWebhook + Remote Debug Level = Debug (MAX)
CVE-2020-8562
SSRF + Time-of-Check-Time-of-Use (TOCTOU)
K8s API = HTTP-Proxy по сути своей. Но в нём есть hardcoded список адресов IP, куда он не проксирует (например, localhost).
При наличии подконтрольного DNS, можно кидать к нему запрос на подключение на случайный IP, а DNS транслирует его в сторону API-сервера как localhost.
Лечится с помощью службы konnectivity, защищающей k8s Control Plane от запросов от Pod Network.
https://kubernetes.io/docs/tasks/extend-kubernetes/setup-konnectivity/
https://stackoverflow.com/questions/61706923/what-is-the-konnectivity-service-for-kubernetes
CVE-2021-25740
Multi-tenant environment.
Load-balancer allows requests from endpoint and passes commands to other namespace.
Kaspersky Container Security
Скрипт для скачивания продукта (версия 1.2.2) локально:
#!/bin/bash
# Securely obtain credentials (replace with your actual method)
read -s -p "Docker Password: " docker_password
docker login repo.kcs.kaspersky.com -u <USER_LOGIN> -p "$docker_password"
images=(
"repo.kcs.kaspersky.com/images/services/clickhouse:v1.2.2-without-ssl"
"repo.kcs.kaspersky.com/images/services/event-broker:v1.2.2"
"repo.kcs.kaspersky.com/images/services/image-handler:v1.2.2"
"repo.kcs.kaspersky.com/images/services/panel/nginx:v1.2.2"
"repo.kcs.kaspersky.com/images/services/scanner-server:v1.2.2"
"repo.kcs.kaspersky.com/images/services/licenses:v1.2.2"
"repo.kcs.kaspersky.com/images/services/middleware:v1.2.2"
"repo.kcs.kaspersky.com/images/initer:v1.2.2"
"repo.kcs.kaspersky.com/images/node-agent:v1.2.2"
"repo.kcs.kaspersky.com/images/kube-agent:v1.2.2"
"repo.kcs.kaspersky.com/images/updates:v1.2"
"repo.kcs.kaspersky.com/images/scanner:v1.2.2-with-db"
"repo.kcs.kaspersky.com/images/external/minio:2023.9.30"
"repo.kcs.kaspersky.com/images/external/nats:2.9.17"
)
for image in "${images[@]}"; do
docker pull "$image" || exit 1 # Exit on failure
done
echo "All images pulled successfully."
docker save $(docker images --format '{{.Repository}}:{{.Tag}}') -o KCS122.tar
<USER_LOGIN> и пароль необходимо получить у представителя компании.
Pod Commands & Configs
Links
Commands & Arguments
Команды и аргументы команд, которые срабатывают при запуске контейнера.
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: test_app
env: productinon
spec:
containers:
- name: nginx-container
image: nginx
command: [ "python3" ]
args: [ "app-test.py" ]
## Вариант 2
command:
- "python3"
- "app-test.py"
## Вариант 3
command: [ "python3", "app-test.py" ]
Заменять команды, аргументы, метки и т.д. нельзя. Однако, можно вызвать ошибку, потом пересоздать Pod на лету из сохранённого промежуточного файла:
$ kubectl edit pod nginx-container # отредактировал поле command
error: pods "nginx-container" is invalid
A copy of your changes has been stored to "/tmp/kubectl-edit-1395347318.yaml"
error: Edit cancelled, no valid changes were saved.
$ kubectl replace --force -f /tmp/kubectl-edit-1395347318.yaml
Environmental Variables
Переменные среды задаются как список, похожим образом с командами.
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: test_app
env: productinon
spec:
containers:
- name: nginx-container
image: nginx
env:
- name: APP_COLOR
value: green
ConfigMap
Отдельный объект, который содержит переменные среды. Можно получить их список через kubectl get configmaps
apiVersion: v1
kind: ConfigMap
metadata:
name: mydb
data:
APP_COLOR: blue
APP_MODE: testdev
Императивный способ создания ConfigMap
kubectl create configmap \
<имя конфига> --from-literal=<ключ>=<значение> \
--from-literal=APP_USER=testuser
kubectl create configmap \
<имя конфига> --from-file=<путь до файла>
# --from-file=app_config.properties
Ссылка на ConfigMap в описании Pod
Ссылка производится по именам ConfigMap в виде списка:
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: test_app
env: productinon
spec:
containers:
- name: nginx-container
image: nginx
envFrom:
- configMapRef:
name: mydb
# Вариант взять только конкретную переменную:
env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: mydb
key: APP_COLOR
Secrets
Секреты - это ConfigMap, значения которых кодируются по base64. Можно получить их список через:
kubectl get secrets # список секретов
kubectl describe secret <имя секрета> # не отображает значения
kubectl get secret <имя секрета> -o yaml # отображает значения в файле
Декларативное описание
apiVersion: v1
kind: Secret
metadata:
name: mydb
data:
APP_PWD: dmVyeXNlY3JldA== # base64 Encode
APP_TOKEN: dGVzdGRldg==
Императивный способ создания Secret
kubectl create secret generic \
<имя конфига> --from-literal=<ключ>=<значение> \
--from-literal=APP_USER=testuser
kubectl create secret generic \
<имя конфига> --from-file=<путь до файла>
# --from-file=app_config.properties
Ссылка на Secret в описании Pod
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: test_app
env: productinon
spec:
containers:
- name: nginx-container
image: nginx
envFrom:
- secretRef:
name: mydb
# Вариант взять только конкретное значение:
env:
- name: APP_COLOR
valueFrom:
secretKeyRef:
name: mydb
key: APP_PWD
# Вариант смонтировать как файлы (каждый пароль - отдельный файл)
volumes:
- name: app-secret-volume
secret:
secretName: app-secret
Service Accounts
Специальные учётные записи для доступа к k8s. При создании вместе с ними создаётся объект secret.
- С версии k8s 1.22 объект Secret имеет время жизни;
- С версии k8s 1.24 секрет не создаётся на автомате, нужно его отдельно создать:
kubectl create serviceaccount dashboard-sa
kubectl create token dashboard-sa # с k8s 1.24+ необходимо создать токен, у которого время жизни (по умолчанию) =1 час с момента создания
Pods
Links
Articles
PODs
Pod - наименьшая сущность в k8s. Обычно, pod = контейнер по принципу 1:1. Однако, можно несколько контейнеров разместить в 1 pod, при условии, что они функционально разные. Обычно это главный контейнер приложения и вспомогательные контейнеры, которые с ним связаны.
В обычном Docker, если развернуть множество копий “контейнер приложения” + “вспомогательный контейнер”, то нужно будет иметь карту взаимосвязей между ними всеми. Более того, в случае выхода из строя контейнера с приложением, нужно будет вручную удалять сопутствующий вспомогательный контейнер. От этого всего избавляют pod-ы, в рамках которых всё размещается, обеспечивается внутренняя связность, и далее k8s размножает готовые копии pod-ов в рамках кластера.
Pod-ы добавляют функционал к контейнерам:
- Labels and annotations
- Restart policies
- Probes (startup probes, readiness probes, liveness probes, and potentially more)
- Affinity and anti-affinity rules
- Termination control
- Security policies
- Resource requests and limits
Работа с pod-ами ведётся с помощью API или инструмента kubectl:
kubectl run nginx --image nginx # образ nginx будет скачан с DockerHub
kubectl get pods # список всех pod-ов и их статусов
kubectl get pods --selector app=App1 # отфильтровать вывод по заданному label
Создание Pod через файл YAML
Создадим pod-definition.yml:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
Далее создаём pod командой:
kubectl create -f pod-definition.yml
kubectl get pods
Посмотреть доступные поля, подробную информацию о поле у pod:
kubectl explain pods --recursive
kubectl explain pod.spec.restartPolicy
Посмотреть конкретное поле у всех pod, например, образ image, из которого он сделан:
kubectl get pods -o jsonpath={.items[*].spec.containers[*].image}
Можно у работающего Pod получить спецификацию в YAML, из которой он сделан:
kubectl get pod <имя pod> -o yaml > pod-definition.yaml
Удалить Pod
kubectl delete pod <имя Pod> --now
Зайти внутрь Pod и выполнить команды:
kubectl exec -it <имя pod> -- /bin/sh
Обновить Pod
В конфигурацию pod можно добавить период обновления (например, 30 секунд) и установить “imagePullPolicy: “Always”. Удалить Pod с помощью kubectl delete pod pod_name
. Новый контейнер будет создан на последней версии образа, после чего старый контейнер будет удалён.
spec:
terminationGracePeriodSeconds: 30
containers:
- name: my_container
image: my_image:latest
imagePullPolicy: "Always"
Есть вариант “дёргать” за Deployment, вызывая тем самым обновление:
kubectl patch deployment <имя deployment> -p \
'{"spec":{"template":{"spec":{"terminationGracePeriodSeconds":31}}}}'
Выполнение задач в Pod
Если необходимо, чтобы Pod поработал и выключился, без перезапуска, то необходимо поменять его restartPolicy
, которая по умолчанию стоит в Always
- то есть перезапуск всегда по завершении работы.
spec:
containers:
- name: my_container
image: my_image:latest
restartPolicy: Never # ещё вариант OnFailure
Императивные команды
В отличие от декларативных, такие команды позволяют быстро решить однократную задачу.
kubectl run nginx --image=nginx --dry-run=client -o yaml # --dry-run=client - не создаёт объект, сообщает о возможности его создания
kubectl run httpd --image=httpd:alpine --port=80 --expose=true # создать Pod из образа httpd:alpine и к нему сразу создать ClusterIP с публикацией порта
Multi-Container PODs
Несколько контейнеров в 1 POD делят один адрес IP (между собой они общаются через адаптер localhost), хранилище. Есть несколько типовых сценариев:
- Sidecar pattern - самый популярный случай, один контейнер отрабатывает задачу (например, выгрузки данных на веб-сайт), а другой решает вспомогательную задачу (например, синхронизация данных для последующей выгрузки);
- Init pattern - перед запуском контейнера с основным ПО сначала стартует вспомогательный контейнер, который производит настройку окружения;
- Adapter pattern - ПО в основном контейнере обрабатывает данные, а вспомогательный контейнер передаёт эти данные в другое приложение в понятном ему формате. Например, система SIEM не понимает формат логов приложения, и вспомогательный модуль парсит и транслирует логи в понятный для SIEM формат;
- Ambassador pattern - ПО в основном контейнере отрабатывает задачи, а вспомогательный контейнер вызывает через API внешние системы, чтобы собрать с них данные для обработки, либо передать данные в эти системы.
PODы стартуют атомарно - только после успешного старта всех контейнеров POD считается запущенным. Частичный запуск не допускается. POD целиком всеми контейнерами размещается на одной ноде worker.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 8080
- name: log-agent
image: log-agent
InitContainer - не живёт постоянно, а выполняется ДО загрузки остальных контейнеров в Pod, поэтому его инициализация - в отдельной секции:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: nginx
ports:
- containerPort: 8080
initContainers:
- name: init-service
image: busybox
command: [ 'sh', '-c', 'git clone <some repo to be used by app>' ]
Если таких InitContainer несколько, они будут выполняться последовательно один за другим. Если любой InitContainer не сможет выполниться успешно, k8s будет перезапускать Pod, пока InitContainer успешно не завершится.
Ручное распределение (manual scheduling)
Если в кластере нет распределения, можно указать вручную параметр nodeName:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx-container
image: nginx
nodeName: node01
Без указания этого параметра в отсутствии распределения Pod будет висеть как Pending. K8s не даст указать этот параметр на лету, после добавления Pod надо заменить kubectl replace --force -f nginx-pod.yaml
Subsections of Pods
Readiness Probes
Readiness Probes
Определение, что ПО в контейнер действительно запустилось успешно и готово принимать данные пользователей, можно провести по-разному, добавив в манифест раздел spec -> containers
поле readinessProbe
.
-
Для проверки HTTP сервера:
readinessProbe:
httpGet:
path: /api/ready
port: 8080
initialDelaySeconds: 10 # предусматриваем 10 сек задержку на старте
periodSeconds: 5 # повторяем проверку спустя 5 секунд
failureThreshold: 8 # повторяем проверку 8 раз (по умолчанию 3)
-
Для проверки открытого порта (например, у СУБД):
readinessProbe:
tcpSocket:
port: 3306
-
Для проверки с помощью своей команды:
readinessProbe:
exec:
command:
- cat
- /app/is_ready
Liveness Probes
Периодическое определение, работает ли ПО в контейнере - для случаев, когда падение ПО не приводит к его вылету и закрытию контейнера.
-
Для проверки HTTP сервера:
livenessProbe:
httpGet:
path: /api/health_status
port: 8080
initialDelaySeconds: 10 # предусматриваем 10 сек задержку на старте
periodSeconds: 5 # повторяем проверку спустя 5 секунд
failureThreshold: 8 # повторяем проверку 8 раз (по умолчанию 3)
-
Для проверки открытого порта (например, у СУБД):
livenessProbe:
tcpSocket:
port: 3306
-
Для проверки с помощью своей команды:
livenessProbe:
exec:
command: [ "cat", "/app/is_working" ]
Container Logging
Для получения логов с Pod:
kubectl logs {-f} <pod name> <container name> # -f = tail
kubectl logs -f myapp logger # пример, в случае нескольких контейнеров в Pod выбран контейнер logger
Resources
Requests
Запрос контейнеров на гарантированные ресурсы.
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: mywebapp
image: nginx
resources:
requests:
memory: "4Gi" # 4 гибибайта
cpu: 2 # минималка 0.1 CPU
Limits
Указание ограничений. При переходе лимита по CPU скорость для Pod замедляется (throttling). При переходе лимита по RAM происходит убийство Pod с ошибкой OOM Error (Out-Of-Memory).
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: mywebapp
image: nginx
resources:
requests:
memory: "2Gi"
cpu: 2
limits:
memory: "4Gi"
cpu: 2
Security Contexts
Security Contexts
В описании Pod можно указать ID пользователя, который запускает контейнеры, а также описать его возможности (capabilities).
- Если контекст безопасности определён на уровне Pod, он действует для всех входящих в него контейнеров;
- Если контекст безопасности определён на уровне Pod и на уровне контейнера, то настройки контейнера приоритетны перед настройками Pod.
Уровень Pod:
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
securityContext:
runAsUser: 1001
containers:
- name: ubuntu
image: ubuntu
Уровень контейнера:
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
containers:
- name: ubuntu
image: ubuntu
securityContext:
runAsUser: 1001
capabilities:
add: ["MAC_ADMIN"]
# возможности можно определить ТОЛЬКО на уровне контейнера
Selectors and Affinity
Node Selectors
Добавить пометки к node можно командой:
kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label nodes node-01 size=Large # пример
После этого в описании Pod можно указать Node Selector:
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: data-processor
image:: data-processor
nodeSelector:
size: Large
Node Selectors работают по принципу 1:1 совпадения метки Node и Pod. Для более сложных сценариев применяют Node Affinity.
Node Affinity
Для создание свойств Node Affinity нужно поменять свойства в манифесте Pod:
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: data-processor
image:: data-processor
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: In # условие, может быть наоборот NotIn, или Exists - есть ли вообще такой label, необязательно имеющий значение?
values:
- Large # условие действует на любое из значений списка
- Medium
В случае, если нужные labels отсутствуют на Nodes кластера, есть 2 типа поведения, которое задаётся свойством Pod:
requiredDuringSchedulingIgnoredDuringExecution
- если Nodes с нужными labels нет, вообще не размещать данный Pod на кластере;
preferredDuringSchedulingIgnoredDuringExecution
- если Nodes с нужными labels нет, всё равно разместить данный Pod где-нибудь на кластере.
Если Pod уже запущен на Node в момент, когда добавили label, то в версии 1.27 ничего не произойдёт в обоих случаях. В плане добавить третий тип поведения:
requiredDuringSchedulingRequiredDuringExecution
- если во время работы Pod произойдёт изменение affinity - удалить Pod с Node.
Taints & Tolerations
Для распределения Pods по Nodes применяется сочетание покраски (taint) и восприимчивости (toleration) к ней.
Taints
Покраска Node говорит kube-scheduler, что есть 1 из 3 эффектов:
- NoSchedule - не назначать сюда Pods без toleration;
- PreferNo Schedule - назначать Pods без toleration с наименьшим приоритетом, если больше некуда;
- NoExecute - не назначать сюда Pods без toleration, уже имеющиеся тут Pods удалить и перенести куда-то ещё.
Покраска node:
kubectl taint nodes <имя node> key=value:effect
kubectl taint nodes node01 app=myapp:NoSchedule # пример
kubectl taint nodes node01 app=myapp:NoSchedule- # минус в конце снимает покрас
Tolerations
Поменять восприимчивость Pod к покраске:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx-controller
image: nginx
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect: "NoSchedule"
Replicasets & Deployments
Links
ReplicaSets
ReplicaSet следит за тем, чтобы количество Pod всегда было заданным числом штук (параметр replicas) - не больше и не меньше.
ReplicaSet Controller является более современным аналогом ReplicationController:
- ReplicationController apiVersion = v1
- ReplicaSetController apiVersion = apps/v1
Отличие ReplicaSet в том, что в нём обязательным параметром есть selectors, который отслеживает контейнеры, в том числе созданные ДО создания самого ReplicaSet. Отслеживание можно производить по их меткам labels. Для совпадающих меток работает алгоритм приведения к нужному количеству.
Создание ReplicaSet:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myreplicaset
labels:
name: myapp
spec:
selector:
matchLabels:
env: production ### метка должна совпадать
replicas: 3
template:
metadata: ### шаблон контейнера берётся из описания Pod
name: myapp
labels:
env: production ### метка должна совпадать
spec:
containers:
- name: nginx-container
image: nginx
Шаблон Pod всегда должен быть описан в части template, чтобы ReplicaSet знал, какие Pod создавать.
Команды для работы с ReplicaSets
kubectl create -f <имя файла с описанием replicaset>
kubectl get rs # вывести все ReplicaSet в кластере
kubectl describe rs <имя replicaset> # подробности о ReplicaSet
kubectl delete rs <имя replicaset> # удаляет все Pods и сам ReplicaSet
kubectl edit rs <имя replicaset> # отредактировать описание Replicaset
kubectl scale rs <имя replicaset> --replicas=<новое количество копий>
kubectl replace -f <имя файла с описанием replicaset> # заменить ReplicaSet
Deployments
Deployment - это надстройка над ReplicaSet, добавляет логику обновления Pod с одной версии на другую. При обновлении Deployment имеет 2 стратегии:
- Rolling Update - (по умолчанию) при обновлении Pods, делает это поштучно: один Pod старой версии кладёт (Terminated), сразу поднимает заместо него новый Pod;
- Recreate - при обновлении сначала удаляются все Pods, после чего взамен поднимаются новые. Сопровождается падением/отключением приложения для потребителей.
kubectl apply -f <имя Deployment> # запустить процесс rollout после внесения изменений в манифест
kubectl rollout status <имя Deployment> # узнать о статусе выкатывания
kubectl rollout history <имя Deployment> # узнать о всех ревизиях и причинах их перехода
kubectl rollout history <deployment> --revision=1 # узнать статус конкретной версии
kubectl rollout undo <имя Deployment> # откатить назад обновление Deployment
kubectl rollout undo <имя Deployment> --to-revision=1 # откатить до версии 1
apiVersion: apps/v1
kind: Deployment
metadata:
name: mydeployment
labels:
name: myapp
spec:
selector:
matchLabels:
env: production ### метка должна совпадать
replicas: 3
strategy:
type: RollingUpdate ### стратегия замены Pods
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata: ### шаблон контейнера берётся из описания Pod
name: myapp
labels:
env: production ### метка должна совпадать
spec:
containers:
- name: nginx-container
image: nginx
Команды для работы с Deployments
kubectl create -f <имя файла с описанием Deployment>
kubectl get deploy # вывести все Deployment в кластере
kubectl describe deploy <имя Deployment> # подробности о Deployment
kubectl delete rs <имя Deployment> # удаляет все Pods и сам Deployment
kubectl edit rs <имя Deployment> # отредактировать описание Deployment и произвести его обновление
kubectl edit rs <имя Deployment> --record # отредактировать описание, вызвав обновление и записать команду как причину обновления в список ревизий
kubectl set image deploy <имя Deployment> nginx=nginx:1.18 # пример обновления без редактирования YAML
Императивные команды
В отличие от декларативных, такие команды позволяют быстро решить однократную задачу.
kubectl create deploy nginx --image=nginx --replicas=4
kubectl scale deploy nginx --replicas=4
kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > nginx-deployment.yaml # вывести манифест YAML для последующего редактирования
Services
Links
Services
Service - логический объект в k8s, который позволяет внешним клиентам подключаться к сетевым портам на контейнерах внутри кластеров Pod.
Service делятся на 3 вида:
- NodePort - публикация порта с Pod наружу на уровень Worker Node (причём, если Pods размазаны лишь по части от всех нод кластера k8s, то всё равно они доступны при обращении К ЛЮБОЙ ноде кластера);
- ClusterIP - по сути внутренний балансировщик для обращения, например, части frontend приложения к множеству Pods, реализующих backend;
- LoadBalancer - NodePort с заданием стратегии балансировки (в NodePort - случайный алгоритм балансировки), поддерживается лишь на ряде облачных площадок (AWS, GCM и т.д.). В остальных площадках ведёт себя как NodePort.
NodePort
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30004 # порты ограничены диапазоном 30000-32767
selector:
app: myapp
ClusterIP
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: myapp
LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
type: LoadBalancer # only works with a supported platform
ports:
- port: 80
targetPort: 80
selector:
app: myapp
Команды работы с Service
kubectl create -f <имя файла с описанием Service>
kubectl get svc # вывести все Service в кластере
kubectl describe svc <имя service> # подробности о service
kubectl delete svc <имя service> # удаляет все объект service
kubectl edit svc <имя service> # отредактировать описание service и произвести его обновление
Императивные команды
В отличие от декларативных, такие команды позволяют быстро решить однократную задачу.
kubectl create service clusterip redis --tcp=6379:6379 --dry-run=client -o yaml # нельзя подавать selectors в команду, следует вывести YAML и отредактировать, что небыстро
kubectl expose pod redis --port=6379 --name=redis-service --dry-run=client -o yaml # такой вариант CLusterIP использует labels самого pod как selectors, что намного эффективнее
kubectl expose pod nginx --port=80 --name=nginx-service --type=NodePort --dry-run=client -o yaml # такой вариант NodePort использует labels самого pod как selectors