59 KiB
YAML Синтаксис
Основные сущности
- Скалярные значения
string_value: hello
number_value: 42
float_value: 3.14
boolean_true: true
boolean_false: false
- Списки (массивы)
servers:
- web01
- web02
- db01
или
servers: [web01, web02, db01]
- Словари
user:
name: "ivan"
age: 30
admin: true
или
user: {name: ivan, age: 30, admin: true}
- Многострочные строки
description: |
Это многострочный текст.
Он сохраняет переносы строк.
Полезно для документации.
command: >
Это тоже многострочный текст,
но переносы будут заменены пробелами.
|
- сохраняет всё как есть, включая\n
>
- склеивает строки в одну с пробелами
Расширенные возможности
- Ссылки и якори ($, *)
defaults: &default_settings
retries: 3
timeout: 30
server1:
<<: *default_settings
timeout: 10 # переопределено
server2:
<<: *default_settings
- Линтер
yamllint fine_name.yml
Разное
- Null
empty1: null
empty2: ~
empty3:
- Boolean
bool1: yes # интерпретируется как true
bool2: no # false
bool3: on # true
bool4: off # false
чтобы явно задать строку, необходимо использовать кавычки
literal_string: "yes" # не будет true
Разворот кластера
Буду поднимать 1 мастер ноду, 2 воркер ноды, 1 вспомогательную (DNS, etc)
Поднимаем ВМ для кластера
!!! info "Я делаю всё от рута"
-
apt update && apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils
- устанавливаем гипервизор -
Создаём ВМ
virt-install \
--name k8s-master \
--ram 4096 \
--vcpus 3 \
--disk path=/var/lib/libvirt/images/k8s-master.qcow2,size=20 \
--os-variant ubuntu24.04 \
--network network=default \
--graphics vnc,listen=127.0.0.1 \
--cdrom /var/lib/libvirt/images/ubuntu-24.04.2-live-server-amd64.iso \
--noautoconsole
virsh vncdisplay k8s-master
- выводит VNC-дисплей, к которому привязан указанная гостевая ОС, если запущена
Пример вывода
127.0.0.1:0
Значит порт: 5900 + 0
remote-viewer vnc://localhost:5900
- конфигурируем, устанвливаем ВМvirsh list --all
- список запущенных ВМvirsh start k8s-master
- запуск созданонй ВМvirsh domifaddr k8s-master
- узнаём адрес ВМssh ilyamak04@192.168.122.157
- ну и подключаемся по ssh
Аналогично поднимаем 2 воркер ноды, и 1 вспомогательную, не забываем менять выделяемые ресурсы для ВМ
??? info "Дополнительные команды для управления ВМ"
- virsh shutdown <vm-name>
- штатное выключение ВМ
- virsh destroy <vm-name>
- жёсткое выключение, например, если ВМ зависла, НЕ УДАЛЯЕТ ВМ
- virsh list --all
- показать список всех виртуальных машин (включая выключенные)
- virsh start <vm-name>
- запустить виртуальную машину
- virsh undefine <vm-name>
- удалить ВМ из libvirt (не удаляет диск в /var/lib/libvirt/images/)
- virsh domifaddr <vm-name>
- показать IP-адрес ВМ (если доступен)
- virsh dumpxml <vm-name>
- вывести XML-конфигурацию ВМ
- virsh console <vm-name>
- подключиться к консоли ВМ (если настроен serial-порт)
- virsh domstate <vm-name>
- показать текущее состояние ВМ
- virsh autostart <vm-name>
- включить автозапуск ВМ при старте хоста
- virsh autostart --disable <vm-name>
- отключить автозапуск ВМ
- virsh net-list
- список виртуальных сетей libvirt
- virsh net-dumpxml default
- показать XML-конфигурацию сети default
- virsh dumpxml <vm-name>
- посмотреть XML-конфиг ВМ
- virsh net-edit default
- отредактировать настройки сети (например, static DHCP)
- Клонировать ВМ
bash # hostname на клонированной вм нужно менять вручную virt-clone \ --original k8s-worker1 \ --name k8s-worker2 \ --file /var/lib/libvirt/images/k8s-worker2.qcow2
Подготовка ВМ
- Откючаем
swap
, k8s требует отключенный swap для корректной работы планировщика
swapoff -a
!!! warn "Не забыть убрать запись из /etc/fstab"
Kubernetes использует cgroups для управления CPU и памятью контейнеров. Если включен swap, ядро может игнорировать лимит памяти, потому что будет сбрасывать часть данных в swap. Это нарушает работу OOM (Out Of Memory) killer и других механизмов kubelet'а. Когда swap включён, kubelet может не "увидеть", что контейнер превысил лимит памяти. Kubelet считает, что вся доступная память — это только RAM.
- Включаем модули ядра для корректной сетевой работы подов
tee /etc/modules-load.d/k8s.conf <<EOF
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
- Для корректной маршрутизации сетевого трафика
tee /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
# перечитываем конфигурации, применяем
sysctl --system
- Время на узлах должно быть синхронизировано, чтобы избежать проблем с сертификатами или ещё чего-нибудь
apt install -y chrony
- Проверить что ssh-сервис запущен
systemctl enable --now ssh
-
Фаервол для простоты настройки можно отключить, но выставлять весь кластер в интернет очевидно плохая идея
-
Добавим репозиторий docker для установки containerd, Kubernetes не запускает контейнеры напрямую, он использует Container Runtime Interface (CRI), который реализует containerd
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y containerd.io
- Kubernetes требует, чтобы containerd использовал systemd как управляющий механизм cgroups, т.е. структуру контроля ресурсов (CPU, память и т.п.)
containerd config default | tee /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd
- Добавим репозиторий k8s, установим необходимые компоненты k8s
# Добавить GPG-ключ
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# Добавить репозиторий Kubernetes
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Обновить список пакетов
apt update
# Установить kubeadm, kubelet, kubectl
apt install -y kubelet kubeadm kubectl
# Заблокировать от автоматического обновления
apt-mark hold kubelet kubeadm kubectl
###
# Проверка
###
kubeadm version
kubelet --version
kubectl version --client
- Установим
crictl
для взаимодействия сcontainerd
(удобно для отладки)
VERSION="v1.30.0"
curl -LO https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz
sudo tar -C /usr/local/bin -xzf crictl-$VERSION-linux-amd64.tar.gz
rm crictl-$VERSION-linux-amd64.tar.gz
cat <<EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: "unix:///run/containerd/containerd.sock"
timeout: 0
debug: false
pull-image-on-create: false
disable-pull-on-run: false
EOF
- Добавим алиас для команды
kubectl
echo "alias k='kubectl'" >> ~/.bashrc
source ~/.bashrc
- Базовые команды
crictl info # информация о рантайме
crictl ps -a # список всех контейнеров
crictl images # список всех образов
crictl pods # список подов
crictl logs <container_id> # логи контейнера
- Автодополнение для
kubectl
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
DNS-сервер
Данный DNS-сервре настраивается для коммуникации между нодами (серверами), для организации резолва имен между сущностями кубера, кубер использует свой ДНС (CoreDNS)
- Установка BIND9
apt update
apt install -y bind9 bind9utils bind9-doc
- vi /etc/bind/named.conf.options
//
// ------------------------------------------------------------
// Глобальные параметры BIND 9
// ------------------------------------------------------------
options {
// Где BIND хранит кэш и служебные файлы
directory "/var/cache/bind";
// Разрешаем рекурсивные запросы
recursion yes;
// Кому разрешена рекурсия. В лаборатории можно any,
// в проде указать свою подсеть.
allow-recursion { any; };
// На каких интерфейсах слушать DNS-запросы
listen-on { 192.168.122.66; 127.0.0.1; };
listen-on-v6 { none; }; // IPv6 не используем
// Куда пересылать внешние запросы
forwarders { 8.8.8.8; 1.1.1.1; };
// Включаем автоматическую проверку DNSSEC-подписей
dnssec-validation auto;
};
- vi /etc/bind/named.conf.local
// ------------------------------------------------------------
// Авторитетные зоны
// ------------------------------------------------------------
// Прямая зона lab.local (имя → IP)
zone "lab.local" IN {
type master; // главный (= авторитет)
file "/etc/bind/zones/db.lab.local";
allow-update { none; }; // динамических правок не ждём
};
// Обратная зона 122.168.192.in-addr.arpa (IP → имя)
zone "122.168.192.in-addr.arpa" IN {
type master;
file "/etc/bind/zones/db.192.168.122";
allow-update { none; };
};
-
mkdir -p /etc/bind/zones
-
vi /etc/bind/zones/db.lab.local
$TTL 86400 ; время жизни записей по умолчанию (24 ч)
@ IN SOA k8s-infra.lab.local. admin.lab.local. (
2025062401 ; Serial (YYYYMMDDnn) — увеличивайте при каждой правке
1h ; Refresh — как часто slave (если бы был) проверяет SOA
15m ; Retry — если refresh не удался
7d ; Expire ; после этого зона считается устаревшей
1h ) ; Negative TTL — кэш «NXDOMAIN»
; — NS-запись: кто авторитетен для зоны
IN NS k8s-infra.lab.local.
; ---------- A-записи ----------
k8s-master IN A 192.168.122.157 ; control-plane
k8s-worker1 IN A 192.168.122.141 ; worker-1
k8s-worker2 IN A 192.168.122.192 ; worker-2
k8s-infra IN A 192.168.122.66 ; infra + DNS
- vi /etc/bind/zones/db.192.168.122
$TTL 3600
@ IN SOA k8s-infra.lab.local. admin.lab.local. (
2025062401
1h
15m
7d
1h )
IN NS k8s-infra.lab.local.
; ---------- PTR-записи (последний октет → FQDN) ----------
157 IN PTR k8s-master.lab.local.
141 IN PTR k8s-worker1.lab.local.
192 IN PTR k8s-worker2.lab.local.
66 IN PTR k8s-infra.lab.local.
- Проверка синтаксиса
# Проверяем синтаксис конфигурации
named-checkconf
# Проверяем каждую зону
named-checkzone lab.local /etc/bind/zones/db.lab.local
named-checkzone 122.168.192.in-addr.arpa /etc/bind/zones/db.192.168.122
- Перезапуск сервиса
systemctl restart named
systemctl enable named
- Добавить на каждой ноде в конфиг netplan
nameservers:
search: [lab.local]
addresses: [192.168.122.66, 8.8.8.8]
- Применить
netplan apply
# или, если нужен лог
sudo netplan apply --debug
- Проверка работы DNS
dig +short k8s-worker2.lab.local
# prt-запись
dig -x 192.168.122.192 +short
Настройка NFS
Настройка NFS-сервера
- Устанавливаем сервер
apt update
apt install -y nfs-kernel-server
- Создаём каталог который будет экспортироваться
mkdir -p /srv/nfs/k8s
# пользователь без привилегий
chown nobody:nogroup /srv/nfs/k8s
chmod 0770 /srv/nfs/k8s
vi /etc/exports
/srv/nfs/k8s 192.168.122.0/24(rw,sync,no_subtree_check,root_squash,fsid=0)
-
rw
- разрешает чтение и запись -
sync
- операции записи выполняются немедленно (безопасно) -
no_subtree_check
- ускоряет работу при экспорте подкаталогов -
root_squash
- если клиент заходит как root, он будет понижен до "nobody" (безопаснее) -
fsid=0
- нужен для корня экспортов в NFSv4 (в NFSv4 экспортируется только один корень) -
192.168.122.0/8
- сеть, которой разрешён доступ -
Экспортировать каталог
exportfs -rav
# проверить
exportfs -v
Настройка NFS-клиента
apt install -y nfs-common
- Проверить доступность сервера
# показывает доступные каталоги
showmount -e 192.168.122.157
- Монтируем расшаренный каталог на клиент
mount -t nfs4 192.168.122.157:/ /mnt
- Добавить в
/etc/fstab
, для автомонтирования при перезагрузке
echo "192.168.122.157:/ /mnt nfs4 defaults,_netdev 0 0" | tee -a /etc/fstab
Разворачиваем кластер
- Версии api, которые поддерживает установленная версия
kubeadm
kubeadm config print init-defaults | grep apiVersion
vi /etc/kubernetes/kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
ttl: 24h0m0s
usages:
- signing
- authentication
localAPIEndpoint:
advertiseAddress: 192.168.122.157
bindPort: 6443
nodeRegistration:
criSocket: "unix:///var/run/containerd/containerd.sock"
imagePullPolicy: IfNotPresent
name: k8s-master.lab.local
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
certificatesDir: /etc/kubernetes/pki
clusterName: cluster.local
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: "registry.k8s.io"
apiServer:
timeoutForControlPlane: 4m0s
extraArgs:
authorization-mode: Node,RBAC
bind-address: 0.0.0.0
service-cluster-ip-range: "10.233.0.0/18"
service-node-port-range: 30000-32767
kubernetesVersion: "1.30.14"
controlPlaneEndpoint: 192.168.122.157:6443
networking:
dnsDomain: cluster.local
podSubnet: "10.233.64.0/18"
serviceSubnet: "10.233.0.0/18"
scheduler: {}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: 0.0.0.0
clusterCIDR: "10.233.64.0/18"
ipvs:
strictARP: True
mode: ipvs
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
clusterDNS:
- 169.254.25.10
systemReserved:
memory: 512Mi
cpu: 500m
ephemeral-storage: 2Gi
# Default: "10Mi"
containerLogMaxSize: 10Mi
# Default: 5
containerLogMaxFiles: 3
- Инициализация первой ноды
kubeadm init --config /etc/kubernetes/kubeadm-config.yaml
- Если приложение долго не завершает свою работу, значит что-то пошло не так. Необходимо отменить все действия и запустить его ещё раз, но с большим уровнем отладки.
kubeadm reset
kubeadm init --config /etc/kubernetes/kubeadm-config.yaml -v5
- Смотрим ip для доступа к кластеру
kubectl cluster-info
- Установим драйвер сети (CNI Plugin), Cilium CNI с поддержкой multicast для разворота нод ROS2
CLI_VER=0.16.7
curl -L --remote-name-all \
https://github.com/cilium/cilium-cli/releases/download/v${CLI_VER}/cilium-linux-amd64.tar.gz
tar xzvf cilium-linux-amd64.tar.gz
sudo mv cilium /usr/local/bin/
cilium version
cilium install \
--version 1.17.5 \
--set ipam.mode=kubernetes \
--set tunnel=vxlan \
--set enable-multicast=true
# ждём OK
cilium status --wait
- Смотрим ноды в кластере
kubectl get nodes
- Смотрим поды на ноде
kubectl get pods -A
- Регистрируем воркер ноды в кластере (представленная команда выводится в стандартный вывод после инициализации первой контрол ноды)
kubeadm join 192.168.122.157:6443 --token xp77tx.kil97vo6tlfdqqr4 \
--discovery-token-ca-cert-hash sha256:2bec2613d6f016eee60d9e7af7bf98ef44753cbd26f11cce8d71df694bcebddf
Общее
kubectl explain <name>
- дока (kubectl explain pod.spec
)kubectl edit deployment deployment_name
(kubectl edit) - изменение манифеста на лету, нигде не версионируется (использовать только для дебага на тесте)kubectl config get-contexts
- информация о текущем контексте
POD
k8s - кластерная ОС
POD - одно запущенное приложение в кластере k8s, минимальная абстракция k8s (внутри пода может быть несколько контейнеров, и в поде всегда минимум 2 контейнера: приложение, сетевой неймспейс) (контейнер внутри пода, как отдельный процесс в ОС)
-
kubectl create -f pod.yml
- создать под согласно конфигу из файла -
kubectl get pod
- список подов -
kubectl describe pod <pod_name>
- описание пода -
kubectl describe pod <pod_name> -n <namespace> | less
- описание пода в нс -
kebectl delete pod <pod_name>
илиkubectl delete -f pod.yml
- удаление пода -
k -n <ns_name> delete pod <pod_name>
- удалить под -
k get pod <pod_name> -n <ns_name> -o yaml | less
- посмотреть полный манифест пода -
kubectl -n <ns_name> logs <pod_name>
- логи пода -
kubectl -n <ns_name> logs <pod_name> -c <container_name>
- логи последнего контейнера
!!! info "Разница между create
и apply
"
create
создаёт ресурс только если его ещё нет, если ресурс уже существует — выдаёт ошибку
`apply` cоздаёт ресурс, если его нет,или обновляет, если он уже существует, поддерживает историю изменений, идемпотентен
# пример описания пода
---
apiVersion: v1
kind: Pod # тип сущности
metadata:
name: mypod # в рамках одного пространства имён имя уникально
spec: # описание объекта
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Ресурсы (QoS)
Приоритет Pod'ов при выделении ресурсов и при давлении на узел
QoS не управляется напрямую, а автоматически присваивается каждому Pod'у в зависимости от указанных ресурсов (requests и limits) в манифесте.
куб определяет 3 уровня QoS
-
Guaranteed
- requests == limits для всех контейнеров в Pod'е, высший приоритет, удаляется в последнюю очередь -
Burstable
- задан requests, но не равно limits, или не для всех -
BestEffort
- не указано ничего (ни requests, ни limits), если ресурсов на ноде не хватает, такие поды убиваются в первую очередь -
Посмотреть QoS пода
kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'
Пробы
- Если проба УСПЕШНА:
Readiness Probe
- Под добавляется в эндпоинты Service. Теперь трафик с Load Balancer'а будет направляться на этот подLiveness Probe
- Ничего не происходит. Контейнер продолжает работать как обычно
- Если проба НЕУДАЧНА:
Readiness Probe
- Под удаляется из эндпоинтов Service. Трафик на этот под прекращается. Контейнер НЕ перезапускаетсяLiveness Probe
- Контейнер убивается и перезапускается (согласно политике restartPolicy).
Best practice для описания пода
Должны быть:
- Метки
- Задан образ контейнера
- Ресурсы контейнера(ов) ограничены
- Пробы
Namespace
-
Namespace используются для изоляции групп ресурсов в пределах одного кластера kubernetes. Имена ресурсов должны быть уникальными в пределах namespace.
-
kubectl get ns
- вывести неймспейсы -
kubectl create ns <name>
- создать нс -
kubectl delete ns <name>
- удалить нс -
k get ns <name>
-
kubectl config set-context --current --namespace=<имя-namespace>
- сменить ns чтобы писать команды без флага-n
-
нс
kube-system
располагаются приложения control-plane -
нс
kube-public
доступен для чтения всем клиентам -
kubectl config get-contexts
- узнать в каком нс находишься
Repcicaset
Задача Replicaset - обеспечить работу заданного количества реплик Pod'ов, описываемых Deployment
-
kubectl get rs
- вывести репликасеты -
kubectl delete rs <name>-rs
- удалить rs -
k delete replicaset --all
- удалить все rs в ns -
k describe replicaset <name>
-
k scale --replicas 3 replicaset <name>
- заскейлисть репликасет -
k set image replicaset <name> <container_name>=<new_image_name>
- обновить образ контейнера (но нужно пересоздать поды, replicaset не решает проблему обновления приложения, rs просто поддерживает заданное количество подов, задачу обновления решает абстрацкия deployment) -
Пример конфигурации
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: nginx
Deployment
Абстракция, которая управляте replicasetами и podами
Deployment предназначен для stateless приложений
-
создаёт и управляет ReplicaSet'ом
-
Rolling updates — обновляет приложения без простоя
-
Откат (rollback) к предыдущей версии
-
Масштабирование (scale up/down)
-
Самовосстановление (если Pod удалён или упал)
-
Пример
deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: nginx:1.25
ports:
- containerPort: 80
-
spec.selector
- определяет за какие поды отвечает Deployment -
kubectl rollout restart
- перезапуск Deployment
Обновление
-
создаваёт новый ReplicaSet с новой версией образа
-
постепенно увеличивает количество новых Pod'ов и уменьшает старые
-
следит, чтобы всегда было достаточно доступных реплик
-
Пример обновления образа
kubectl set image deployment/myapp myapp-container=nginx:1.26
- Откат на предыдущую версию deployment (на ту версию, которая была применена до последнего успешного обновления)
kubectl rollout undo deployment myapp
- Проверка состояния
kubectl rollout status deployment myapp
kubectl get deployment
kubectl describe deployment myapp
- При каждом изменении (kubectl apply, set image, scale, и т.п.) создаётся новая ревизия, по умолчанию куб хранит 10 ревизий
# посмотреть историю ревизий
kubectl rollout history deployment myapp
# откатиться к ревизии
kubectl rollout undo deployment myapp --to-revision=3
Service
Сущность, которая предоставляет постоянную сетевую точку доступа к группе Pod'ов
kubectl get endpoints my-service
- оказывает IP-адреса Pod'ов, к которым направляет трафик Service my-servicek get EndpointSlice
Service headless
Не обеспечивает балансировку трафика к подам (нет ClusterIP), позволяет обращаться к поду по его доменному имени, используется с Statefulset, т.к. поды "статичны"
Statefulset
Крнтроллер, похожий на Deployment гарантирует уникальность имени пода, порядок запуска, рестарта, удаления пода, постоянство ip-адреса, томов
Тома
emptyDir
Обычно используется для:
- размещения кэша файлов
- данные которые необходимо хранить при сбоях в работе контейнера
- обмена файлами между несколькими контейнерами в поде
!!! info "При удалении пода (например, при перезапуске, обновлении, сбое узла и т.д.) — данные из emptyDir удаляются безвозвратно"
- Кусочек конфига
volumeMounts:
- name: empty-volume
mountPath: /empty
volumes:
- name: empty-volume
emptyDir: {}
hostPath
!!! warning "Изпользовать hostPath небезопасно!!!" Контейнер получает прямой доступ к файловой системе хоста
- Пример
volumes:
- name: host-logs
hostPath:
path: /var/log/nginx
type: Directory
- Kubernetes может проверять, существует ли путь, и что он из себя представляет
type: Directory # Должен быть каталог
type: DirectoryOrCreate # Создает каталог, если его нет
type: File # Должен быть файл
type: FileOrCreate # Создает файл, если его нет
type: Socket # Должен быть сокет
type: CharDevice # Символьное устройство
type: BlockDevice # Блочное устройство
ConfigMap
ConfigMap - сущность, предназначенная для хранения нечувствительных данных конфигурации в виде пар ключ: значение, позволяет отделить конфигурацию от кода и применять её к контейнерам без необходимости пересборки образа.
-
k get cm
-
Пример
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: my-config
- Пример
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
APP_MODE: production
LOG_LEVEL: debug
- Передача переменных окружения из ConfigMap в Pod
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "env"]
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: my-config
key: APP_MODE
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: my-config
key: LOG_LEVEL
- Если переменная уже определена через env, она не будет перезаписана envFrom.
- Можно использовать сразу несколько envFrom (например, ConfigMap и Secret).
- Если переменная в ConfigMap содержит недопустимые символы (например, точки или тире), она не будет импортирована как env.
Secret
Секрет - это объект, который содержит небольшое количетсво конфиденциальных даннх
-
k get secret
-
k get secret <name> -o yaml
-
Типы секрета
generic
(Opaque) - пароли/токены для приложенийdocker-registry
- данные авторизации в docker registrytls
- TLS сертификаты
-
Пример
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # base64 от 'admin'
password: MWYyZDFlMmU2N2Rm # base64 от '1f2d1e2e67df'
- Для удобства админитратора есть поле
strigData
, когда манифест примениться содержимое будет закодировано в base64
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
stringData:
username: admin
password: s3cr3t
- Так подключается в манифест
env:
- name: username
valueFrom:
secretKeyRef:
name: my-secret
key: username
!!! warning ""
При добавлении новых секретов, необходимо помнить про правила мерджа манифестов, аннотацию kubectl.kubernetes.io/last-applied-configuration
- Добавление секретов в контейнер в виде тома
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: app
image: alpine
command: ["/bin/sh", "-c", "cat /etc/secret/* && sleep 3600"]
volumeMounts:
- name: secret-volume
mountPath: /etc/secret
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-secret
# внутри контейнера
cat /etc/secret/username # выведет: user
cat /etc/secret/password # выведет: password
downwardAPI
downwardAPI
позволяет передать метаданные Pod'а
(например, имя пода, namespace, labels, annotations, ресурсы) в контейнер через переменные окружения или файлы.
- Пример (как том (файлы))
volumeMounts:
- mountPath: "/etc/pod-info"
name: pod-info
readOnly: true
volumes:
- name: pod-info
downwardAPI:
items:
- path: limit-cpu-millicores
resourceFieldRef:
containerName: openresty
resource: limits.cpu
divisor: 1m
- path: limit-memory-kibibytes
resourceFieldRef:
containerName: openresty
resource: limits.memory
divisor: 1Ki
- path: labels
fieldRef:
fieldPath: metadata.labels
- Пример (как переменные окружения)
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_CPU_LIMIT
valueFrom:
resourceFieldRef:
resource: limits.cpu
projected
projected
- это том, который объединяет несколько источников данных в одну директорию
-
secret
-
configMap
-
downwardAPI
-
serviceAccountToken
-
Пример
volumeMounts:
- mountPath: "/etc/pod-data"
name: all-values
readOnly: true
volumes:
- name: all-values
projected:
sources:
- downwardAPI:
items:
- path: limits/cpu-millicore
resourceFieldRef:
containerName: openresty
resource: limits.cpu
divisor: 1m
- path: limits/memory-kibibytes
resourceFieldRef:
containerName: openresty
resource: limits.memory
divisor: 1Ki
- path: labels
fieldRef:
fieldPath: metadata.labels
- secret:
name: user-password-secret
items:
- key: user
path: secret/user
- key: password
path: secret/password
- configMap:
name: example-txt
items:
- key: example.txt
path: configs/example.txt
- key: config.yaml
path: configs/config.yaml
PV, PVC
k get pv
PersistentVolume (PV) - это объект, который предоставляет долговременное хранилище для Pod'ов, независимое от их жизненного цикла, под подключается к хранилищу не напрямую, а через PersistentVolumeClaim (PVC)
!!! info "PVC работает только внутри одного namespace, а PV - кластерный объект"
-
Архитектура
- PersistentVolume (PV) - описывает конкретный ресурс хранилища (например, NFS, iSCSI, Ceph, диск в облаке, локальный диск)
- PersistentVolumeClaim (PVC) - это запрос от Pod-а: «Хочу хранилище с такими-то параметрами»
- Kubernetes связывает PVC с подходящим PV (если типы и параметры совместимы)
-
accessModes
(способы доступа)ReadWriteOnce
(RWO): один Pod может писать (самый частый случай)ReadOnlyMany
(ROX): много Pod-ов читаютReadWriteMany
(RWX): несколько Pod-ов могут читать и писать (например, NFS)
-
persistentVolumeReclaimPolicy
— что делать после удаления PVCRetain
- PV остаётся, данные сохраняются (нужно вручную очистить/перепривязать)Delete
- PV и данные удаляются автоматическиRecycle
- устаревший способ (удаляет файлы, оставляет PV)
-
(Связывание PVC c PV) Куб находит подходящий PV по:
storage
(размер — должен быть ≥ запроса)accessModes
(PV должен удовлетворять запрошенному)StorageClass
(если указан)
!!! info "Если нет подходящего PV - PVC останется в состоянии Pending"
DaemonSet
Для запуска пода на каждой ноде кластера, если нет ограничений (Taints и Tolerations)
Манифест как у Deployment
, кроме параметра kind
, нет параметра resplicas
k get ds
Taint
Taint - это свойство ноды, которое действует как ограничение. Взаимодействует с планировщиком.
taint состоит из трёх частей: key=[value]:Effect
key
- ключ taint (например, node-role.kubernetes.io/control-plane)value
- значение taint. Не обязателен к определению. Если не указано, то любое значение будет считаться совпадением.Effect
- действие.NoSchedule
- запрещает планирование под на ноде. Поды, запущенные до применения taint не удаляются.NoExecute
- запрещает планирование под на ноде. Поды, запущенные до применения taint будут удалены с ноды.PreferNoSchedule
- это «предпочтительная» или «мягкая» версия NoSchedule. Планировщик будет пытаться не размещать на узле поды, но это не гарантировано.
Что бы игнорировать taint node-role.kubernetes.io/control-plane:NoSchedule
для подов DaemonSet необходимо добавить в манифест толерантность к конкретному типу taint в спецификации пода, например:
spec:
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
Если мы не указываем значение ключа (value), operator
должен быть установлен в Exists
.
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
- посмотреть taint'ы на нодах- Добавить taint
kubectl taint nodes <node_name> key=[value]:Effect
kubectl taint nodes wr2.kryukov.local test-taint=:NoExecute
- Чтобы снять taint, добавить в конце команды
-
kubectl taint nodes wr2.kryukov.local test-taint=:NoExecute-
NodeSelector
Если необходимо разместить поды на строго определённых нодах кластера, в этом случае можно использовать nodeselector
. В качестве параметра, используемого для отбора нод, можно указать метки (labels), установленные на нодах.
kubectl get nodes --show-labels
- метки на нодахkubectl label nodes <node_name> test=test
- добавить метку на нодуkubectl label nodes <node_name> test=test-
- снять метку с ноды
spec:
nodeSelector:
special: ds-only
Toleration
Toleration не гарантирует, что под будет размещен на помеченном узле. Он лишь разрешает это. Решение все равно принимает планировщик на основе других факторов (достаточно ли ресурсов и т.д.).
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: my-app
image: nvidia/cuda:11.0-base
resources:
limits:
nvidia.com/gpu: 1
# Ключевая секция:
tolerations:
- key: "gpu" # Должен совпадать с key taint'а
operator: "Equal" # Оператор сравнения. "Equal" или "Exists"
value: "true" # Должен совпадать с value taint'а (если operator=Equal)
effect: "NoSchedule" # Должен совпадать с effect taint'а
operator: "Equal" # точное совпадение по value
operator: "Exists" # Toleration сработает для любого taint'а с указанными key и effect. Значение value в этом случае указывать не нужно
Job
Deployment, например, предназначен для запуска долгоживущих процессов (веб-сервер), которые должны работать постоянно (running), их цель быть всегда доступными
Job
предназначен для запуска одноразовых задач, которые должны выполниться и завершиться успешно (Succeeded), их цель - выполнить работу и прекратить существование
apiVersion: batch/v1
kind: Job
metadata:
name: example-job
spec:
# Шаблон пода, который будет выполнять работу
template:
spec:
containers:
- name: worker
image: busybox
command: ["echo", "Hello, Kubernetes Job!"]
restartPolicy: Never # или OnFailure. Для Job НЕ допускается Always.
# Количество успешных завершений, необходимое для успеха всей Job
completions: 1 # (по умолчанию 1)
# Количество Pod'ов, которые могут работать параллельно для достижения цели
parallelism: 1 # (по умолчанию 1)
# Политика перезапуска подов при failure
backoffLimit: 6 # (по умолчанию 6) Макс. количество попыток перезапуска пода
# Таймаут для Job в секундах. Если Job выполняется дольше - она будет убита.
activeDeadlineSeconds: 3600
Как работает Job?
-
Вы создаете объект Job (например, через kubectl apply -f job.yaml).
-
Job-контроллер видит новую задачу и создает один или несколько Pod'ов на основе template.
-
Контроллер следит за состоянием Pod'ов.
- Успех: Если под завершается с кодом выхода 0, это считается успешным завершением (Succeeded).
- Неудача: Если под завершается с ненулевым кодом выхода, он считается неудачным (Failed).
-
Логика перезапуска:
- Если restartPolicy: OnFailure, kubelet перезапустит контейнер внутри того же пода.
- Если restartPolicy: Never, Job-контроллер создаст новый под.
-
Job продолжает создавать новые поды (с экспоненциальной задержкой, чтобы не заспамить кластер), пока не будет достигнуто либо:
-
Успешное завершение количества подов, указанного в completions.
-
Превышено количество попыток backoffLimit — тогда вся Job помечается как Failed.
-
kubectl apply -f job.yaml
- создать job из файла -
kubectl get jobs
- список джобов -
kubectl describe job <job-name>
- свойства джоба -
kubectl logs <pod-name>
- логи конкретного пода -
kubectl delete job <job-name>
- удалить Job (автоматически удалит и все его Pod'ы) -
Пример манифеста Job
apiVersion: batch/v1
kind: Job
metadata:
name: pi-calculation
spec:
backoffLimit: 4
template:
spec:
containers:
- name: pi
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
CronJob
CronJob — это контроллер, который управляет Job'ами, он создает объекты Job по расписанию, используя синтаксис cron
apiVersion: batch/v1
kind: CronJob
metadata:
name: example-cronjob
spec:
# Самое главное: расписание в формате cron
schedule: "*/5 * * * *"
# Шаблон для создания Job
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
command: ["echo", "Hello from CronJob!"]
restartPolicy: OnFailure
# Сколько последних успешных Job хранить в истории
successfulJobsHistoryLimit: 3 # (по умолчанию 3)
# Сколько последних неудачных Job хранить в истории
failedJobsHistoryLimit: 1 # (по умолчанию 1)
# Что делать, если новый запуск по расписанию наступает, а предыдущая Job все еще работает
concurrencyPolicy: Allow # Разрешить параллельные запуски. Другие значения: "Forbid" (запретить), "Replace" (заменить текущую).
# Приостановить работу CronJob (не создавать новые Job), не удаляя уже работающие Job
suspend: false # по умолчанию
-
kubectl apply -f cronjob.yaml
- создать/обновить CronJob -
kubectl get cronjobs
- посмотреть CronJob -
kubectl get cj
- посмотреть CronJob -
kubectl get jobs
- посмотреть Job, созданные CronJob -
kubectl patch cronjob <cronjob-name> -p '{"spec":{"suspend":true}}'
- приостановить CronJob -
kubectl patch cronjob <cronjob-name> -p '{"spec":{"suspend":false}}'
- возобновить CronJob -
kubectl delete cronjob <cronjob-name>
- удалить CronJob (удаляет сам CronJob, но НЕ удаляет созданные им Job) -
kubectl create job --from=cronjob/<cronjob-name> <manual-job-name>
- принудительно запустить CronJob немедленно, не дожидаясь расписания -
Пример манифеста CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *" # Каждый день в 2:00 ночи
successfulJobsHistoryLimit: 2
jobTemplate:
spec:
template:
spec:
containers:
- name: report-generator
image: python:3.9
command: ["python", "/app/generate_daily_report.py"]
restartPolicy: OnFailure
Affinity
Основные вижы Affinity
Node Affinity
- привязка пода к определенным характеристикам нодыInter-Pod Affinity/Anti-Affinity
- привязка пода к другим подам или отталкивание от них
Node Affinity
requiredDuringSchedulingIgnoredDuringExecution
- Жесткое правило ("Должен"). Под обязательно будет размещен на узле, удовлетворяющем условию. Если подходящего узла нет, под останется в статусе PendingpreferredDuringSchedulingIgnoredDuringExecution
- Предпочтение ("Желательно"). Планировщик попытается найти узел, удовлетворяющий условию. Если не найдет - разместит под на любом другом подходящем узле
Часть
IgnoredDuringExecution
означает, что если метки на узле изменятся после того, как под уже был размещен, это не приведет к выселению пода
Операторы (operator) в matchExpressions
:
-
In
- значение метки узла находится в указанном списке -
NotIn
- значение метки узла НЕ находится в указанном списке -
Exists
- узел имеет метку с указанным ключом (значение не важно) -
DoesNotExist
- у узла НЕТ метки с указанным ключом -
Gt (Greater than)
,Lt (Less than)
- для числовых значений -
Пример манифеста
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app
image: my-app:latest
affinity:
nodeAffinity:
# ЖЕСТКОЕ правило: под должен быть размещен на узле с меткой 'disktype=ssd'
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
# ПРЕДПОЧТЕНИЕ: и желательно, чтобы это был быстрый NVMe SSD
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 # Относительный вес (важность) среди других предпочтений (1-100)
preference:
matchExpressions:
- key: ssd-type
operator: In
values:
- nvme
Inter-Pod Affinity/Anti-Affinity
Позволяет указывать правила размещения пода относительно других подов.
Pod Affinity
- "Размести этот под рядом/на том же узле, что и эти другие поды"Pod Anti-Affinity
- "Размести этот под подальше/на другом узле, от этих других подов"
Ключевые понятия:
-
topologyKey
- указывает домен, в котором применяется правило, это метка узла. Может использоватьсяkubernetes.io/hostname
(правило применяется в пределах одного узла) илиtopology.kubernetes.io/zone
(правило применяется в пределах одной зоны доступности) -
ПРИМЕР. Разместить реплики одного приложения на разных узлах для повышения отказоустойчивости.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web-app
spec:
replicas: 3
selector:
matchLabels:
app: my-web-app
template:
metadata:
labels:
app: my-web-app # По этой метке будем искать другие поды
spec:
containers:
- name: web
image: nginx:latest
affinity:
podAntiAffinity:
# ЖЕСТКОЕ правило: не размещать два пода с app=my-web-app на одном узле
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- my-web-app
topologyKey: kubernetes.io/hostname # Где применять Affinity
!!! tip "Affinity-правила могут быть сложными, полезно комментировать их в манифестах"
В итоге:
!!! info ""
- Taint
- это свойство ноды, которое действует как ограничение,сообщает планировщику кубера (kube-scheduler), что на этом узле запрещено пускать любые поды, которые не имеют Toleration
к данной Taint
- Toleration
- это свойство пода, которое дает ему право быть запланированным на узле с определенным Taint
, несмотря на ограничение
- Affinity
- это набор правил для пода, которые позволяют ему притягиваться к узлам или другим подам с определенными характеристиками
Pod Topology Spread Constraints
Для равномерного распределения подов между зонами
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: *name
app.kubernetes.io/instance: *instance
app.kubernetes.io/version: *version
nodeAffinityPolicy: Ignore
nodeTaintsPolicy: Honor
Параметры topologySpreadConstraints
maxSkew
- максимальная разница количества подов между доменами топологииtopologyKey
- метка на ноде кластера, которая используется для определения доменов топологииwhenUnsatisfiable
- что делать с подом, если он не соответствует ограничениюDoNotSchedule
- (по умолчанию) запрещает планировщику запускать под на нодеScheduleAnyway
- разрешает запускать под на ноде
labelSelector
- определяет список меток подов, попадающих под это правилоnodeAffinityPolicy
- определят будут ли учитыватьсяnodeAffinity
/nodeSelector
пода при расчёте неравномерности распределения подаHonor
- (по умолчанию) в расчёт включаются только ноды, соответствующиеnodeAffinity
/nodeSelector
Ignore
- в расчёты включены все ноды
nodeTaintsPolicy
- аналогичноnodeAffinityPolicy
, только учитываютсяTaints
Honor
- Включаются ноды без установленныхTaints
, а так же ноды для которых у пода естьToleration
Ignore
- (по умолчанию) в расчёты включены все ноды.
Разное
Labels — структурированные данные для логики Kubernetes
- для селекторов (
matchLabels
,labelSelector
) - для группировки объектов (например, связать
Pod
сReplicaSet
,Service
,Deployment
) - участвуют в логике работы контроллеров, планировщика (
scheduler
), сервисов и т.д. - нужны для фильтрации:
kubectl get pods -l app=nginx
Annotations — это метаданные, которые:
- Используются для хранения произвольной информации
- не участвуют в селекции
- используются вспомогательными компонентами:
- Ingress-контроллеры
- cert-manager
- kubectl
- Helm
- CSI (storage drivers)
- операторы
- аннотации часто используются для внутренней логики, дополнительных настроек, или даже инструкций для других систем, в том числе приложений внутри подов
kubectl describe node <node-name>
- инфо о ноде кубаkubectl get pods -o wide
- расширенный вывод о сущностиkubectl events
- события в кластере кубера