ilyamak04 e3b6e54d7b
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 16s
fix format
2025-09-11 15:31:27 +03:00

1522 lines
59 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### YAML Синтаксис
#### Основные сущности
- Скалярные значения
```yml
string_value: hello
number_value: 42
float_value: 3.14
boolean_true: true
boolean_false: false
```
- Списки (массивы)
```yml
servers:
- web01
- web02
- db01
```
или
```yml
servers: [web01, web02, db01]
```
- Словари
```yml
user:
name: "ivan"
age: 30
admin: true
```
или
```yml
user: {name: ivan, age: 30, admin: true}
```
- Многострочные строки
```yml
description: |
Это многострочный текст.
Он сохраняет переносы строк.
Полезно для документации.
command: >
Это тоже многострочный текст,
но переносы будут заменены пробелами.
```
- `|` - сохраняет всё как есть, включая `\n`
- `>` - склеивает строки в одну с пробелами
#### Расширенные возможности
- Ссылки и якори ($, *)
```yml
defaults: &default_settings
retries: 3
timeout: 30
server1:
<<: *default_settings
timeout: 10 # переопределено
server2:
<<: *default_settings
```
- Линтер
```yml
yamllint fine_name.yml
```
#### Разное
- Null
```yml
empty1: null
empty2: ~
empty3:
```
- Boolean
```yml
bool1: yes # интерпретируется как true
bool2: no # false
bool3: on # true
bool4: off # false
```
чтобы явно задать строку, необходимо использовать кавычки
```yml
literal_string: "yes" # не будет true
```
### Разворот кластера
Буду поднимать 1 мастер ноду, 2 воркер ноды, 1 вспомогательную (DNS, etc)
#### Поднимаем ВМ для кластера
!!! info "Я делаю всё от рута"
- `apt update && apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils` - устанавливаем гипервизор
- Создаём ВМ
```bash
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-дисплей, к которому привязан указанная гостевая ОС, если запущена
Пример вывода
```bash
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 для корректной работы планировщика
```bash
swapoff -a
```
!!! warn "Не забыть убрать запись из /etc/fstab"
Kubernetes использует cgroups для управления CPU и памятью контейнеров. Если включен swap, ядро может игнорировать лимит памяти, потому что будет сбрасывать часть данных в swap. Это нарушает работу OOM (Out Of Memory) killer и других механизмов kubelet'а. Когда swap включён, kubelet может не "увидеть", что контейнер превысил лимит памяти. Kubelet считает, что вся доступная память — это только RAM.
- Включаем модули ядра для корректной сетевой работы подов
```bash
tee /etc/modules-load.d/k8s.conf <<EOF
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
```
- Для корректной маршрутизации сетевого трафика
```bash
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
```
- Время на узлах должно быть синхронизировано, чтобы избежать проблем с сертификатами или ещё чего-нибудь
```bash
apt install -y chrony
```
- Проверить что ssh-сервис запущен
```bash
systemctl enable --now ssh
```
- Фаервол для простоты настройки можно отключить, но выставлять весь кластер в интернет очевидно плохая идея
- Добавим репозиторий docker для установки containerd, Kubernetes не запускает контейнеры напрямую, он использует Container Runtime Interface (CRI), который реализует containerd
```bash
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, память и т.п.)
```bash
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
```bash
# Добавить 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` (удобно для отладки)
```bash
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
```
```bash
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`
```bash
echo "alias k='kubectl'" >> ~/.bashrc
source ~/.bashrc
```
- Базовые команды
```bash
crictl info # информация о рантайме
crictl ps -a # список всех контейнеров
crictl images # список всех образов
crictl pods # список подов
crictl logs <container_id> # логи контейнера
```
- Автодополнение для `kubectl`
```bash
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
```
#### DNS-сервер
Данный DNS-сервре настраивается для коммуникации между нодами (серверами), для организации резолва имен между сущностями кубера, кубер использует свой ДНС (CoreDNS)
- Установка BIND9
```bash
apt update
apt install -y bind9 bind9utils bind9-doc
```
- vi /etc/bind/named.conf.options
```bash
//
// ------------------------------------------------------------
// Глобальные параметры 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
```bash
// ------------------------------------------------------------
// Авторитетные зоны
// ------------------------------------------------------------
// Прямая зона 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
```bash
$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
```bash
$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.
```
- Проверка синтаксиса
```bash
# Проверяем синтаксис конфигурации
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
```
- Перезапуск сервиса
```bash
systemctl restart named
systemctl enable named
```
- Добавить на каждой ноде в конфиг netplan
```yml
nameservers:
search: [lab.local]
addresses: [192.168.122.66, 8.8.8.8]
```
- Применить
```bash
netplan apply
# или, если нужен лог
sudo netplan apply --debug
```
- Проверка работы DNS
```bash
dig +short k8s-worker2.lab.local
# prt-запись
dig -x 192.168.122.192 +short
```
#### Настройка NFS
##### Настройка NFS-сервера
- Устанавливаем сервер
```bash
apt update
apt install -y nfs-kernel-server
```
- Создаём каталог который будет экспортироваться
```bash
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` - сеть, которой разрешён доступ
- Экспортировать каталог
```bash
exportfs -rav
# проверить
exportfs -v
```
##### Настройка NFS-клиента
```bash
apt install -y nfs-common
```
- Проверить доступность сервера
```bash
# показывает доступные каталоги
showmount -e 192.168.122.157
```
- Монтируем расшаренный каталог на клиент
```bash
mount -t nfs4 192.168.122.157:/ /mnt
```
- Добавить в `/etc/fstab`, для автомонтирования при перезагрузке
```bash
echo "192.168.122.157:/ /mnt nfs4 defaults,_netdev 0 0" | tee -a /etc/fstab
```
#### Разворачиваем кластер
- Версии api, которые поддерживает установленная версия `kubeadm`
```bash
kubeadm config print init-defaults | grep apiVersion
```
- `vi /etc/kubernetes/kubeadm-config.yaml`
```bash
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
```
- Инициализация первой ноды
```bash
kubeadm init --config /etc/kubernetes/kubeadm-config.yaml
```
- Если приложение долго не завершает свою работу, значит что-то пошло не так. Необходимо отменить все действия и запустить его ещё раз, но с большим уровнем отладки.
```bash
kubeadm reset
kubeadm init --config /etc/kubernetes/kubeadm-config.yaml -v5
```
- Смотрим ip для доступа к кластеру
```bash
kubectl cluster-info
```
- Установим драйвер сети (CNI Plugin), Cilium CNI с поддержкой multicast для разворота нод ROS2
```bash
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
```
- Смотрим ноды в кластере
```bash
kubectl get nodes
```
- Смотрим поды на ноде
```bash
kubectl get pods -A
```
- Регистрируем воркер ноды в кластере (представленная команда выводится в стандартный вывод после инициализации первой контрол ноды)
```bash
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оздаёт ресурс, если его нет,или обновляет, если он уже существует, поддерживает историю изменений, идемпотентен
```yml
# пример описания пода
---
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 пода
```bash
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)
- Пример конфигурации
```yaml
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`
```yml
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'ов и уменьшает старые
- следит, чтобы всегда было достаточно доступных реплик
- Пример обновления образа
```bash
kubectl set image deployment/myapp myapp-container=nginx:1.26
```
- Откат на предыдущую версию deployment (на ту версию, которая была применена до последнего успешного обновления)
```bash
kubectl rollout undo deployment myapp
```
- Проверка состояния
```bash
kubectl rollout status deployment myapp
kubectl get deployment
kubectl describe deployment myapp
```
- При каждом изменении (kubectl apply, set image, scale, и т.п.) создаётся новая ревизия, по умолчанию куб хранит 10 ревизий
```bash
# посмотреть историю ревизий
kubectl rollout history deployment myapp
# откатиться к ревизии
kubectl rollout undo deployment myapp --to-revision=3
```
### Service
Сущность, которая предоставляет постоянную сетевую точку доступа к группе Pod'ов
- `kubectl get endpoints my-service` - оказывает IP-адреса Pod'ов, к которым направляет трафик Service my-service
- `k get EndpointSlice`
#### Service headless
Не обеспечивает балансировку трафика к подам (нет ClusterIP), позволяет обращаться к поду по его доменному имени, используется с Statefulset, т.к. поды "статичны"
### Statefulset
Крнтроллер, похожий на Deployment гарантирует уникальность имени пода, порядок запуска, рестарта, удаления пода, постоянство ip-адреса, томов
### Тома
#### emptyDir
Обычно используется для:
- размещения кэша файлов
- данные которые необходимо хранить при сбоях в работе контейнера
- обмена файлами между несколькими контейнерами в поде
!!! info "При удалении пода (например, при перезапуске, обновлении, сбое узла и т.д.) — данные из emptyDir удаляются безвозвратно"
- Кусочек конфига
```yaml
volumeMounts:
- name: empty-volume
mountPath: /empty
volumes:
- name: empty-volume
emptyDir: {}
```
#### hostPath
!!! warning "Изпользовать hostPath небезопасно!!!"
Контейнер получает прямой доступ к файловой системе хоста
- Пример
```yaml
volumes:
- name: host-logs
hostPath:
path: /var/log/nginx
type: Directory
```
- Kubernetes может проверять, существует ли путь, и что он из себя представляет
```yaml
type: Directory # Должен быть каталог
type: DirectoryOrCreate # Создает каталог, если его нет
type: File # Должен быть файл
type: FileOrCreate # Создает файл, если его нет
type: Socket # Должен быть сокет
type: CharDevice # Символьное устройство
type: BlockDevice # Блочное устройство
```
#### ConfigMap
ConfigMap - сущность, предназначенная для хранения нечувствительных данных конфигурации в виде пар ключ: значение, позволяет отделить конфигурацию от кода и применять её к контейнерам без необходимости пересборки образа.
- `k get cm`
- Пример
```yaml
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: my-config
```
---
- Пример
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
APP_MODE: production
LOG_LEVEL: debug
```
- Передача переменных окружения из ConfigMap в Pod
```yaml
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 registry
- `tls` - TLS сертификаты
- Пример
```yaml
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # base64 от 'admin'
password: MWYyZDFlMmU2N2Rm # base64 от '1f2d1e2e67df'
```
- Для удобства админитратора есть поле `strigData`, когда манифест примениться содержимое будет закодировано в base64
```yaml
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
stringData:
username: admin
password: s3cr3t
```
- Так подключается в манифест
```yaml
env:
- name: username
valueFrom:
secretKeyRef:
name: my-secret
key: username
```
!!! warning ""
При добавлении новых секретов, необходимо помнить про правила мерджа манифестов, аннотацию `kubectl.kubernetes.io/last-applied-configuration`
- Добавление секретов в контейнер в виде тома
```yaml
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
```
```bash
# внутри контейнера
cat /etc/secret/username # выведет: user
cat /etc/secret/password # выведет: password
```
#### downwardAPI
`downwardAPI` позволяет передать метаданные `Pod'а`(например, имя пода, namespace, labels, annotations, ресурсы) в контейнер через переменные окружения или файлы.
- Пример (как том (файлы))
```yaml
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
```
- Пример (как переменные окружения)
```yaml
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`
- Пример
```yaml
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` — что делать после удаления PVC
- `Retain` - 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 в спецификации пода, например:
```yaml
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
```bash
kubectl taint nodes <node_name> key=[value]:Effect
kubectl taint nodes wr2.kryukov.local test-taint=:NoExecute
```
- Чтобы снять taint, добавить в конце команды `-`
```bash
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-` - снять метку с ноды
```yaml
spec:
nodeSelector:
special: ds-only
```
### Toleration
Toleration не гарантирует, что под будет размещен на помеченном узле. Он лишь разрешает это. Решение все равно принимает планировщик на основе других факторов (достаточно ли ресурсов и т.д.).
```yaml
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'а
```
```yaml
operator: "Equal" # точное совпадение по value
operator: "Exists" # Toleration сработает для любого taint'а с указанными key и effect. Значение value в этом случае указывать не нужно
```
### Job
Deployment, например, предназначен для запуска долгоживущих процессов (веб-сервер), которые должны работать постоянно (running), их цель быть всегда доступными
`Job` предназначен для запуска одноразовых задач, которые должны выполниться и завершиться успешно (Succeeded), их цель - выполнить работу и прекратить существование
```yaml
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
```yaml
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
```yaml
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
```yaml
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` - Жесткое правило ("Должен"). Под обязательно будет размещен на узле, удовлетворяющем условию. Если подходящего узла нет, под останется в статусе Pending
- `preferredDuringSchedulingIgnoredDuringExecution` - Предпочтение ("Желательно"). Планировщик попытается найти узел, удовлетворяющий условию. Если не найдет - разместит под на любом другом подходящем узле
> Часть `IgnoredDuringExecution` означает, что если метки на узле изменятся после того, как под уже был размещен, это не приведет к выселению пода
Операторы (operator) в `matchExpressions`:
- `In` - значение метки узла находится в указанном списке
- `NotIn` - значение метки узла НЕ находится в указанном списке
- `Exists`- узел имеет метку с указанным ключом (значение не важно)
- `DoesNotExist` - у узла НЕТ метки с указанным ключом
- `Gt (Greater than)`,` Lt (Less than)` - для числовых значений
- Пример манифеста
```yaml
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` (правило применяется в пределах одной зоны доступности)
- ПРИМЕР. Разместить реплики одного приложения на разных узлах для повышения отказоустойчивости.
```yaml
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
Для равномерного распределения подов между зонами
```yaml
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` - события в кластере кубера