# k8s

# Тестовый kubernetes

[Гитхаб книги](https://github.com/nigelpoulton/TheK8sBook.git)

[Интересная статья по настройке HA k8s](https://habr.com/ru/companies/slurm/articles/439562/)

[Hard way установка](https://github.com/kelseyhightower/kubernetes-the-hard-way/tree/master)

**Docker desktop**

**Введение.**

Для изучения kubernetes в книге "The kubernetes book 2024 edition" автора Nigel Poulton предложено использовать Docker Desktop для запуска одноузлового кластера и дальнейших экспериментов. Я решил не устанавливать лишнего в систему и запустить все на виртуальной машине. Итоговый стек: Windows 10 - Virtualbox 7.0 - Ubuntu 24.04 - Docker Desktop - K8s.

**Настройки VM:**

[![k8s_install_vmsettings1.JPG](http://bobrobotirk.ru/uploads/images/gallery/2024-11/scaled-1680-/k8s-install-vmsettings1.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2024-11/k8s-install-vmsettings1.JPG)

[![k8s_install_vmsettings2.JPG](http://bobrobotirk.ru/uploads/images/gallery/2024-11/scaled-1680-/k8s-install-vmsettings2.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2024-11/k8s-install-vmsettings2.JPG)

[![k8s_install_vmsettings3.JPG](http://bobrobotirk.ru/uploads/images/gallery/2024-11/scaled-1680-/k8s-install-vmsettings3.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2024-11/k8s-install-vmsettings3.JPG)

**Установка**

```
mkdir -p $HOME/.kube
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet
swapoff -a
kubectl
kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.0.109
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes
kubeadm token create --print-join-command
kubectl get nodes
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
kubectl get no
kubectl get po --all-namespaces
```

**Дополнительные удобства**

Настройка другого редактора (по умолчанию vi) например при выполнении команды kubectl edit pod ...

```
nano .bashrc
#Добавить строку
export EDITOR=nano
#Для использования в текущей сессии, в последующих сессиях автоматически
source ~/.bashrc
```

При установке через kubeadm для балансировки трафика требуется MetalLB, [детали установки](http://bobrobotirk.ru/books/docker-k8s/page/setevaia-podsistema "Сетевая подсистема")

Установка kubectl для управления с другой системы

```
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
```

Скопировать файл авторизации config в ~/.kube/config

# Сетевая подсистема

**Стартовая информация**

Сетевой плагин выбирается во время установки кластера.

 [Документация, сетевая модель K8s](https://kubernetes.io/docs/concepts/services-networking/) Выдержки:

1. Сетевая подсистема используется только подами (не нодами).
2. У каждой node свой пул ip адресов для запущенных у них pod.
3. Каждый Pod в кластере имеет собственный уникальный в пределах кластера IP адрес.
4. У каждого Pod свое частное сетевое пространство, общее для всех контейнеров внутри Pod. Контейнеры внутри Pod взаимодействуют через localhost.
5. Pod'ы взаимодействуют с Pod на других нодах при помощи сетевого плагина (CNI).
6. Служба - это метод предоставления доступа к Pod в кластере (внутренний и внешний)

Вот вроде просто, но нихера не понятно. Поэтому дальше начинается самое веселое)

**Следствия пункта 1.** Связность между нодами и контроллером опосредованно зависит от сетевого взаимодействия подов. Поэтому вопрос сети при управлении - один вопрос, вопрос сети подов - второй вопрос.

Первый вопрос относительно простой: сетевая видимость точка - точка между IP адресами. Необходимые порты при настройке port-forwarding:

На мастер нодах:

```
TCP     6443*       Kubernetes API Server
TCP     2379-2380   etcd server client API
TCP     10250       Kubelet API
TCP     10251       kube-scheduler
TCP     10252       kube-controller-manager
TCP     10255       Read-Only Kubelet API
```

На воркерах:

```
TCP     10250       Kubelet API
TCP     10255       Read-Only Kubelet API
TCP     30000-32767 NodePort Services
```

Открываем порты и управление начнет работать. Теперь нужна сетевая связность между подами.

<div drawio-diagram="127"><img src="http://bobrobotirk.ru/uploads/images/drawio/2025-03/drawing-1-1742625260.png" alt=""/></div>

Для использования службы типа LoadBalancer необходимо установить балансировщик, например MetalLB. При существовании кластера в пределах одного L2 сегмента хватит L2 режима. Однако при разных сетях у worker потребуется BGP. Картинку можно представить следующим образом:

<div drawio-diagram="134"><img src="http://bobrobotirk.ru/uploads/images/drawio/2025-04/drawing-1-1743536558.png" alt=""/></div>

Роли маршрутизаторов R1 и R2 выполняют сетевые плагины (MetalLB, Calico, ...) в BGP режиме. Им необходим обмен маршрутной информацией в пределах L3 сети. Классическая сетевая задача - сделать видимым IP Pool 1 для IP Pool 2. Тут можно решить при помощи VXLAN и т д. В случае реализации без туннелей в L3 сети требуется, чтобы маршрутизаторы внутри этой сети также обладали маршрутной информацией.

**Настройка MetalLB**

Примените манифест для установки MetalLB

```
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
```

Дождаться запуска контейнеров

```
kubectl get pods -n metallb-system
```

Создайте конфигурационный файл для MetalLB. Например, `metallb-config.yaml`

```yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.240-192.168.1.250  # Укажите диапазон IP-адресов, доступных в вашей сети
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2advert
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool
```

Применить конфигурацию:

```
kubectl apply -f metallb-config.yaml
```

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"></div>

# Pod & containers

**Pod (под)**

Pod это уровень абстракции. Он включает:

- совместное использование ресурсов
- управление планировщиком
- тестирование на рабочеспособность
- политики перезапуска
- политики безопасности
- контроль завершения
- тома

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

Под смертен (после завершения или ошибки он удаляется без возможности перезапуска) и постоянны (для изменения нужно удалить старый и создать новый).

**Настройка ноды для пода**

NodeSelectors - список меток нодов. Простейший случай.

Affinity and anti-affinity - продвинутый способ Позволяет   
attract  
• Anti-affinity rules repel  
• Hard rules must be obeyed  
• Soft rules are only suggestions

Topology spread constraints - ограничения на топологию  
Resource requests and resource limits

**Теория процесса запуска pod**

1. Создать YAML манифест
2. Отправить манифест на API сервер
3. Запрос будет аутентифицирован и авторизован
4. Спецификация будет проверена
5. Планировщик отфильтрует ноды на основе ограничений
6. Под будет привязан к удовлетворяющей требованиям ноде
7. Сервис kubelet на ноде получит задание
8. Сервис kubelet скачает спецификацию и сформирует задачу для исполнения
9. Сервис kubelet мониторит статус поды и в случае изменения направляет информацию на планировщик

**Запуск пода**

Есть два варианта: непосредственно через манифест на ноде или через контроллер. Первый вариант быстрый но без большинства возможностей (статический под, типа docker container). Второй вариант используется обычно.

Кластер создает сеть подов и автоматически подключает все поды к ней. Это одноуровневая L2 overlay сеть

**Статусы пода**

Pending под еще не создан, поиск ноды для запуска  
Running нода найдена, запуск произведен  
Init:X/Y исполнение инит контейнеров, завершено X из Y

Политики перезапуска настраиваются для контейнера. Поэтому пока идет перезапуск контейнера, считается что под еще работает. При обновлении под удаляется и создается новый. Это необходимо учитывать при разработке архитектуры.

**Структура YAML файла**

Верхний уровень

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki" style="border-collapse: collapse; width: 100%; height: 148.563px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.375px;"><td style="width: 31.7044%; height: 29.375px;">Kind</td><td style="width: 68.2956%; height: 29.375px;">Тип определяемого объекта. В данном случае Pod

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">apiVersion</td><td style="width: 68.2956%; height: 29.7969px;">Версия API

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">metadata</td><td style="width: 68.2956%; height: 29.7969px;">Метаданные

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">spec</td><td style="width: 68.2956%; height: 29.7969px;">Спецификация контейнеров

</td></tr></tbody></table>

Metadata: метаданные пода

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-na" style="border-collapse: collapse; width: 100%; height: 89.3907px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">name</td><td style="width: 68.2956%; height: 29.7969px;">Имя пода. Используется в качестве hostname у всех контейнеров для этого пода. Поэтому должно быть валидным DNS именем

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">labels</td><td style="width: 68.2956%; height: 29.7969px;">Метки пода

</td></tr></tbody></table>

Spec: описание параметров контейнера, томов, внутри для каждого контейнера - name: имя\_контейнера

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-im" style="border-collapse: collapse; width: 100%; height: 89.3907px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">containers</td><td style="width: 68.2956%; height: 29.7969px;">Обычные контейнеры

```yaml
spec:
  initContainers:
  - name: ...
  containers:
  - name: ...
```

</td></tr><tr><td style="width: 31.7044%;">initContainers</td><td style="width: 68.2956%;">Контейнеры, запускаемые до старта обычных контейнеров. Обычные запускаются только после завершения init контейнеров.

</td></tr><tr><td style="width: 31.7044%;">volumes:</td><td style="width: 68.2956%;">Тома

```yaml
volumes:
  - name: html
    emptyDir: {}
```

</td></tr></tbody></table>

Параметры контейнера:

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-im-1" style="border-collapse: collapse; width: 100%; height: 89.3907px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">image</td><td style="width: 68.2956%; height: 29.7969px;">Образ

```
image: nigelpoulton/k8sbook:1.0
```

Для использования другого хаба добавить URL перед именем образа

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">ports</td><td style="width: 68.2956%; height: 29.7969px;">Порты

</td></tr><tr><td style="width: 31.7044%;">resources</td><td style="width: 68.2956%;">ограничения на ресурсы

```
resources:
  requests: <<==== Minimums for scheduling
    cpu: 0.5
    memory: 256Mi
  limits: <<==== Maximums for kubelet to cap
    cpu: 1.0
    memory: 512Mi
```

Если нода с нужными ресурсами не найдена - под в статусе Pending. Для всех контейнеров в поде.

</td></tr><tr><td style="width: 31.7044%;">env</td><td style="width: 68.2956%;">Переменные окружения.

```
env:
- name: GIT_SYNC_REPO
  value: https://github.com/nigelpoulton/ps-sidecar.git
```

</td></tr><tr><td style="width: 31.7044%;">volumeMounts</td><td style="width: 68.2956%;">Тома

```
volumeMounts:
- name: html <<==== Mount shared volume
  mountPath: /usr/share/nginx/
```

Настройки тома проводятся отдельно

</td></tr></tbody></table>

Пример 1.

```yaml
kind: Pod
apiVersion: v1
metadata:
  name: hello-pod
  labels:
    zone: prod
    version: v1
spec:
  containers:
  - name: hello-ctr
    image: nigelpoulton/k8sbook:1.0
    ports:
    - containerPort: 8080
    resources:
      limits:
        memory: 128Mi
        cpu: 0.5
```

Пример 2. Init контейнер.

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: initpod
  labels:
    app: initializer
spec:
  initContainers:
  - name: init-ctr
    # Pinned to 1.28 as newer versions have a sketchy nslookup command that doesn't work. Can also use a non-busybox image here
    image: busybox:1.28.4
    command: ['sh', '-c', 'until nslookup k8sbook; do echo waiting for k8sbook service; sleep 1; done; echo Service found!']
  containers:
    - name: web-ctr
      image: nigelpoulton/web-app:1.0
      ports:
        - containerPort: 8080
```

Пока образы в init контейнере не завершились, статус в Init:0/1

```
kubectl get pods --watch
NAME                               READY   STATUS     RESTARTS   AGE
initpod                            0/1     Init:0/1   0          16s
```

Пример 3. Дополнительный контейнер, обновляющий папку при изменении репозитория

```yaml
# Some network drivers and laptop VM implementations cause issues with Service port mapping
# Minikube users may have to `minikube service svc-sidecar` to be able to access on `localhost:30001`
# Other users may have to run `kubectl port-forward service/svc-sidecar 30001:80`
apiVersion: v1
kind: Pod
metadata:
  name: git-sync
  labels:
    app: sidecar
spec:
  containers:
  - name: ctr-web
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/
  - name: ctr-sync
    image: k8s.gcr.io/git-sync:v3.1.6
    volumeMounts:
    - name: html
      mountPath: /tmp/git
    env:
    - name: GIT_SYNC_REPO
      value: https://gitverse.ru/bobrobot/k8s_pods.git
    - name: GIT_SYNC_BRANCH
      value: master
    - name: GIT_SYNC_DEPTH
      value: "1"
    - name: GIT_SYNC_DEST
      value: "html"
  volumes:
  - name: html
    emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: svc-sidecar
spec:
  selector:
    app: sidecar
  type: NodePort
  ports:
  - port: 80
    nodePort: 30001
```

При обращении на адрес кластера или ноды будет отображаться страница.

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="border-collapse: collapse; width: 100%;"><thead><tr><td class="align-center" style="width: 24.9106%;">Команда</td><td class="align-center" style="width: 21.4483%;">Доп. пар.</td><td class="align-center" style="width: 53.6411%;">Описание</td></tr></thead><tbody><tr><td style="width: 24.9106%;">kubectl explain pods --recursive</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Вывод всех параметров, доступных для конфигурирования Pod</td></tr><tr><td style="width: 24.9106%;">kubectl get pods</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">список контейнеров</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">-o yaml</td><td style="width: 53.6411%;">расширенная информация о подах</td></tr><tr><td style="width: 24.9106%;">kubectl apply</td><td style="width: 21.4483%;">-f file.yml</td><td style="width: 53.6411%;">Создание под из file.yml</td></tr><tr><td style="width: 24.9106%;">kubectl describe </td><td style="width: 21.4483%;">pod pod\_name</td><td style="width: 53.6411%;">Описание пода</td></tr><tr><td style="width: 24.9106%;">kubectl logs pod\_name</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">логи на поде. По умолчанию первого контейнера в поде</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">--container cont\_name</td><td style="width: 53.6411%;">логи конкретного контейнера</td></tr><tr><td style="width: 24.9106%;">kubectl exec</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Выполнение команд внутри контейнера Два варианта</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">pod\_name -- command</td><td style="width: 53.6411%;">Выполняет command на pod\_name и возвращает результат в консоль. По умолчанию на первом контейнере. -- container для указания контейнера.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">-it pod\_name -- command</td><td style="width: 53.6411%;">Подключается в интерактивном режиме на контейнер и выполняет команду. ```
kubectl exec -it hello-pod -- sh
```

</td></tr><tr><td style="width: 24.9106%;">kubectl edit pod pod\_name</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Редактирование под (для nano - в части дополнительные удобства Тестовый k8s) Пока не получилось.</td></tr><tr><td style="width: 24.9106%;">kubectl delete </td><td style="width: 21.4483%;">pod</td><td style="width: 53.6411%;">Удаление подов. Имена подов через пробел</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">svc</td><td style="width: 53.6411%;">Удаление сервисов.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">-f </td><td style="width: 53.6411%;">Удаление с использованием yaml файлов.</td></tr></tbody></table>

</div>**Container (контейнер)**

Паттерны мультиконтейнеров

Init контейнеры - специальный тип контейнеров, для которых K8s гарантирует единственный запуск и завершение, перед остальными контейнерами. Пример: есть приложение и внешнее API с которым обязательно должно быть взаимодействие при старте. Вместо нагрузки на основную логику, можно процесс проверки вывести в init контейнер.

Slidecar контейнеры - выполняют периферийные задачи. Пока что бета.

# Namespaces

Разделяет кластер на виртуальные кластеры. Это не Namespace ядра! По умолчанию объекты попадают в default namespace. Настраиваются свои пользователи, права, ресурсы и политики.

**Создание и привязка к пространству имен**

Императивный способ:

```
kubectl create ns hydra
```

Декларативный способ: создать yaml файл и применить его.

---

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

```
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: shield <<==== Namespace
  name: default
```

**Структура YAML файла**

Верхний уровень

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki" style="border-collapse: collapse; width: 100%; height: 117.766px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 28.375px;"><td style="width: 31.7044%; height: 28.375px;">Kind</td><td style="width: 68.2956%; height: 28.375px;">Тип определяемого объекта, Namespace

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">apiVersion</td><td style="width: 68.2956%; height: 29.7969px;">Версия API

</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">metadata</td><td style="width: 68.2956%; height: 29.7969px;">Метаданные

</td></tr></tbody></table>

Metadata

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-na" style="border-collapse: collapse; width: 100%; height: 117.766px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 28.375px;"><td style="width: 31.7044%; height: 28.375px;">name</td><td style="width: 68.2956%; height: 28.375px;">Имя</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">labels</td><td style="width: 68.2956%; height: 29.7969px;">метки</td></tr></tbody></table>

**Примеры**

```
kind: Namespace
apiVersion: v1
metadata:
  name: shield
  labels:
    env: marvel
```

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="border-collapse: collapse; width: 100%;"><thead><tr><td class="align-center" style="width: 24.9106%;">Команда</td><td class="align-center" style="width: 21.4483%;">Доп. пар.</td><td class="align-center" style="width: 53.6411%;">Описание</td></tr></thead><tbody><tr><td style="width: 24.9106%;">kubectl api-resources</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Список API ресурсов, в частности - делятся ли на namespace</td></tr><tr><td style="width: 24.9106%;">kubectl get namespaces</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Список пространств имен</td></tr><tr><td style="width: 24.9106%;">kubectl describe ns name\_ns</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Информация по name\_ns пространству имен</td></tr><tr><td style="width: 24.9106%;">Все команды получения информации</td><td style="width: 21.4483%;">--namespace default</td><td style="width: 53.6411%;">Фильтрация по определенному namespace</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">--all-namespaces</td><td style="width: 53.6411%;">Для всех namespace</td></tr><tr><td style="width: 24.9106%;">kubectl create ns ns\_name</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Создание пространства имен ns\_name</td></tr><tr><td style="width: 24.9106%;">kubectl delete ns ns\_name</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Удаление пространства имен ns\_name</td></tr><tr><td style="width: 24.9106%;">kubectl config set-context --current --namespace shield</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Установка пространства имен по умолчанию</td></tr></tbody></table>

</div>

# Deployment

Deployments наиболее популярный способ для запуска приложений без сохранения состояния. Это добавляет проверку состояния, масштабирование, восстановление.

Реализовано через deployment контроллер. Каждый контроллер управляет одним или несколькими одинаковыми подами.

[![k8s_deployments_1.JPG](http://bobrobotirk.ru/uploads/images/gallery/2025-03/scaled-1680-/k8s-deployments-1.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2025-03/k8s-deployments-1.JPG)

**Масштабирование (Scalling)**

Существуют несколько типов

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki" style="border-collapse: collapse; width: 100%; height: 117.766px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Тип</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 28.375px;"><td style="width: 31.7044%; height: 28.375px;">Horizontal Pod Autoscaler</td><td style="width: 68.2956%; height: 28.375px;">Масштабирование количества подов, наиболее часто используется.</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">Vertical Pod Autoscaler</td><td style="width: 68.2956%; height: 29.7969px;">Масштабирование ресурсов, потребляемых подами. Не установлен по умолчанию. Редко используется</td></tr><tr><td style="width: 31.7044%;">Cluster Autoscaler</td><td style="width: 68.2956%;">Добавляет/удаляет ноды. По умолчанию, часто используется.</td></tr></tbody></table>

Например, указываем кол-во подов от 2 до 10. Нагрузка повысилась, и HPA запрашивает еще 2 пода. Они запускаются. Но нагрузка растет, и запрашивается еще 2 пода. Однако на существующем кластере нет возможности запустить еще 2 пода, и они переходят в статус Pending. CA определяет Pending поды и увеличивает количество нодов, запуская там поды. И наоборот.

Масштабирование связано с понятием текущего состояния (state). Есть необходимое состояние и наблюдаемое состояние. При неравенстве контроллер запускает процесс изменений.

<span style="color: rgb(224, 62, 45);">Важно: архитектура приложения должна поддерживать возможность масштабирования. Микросервисы должны взаимодействовать только через API. При увеличении количества, добавляется новый под.</span>

**Реплики**

ReplicaSets - набор настроек и подов с одной версией конфигурации. При обновлении yaml создается вторая ReplicaSet и один новый под. Из старой ReplicaSet удаляется один под. И так далее до полного обновления. Но конфигурация сохраняется. Можно вернуть к старым настройкам.

**Структура YAML файла**

Верхний уровень

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki-1" style="border-collapse: collapse; width: 100%; height: 117.766px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 28.375px;"><td style="width: 31.7044%; height: 28.375px;">kind</td><td style="width: 68.2956%; height: 28.375px;">Тип, в данном случае Deployments</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">spec</td><td style="width: 68.2956%; height: 29.7969px;">Спецификация</td></tr></tbody></table>

spec

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-na" style="border-collapse: collapse; width: 100%; height: 117.766px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 31.7044%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 68.2956%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 28.375px;"><td style="width: 31.7044%; height: 28.375px;">strategy</td><td style="width: 68.2956%; height: 28.375px;">Стратегия восстановления</td></tr><tr style="height: 29.7969px;"><td style="width: 31.7044%; height: 29.7969px;">replicas</td><td style="width: 68.2956%; height: 29.7969px;">кол-во реплик</td></tr><tr><td style="width: 31.7044%;">selector</td><td style="width: 68.2956%;">правила выбора меток</td></tr><tr><td style="width: 31.7044%;">template</td><td style="width: 68.2956%;">описание шаблона (все аналогично описанию пода)</td></tr></tbody></table>

**Примеры**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deploy
spec:
  replicas: 10
  selector:
    matchLabels:
      app: hello-world
  revisionHistoryLimit: 5
  progressDeadlineSeconds: 300
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-pod
        image: nigelpoulton/k8sbook:1.0
        ports:
        - containerPort: 8080
        resources:
          limits:
            memory: 128Mi
            cpu: 0.1
```

Пример сервиса для данного приложения

```yaml
apiVersion: v1
kind: Service
metadata:
  name: lb-svc
  labels:
    app: hello-world
spec:
  type: LoadBalancer
  ports:
  - port: 8080
    protocol: TCP
  selector:
    app: hello-world
```

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="border-collapse: collapse; width: 100%; height: 268.172px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 24.9106%; height: 29.7969px;">Команда</td><td class="align-center" style="width: 21.4483%; height: 29.7969px;">Доп. пар.</td><td class="align-center" style="width: 53.6411%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl get deploy dep\_name</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">статус</td></tr><tr><td style="width: 24.9106%;">kubectl describe deploy dep-name</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Расширенная информация</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl get rs</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">Список реплик</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl scale</td><td style="width: 21.4483%; height: 29.7969px;">deploy dep\_name --replicas count</td><td style="width: 53.6411%; height: 29.7969px;">Императивное масштабирование. Нежелательно.</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl rollout status deployment dep\_name</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">Статус обновления подов</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl rollout pause deploy dep\_name</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">Приостановка обновления</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl describe deploy dep\_name</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">Отображает в частности список роллбеков</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl rollout history deployment dep\_name</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">История роллбеков</td></tr><tr style="height: 29.7969px;"><td style="width: 24.9106%; height: 29.7969px;">kubectl rollout undo deployment hello-deploy --to-revision=1</td><td style="width: 21.4483%; height: 29.7969px;"> </td><td style="width: 53.6411%; height: 29.7969px;">Возврат. Быстро, но не рекомендуется. Лучше через загрузку старого файла из репозитория и обновление.</td></tr></tbody></table>

</div>

# Services

Сервис используется для подключения подов к внешней сети. Сервис использует метки для выбора подов. Все указанные метки должны быть на поде (дополнительные метки пода игнорируются)

В сервисе - раздел selector

```
spec:
  replicas: 10
  <Snip>
  template:
    metadata:
      labels:
        project: tkb
        zone: prod
<Snip>
---
apiVersion: v1
kind: Service
metadata:
  name: tkb
spec:
  ports:
  - port: 8080
  selector:
    project: tkb 
    zone: prod 
```

**Типы сервисов**

**ClusterIP** Используется для доступности подов внутри кластера. Доступность по имени сервиса.

**NodePort Services** Используется для доступа приложений снаружи кластера. Добавляет указанный порт на каждую ноду.

```
apiVersion: v1
kind: Service
metadata:
  name: skippy <<==== Registered with the internal cluster DNS (ClusterIP)
spec:
  type: NodePort <<==== Service type
  ports:
  - port: 8080 <<==== ClusterIP port
    targetPort: 9000 <<==== Application port in container
    nodePort: 30050 <<==== External port on every cluster node (NodePort)
selector:
  app: hello-world
```

В данном примере изнутри под доступен по порту 8080, снаружи - по порту 30050. Диапазон портов 30000-32767.

**LoadBalancer Services** Используется для сервисов со стартовым диапазоном портов, наиболее часто.

```
apiVersion: v1
kind: Service
metadata:
  name: lb <<==== Registered with cluster DNS
spec:
  type: LoadBalancer
  ports:
  - port: 8080 <<==== Load balancer port
    targetPort: 9000 <<==== Application port inside container
selector:
  project: tkb
```

Мой кубер использует flannel, а для корректной работы LoadBalancer нужен балансировщик, например MetalLB. Стек в случае балансировщика:

[![k8s_services_1.JPG](http://bobrobotirk.ru/uploads/images/gallery/2025-03/scaled-1680-/k8s-services-1.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2025-03/k8s-services-1.JPG)

**Headless сервисы**

Сервисы без IP адреса. Их цель - создать DNS записи для StatefulSet подов. Клиенты запрашивают DNS имена подов и направляют непосредственно им запросы вместо использования кластерного IP. Пример сервиса:

```
apiVersion: v1
kind: Service <<==== Normal Kubernetes Service
metadata:
  name: dullahan
  labels:
    app: web
spec:
  ports:
    - port: 80
  name: web
  clusterIP: None <<==== Make this a headless Service
  selector:
    app: web

```

**Структура YAML файла**

Верхний уровень

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki-1" style="width: 105.952%;"><thead><tr><td class="align-center" style="width: 25.495%;">Параметр</td><td class="align-center" style="width: 74.505%;">Описание</td></tr></thead><tbody><tr><td style="width: 25.495%;">kind</td><td style="width: 74.505%;">Тип, в данном случае Service</td></tr><tr><td style="width: 25.495%;">spec</td><td style="width: 74.505%;">Спецификация</td></tr></tbody></table>

spec

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-na" style="width: 99.6429%;"><thead><tr><td class="align-center" style="width: 18.6788%;">Параметр</td><td class="align-center" style="width: 81.3212%;">Описание</td></tr></thead><tbody><tr><td style="width: 18.6788%;">type</td><td style="width: 81.3212%;">тип (ClusterIP, NodePort, LoadBalancer)</td></tr><tr><td style="width: 18.6788%;">ports</td><td style="width: 81.3212%;">port - порт фронтенда

targetPort - порт на бэке

</td></tr><tr><td style="width: 18.6788%;">selector</td><td style="width: 81.3212%;">правила выбора меток</td></tr></tbody></table>

Пример yaml файла

```
apiVersion: v1
kind: Service
metadata:
  name: cloud-lb
spec:
  type: LoadBalancer
  ports:
  - port: 9000
    targetPort: 8080
  selector:
    chapter: services
```

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="width: 99.7619%;"><thead><tr><td class="align-center" style="width: 27.3572%;">Команда</td><td class="align-center" style="width: 22.9415%;">Доп. пар.</td><td class="align-center" style="width: 49.7013%;">Описание</td></tr></thead><tbody><tr><td style="width: 27.3572%;">kubectl expose deployment dep\_name --type=LoadBalancer</td><td style="width: 22.9415%;"> </td><td style="width: 49.7013%;">Ручное создание сервиса. Вот только не поехало.</td></tr><tr><td style="width: 27.3572%;">kubectl get svc -o wide</td><td style="width: 22.9415%;"> </td><td style="width: 49.7013%;">Информация по сервисам</td></tr><tr><td style="width: 27.3572%;">kubectl get endpointslices</td><td style="width: 22.9415%;">  
</td><td style="width: 49.7013%;">Список ендпоинтов</td></tr><tr><td style="width: 27.3572%;">kubectl describe endpointslice epname</td><td style="width: 22.9415%;">  
</td><td style="width: 49.7013%;">Описание ендпоинта</td></tr></tbody></table>

</div>

# Ingress

Используется для организации внешнего взаимодействия на L7 уровне. Ingress ресурсы определяют правила маршрутизации, Ingress контроллер выполняет задачу.

<span style="color: rgb(224, 62, 45);">Маршрутизация в смысле L7, не в смысле L3</span>

Могут быть host-based и path-based маршруты:

<table border="1" id="bkmrk-host-based-example-p" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 33.3333%;"></col><col style="width: 33.3333%;"></col><col style="width: 33.3333%;"></col></colgroup><thead><tr><td class="align-center">Host-based example</td><td class="align-center">Path-based example</td><td class="align-center">Backend K8s Service</td></tr></thead><tbody><tr><td>shield.mcu.com</td><td>mcu.com/shield</td><td>shield</td></tr><tr><td>hydra.mcu.com</td><td>mcu.com/hydra</td><td>hydra</td></tr></tbody></table>

[![k8s_ingress_1.JPG](http://bobrobotirk.ru/uploads/images/gallery/2025-03/scaled-1680-/k8s-ingress-1.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2025-03/k8s-ingress-1.JPG)

Необходим внешний Ingress-controller, очень часто Nginx.

**Ingress классы**

Позволяют запустить несколько ingress контроллеров в одном кластере. Сначала привязывается Ingress контроллер к классу, затем при создании объект Ingress привязывается к классу.

**Пример**

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mcu-all
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: shield.mcu.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-shield
            port:
              number: 8080
  - host: mcu.com
    http:
      paths:
      - path: /shield
        pathType: Prefix
        backend:
          service:
            name: svc-shield
            port:
              number: 8080
```

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="width: 100.357%;"><thead><tr><td class="align-center" style="width: 25.5066%;">Команда</td><td class="align-center" style="width: 19.4381%;">Доп. пар.</td><td class="align-center" style="width: 55.0553%;">Описание</td></tr></thead><tbody><tr><td style="width: 25.5066%;">kubectl get ingressclass</td><td style="width: 19.4381%;"> </td><td style="width: 55.0553%;">Список классов Ingress</td></tr><tr><td style="width: 25.5066%;">kubectl describe ingressclass class\_name</td><td style="width: 19.4381%;"> </td><td style="width: 55.0553%;">Детализация для класса class\_name</td></tr><tr><td style="width: 25.5066%;">kubectl get ingress my-ingress -n my-ns -o yaml</td><td style="width: 19.4381%;"> </td><td style="width: 55.0553%;">Получить конфигурацию в виде yaml</td></tr><tr><td style="width: 25.5066%;">kubectl get ing</td><td style="width: 19.4381%;"> </td><td style="width: 55.0553%;">Список ingress</td></tr><tr><td style="width: 25.5066%;">kubectl describe ing mcu-all</td><td style="width: 19.4381%;">  
</td><td style="width: 55.0553%;">Детализация ingress</td></tr></tbody></table>

</div>

# Storages

Система хранения работает через драйверы (CSI плагины) или локально на нодах. Второй вариант неудобный. Далее первый вариант. Разработчик обычно предоставляет плагины в виде Helm чартов или yaml установщиков. Они устанавливаются в виде набора подов в namespace kube-system. [Список плагинов](https://kubernetes-csi.github.io/docs/drivers.html) Для тестов можно использовать встроенный драйвер, OpenEbs. К вопросу выбора драйвера, архитектуры хранилища и безопасности необходимо подходить очень серьезно.

Процесс запроса ресурсов: Pod Volume - PVC - SC - CSI Plugin

**Storage Classes**

ресурсы в storage.k8s.io/v1 группе. Неизменяемый. Для обновления нужно удалить и создать. SC access mode:

- ReadWriteOnce - один PVC может подключиться в режиме чтения/записи
- ReadWriteMany - несколько PVC. Файловые и объектные хранилища обычно поддерживают, блоковые нет.
- ReadOnlyMany - несколько PVC в режиме чтения.

Все PV должны подключиться в одинаковом режиме.

**Reclaim policy (политика восстановления)**

Политики восстановления сообщают Kubernetes, что делать с PV и связанным с ним внешним хранилищем, когда его PVC будет запущен.

- Delete. Удаление PVC приведет к удалению PV и внешнего хранилища.
- Retain. Безопаснее, но нужно самостоятельно удалять ресурсы.

**Volume binding mode**

Момент создания бакета. Immediate - сразу же, WaitForFirstConsumer - при подключении первого.

**Примеры**

**Локальное хранилище (устаревшее)**

На воркере

```
sudo mkdir -p /mnt/disks/ssd1
sudo chmod 777 /mnt/disks/ssd1  # Для упрощения примера
```

Настройка PV

```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-local-pv
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - <node-name>  # Замените на имя узла, где находится директория
```

Настройка PVC

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: example-local-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 10Gi
```

Проверка связи

```
kubectl get pv
kubectl get pvc
```

Использование в Pod

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
    - name: example-container
      image: nginx
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: local-storage
  volumes:
    - name: local-storage
      persistentVolumeClaim:
        claimName: example-local-pvc
```

Удаление ресурсов

```
kubectl delete pod example-pod
kubectl delete pvc example-local-pvc
kubectl delete pv example-local-pv
```

**Хранилище Yandex.cloud**

S3 aws - совместимое хранилище.

1\. Настройка доступа через консоль

Установить консоль yc

```
curl -sSL https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash
source ~/.bashrc
```

Запросить новый OAuth токен. Время жизни токена 1 год.

Инициализировать консоль

```
yc init
```

2\. Настройка доступа к бакету

Через web-консоль создать бакет.

[![k8s_volumes_1.JPG](http://bobrobotirk.ru/uploads/images/gallery/2025-03/scaled-1680-/k8s-volumes-1.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2025-03/k8s-volumes-1.JPG)

Выяснить folder\_id созданного аккаунта

```
yc storage bucket get k8stest

name: k8stest
folder_id: b...q
anonymous_access_flags:
  read: false
  list: false
  config_read: false
default_storage_class: STANDARD
versioning: VERSIONING_DISABLED
max_size: "1073741824"
created_at: "2025-03-23T06:35:58.715069Z"
```

Создать сервисный аккаунт для доступа к бакету, в выводе будет id аккаунта

```
yc iam service-account create --name k8stest --output key.json
```

Файл key.json понадобится далее.

Добавить роль для созданного бакета сервис аккаунту

```
yc resource-manager folder add-access-binding <идентификатор_каталога> \
  --role <роль> \
  --subject serviceAccount:<идентификатор_сервисного_аккаунта>
```

3\. Установка CSI плагина для yandex.cloud

```
git clone https://github.com/deckhouse/yandex-csi-driver.git
cd yandex-csi-driver
```

В самом git сказано, что запускать нужно из папки deploy/1.17 установив 2 переменные. У меня это не заработало.

В папке yandex-csi-driver/charts/yandex-csi-controller установить serviceAccountJSON и folderID

В файле csidriver.yaml заменить apiVersion: storage.k8s.io/v1beta1 на apiVersion: storage.k8s.io/v1

Дополнительно:

```
# Список сервис аккаунтов
yc iam service-account list 
# Детализация информации по аккаунту
yc iam service-account get <идентификатор_аккаунта>
# Удаление аккаунта
yc iam service-account delete <идентификатор_аккаунта>
```

И нечего с yandex не получилось.

**Настройка MinIO**

[Настройка внешнего MinIO](http://bobrobotirk.ru/books/docker-k8s/page/failovyi-server "Файловый сервер")

**Настройка авторизации**

Создать Secret. Ключ и Секрет не в Base64, а как указано при создании бакета.

```yaml
apiVersion: v1
kind: Secret
metadata:
  namespace: kube-system
  name: csi-s3-secret
stringData:
  accessKeyID: XP5...Ih
  secretAccessKey: klz...zo
  # For AWS set it to "https://s3.<region>.amazonaws.com"
  endpoint: http://192.168.1.194:9000
  # If not on S3, set it to ""
  region: ""

```

Применить Secret

```
kubectl apply -f 1-minio-credentials.yaml
kubectl get secret
```

**Установка csi драйвера**

Проверить установленные csi драйверы, поставить драйвер

```
git clone https://github.com/ctrox/csi-s3.git
cd csi-s3/deploy/kubernetes
kubectl apply -f .
```

Вот только какого-то хера в данной директории не было yaml для создания драйвера) Добавляем драйвер

```yaml
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: ch.ctrox.csi.s3-driver
spec:
  attachRequired: false
  podInfoOnMount: true

```

Обязательно в списке драйверов должен появиться драйвер

```
kubectl get csidrivers.storage.k8s.io
NAME                     ATTACHREQUIRED   PODINFOONMOUNT   TOKENREQUESTS   REQUIRESREPUBLISH   MODES        AGE
ch.ctrox.csi.s3-driver   false            true             <unset>         false               Persistent   36m

```

И все поды csi должны быть запущены

```
kubectl --namespace kube-system get pods | grep csi
csi-attacher-s3-0               1/1     Running   0              9h
csi-provisioner-s3-0            2/2     Running   0              9h
csi-s3-f5v74                    2/2     Running   0              9h
```

**Настройка класса и PVC**

Добавляем StorageClass

```yaml
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: csi-s3
provisioner: ch.ctrox.csi.s3-driver
parameters:
  # specify which mounter to use
  # can be set to rclone, s3fs, goofys or s3backer
  #mounter: rclone
  mounter: "s3fs"
  otherOpts: "-o allow_other -o uid=0 -o gid=0 -o umask=000"
  # to use an existing bucket, specify it here:
  bucket: bucketone
  path: "pvc-fbd999ab-1ba7-4b07-922d-51270e6028d9"
  csi.storage.k8s.io/provisioner-secret-name: csi-s3-secret
  csi.storage.k8s.io/provisioner-secret-namespace: kube-system
  csi.storage.k8s.io/controller-publish-secret-name: csi-s3-secret
  csi.storage.k8s.io/controller-publish-secret-namespace: kube-system
  csi.storage.k8s.io/node-stage-secret-name: csi-s3-secret
  csi.storage.k8s.io/node-stage-secret-namespace: kube-system
  csi.storage.k8s.io/node-publish-secret-name: csi-s3-secret
  csi.storage.k8s.io/node-publish-secret-namespace: kube-system
  sslVerify: "false"
reclaimPolicy: Retain
volumeBindingMode: Immediate

```

mounter rclone на финальном этапе отказался создавать файлы.

В директории бакета была создана директория pvc-fbd999ab-1ba7-4b07-922d-51270e6028d9/csi-fs Не знаю почему. но при команде ls (проверка) он находился внутри нее.

Добавляем PVC

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-s3-pvc
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-s3

```

**Проверка:**

```
apiVersion: v1
kind: Pod
metadata:
  name: minio-test-pod
spec:
  containers:
  - name: app
    image: alpine
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: minio-storage
      mountPath: /mnt/minio
  volumes:
  - name: minio-storage
    persistentVolumeClaim:
      claimName: csi-s3-pvc
      readOnly: false

```

Команда

```
kubectl exec -it minio-test-pod -- sh -c "echo 'New test' > /mnt/minio/test2.txt && ls /mnt/minio"
```

должна вывести созданный файл в списке.

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="width: 100.238%;"><thead><tr><td class="align-center" style="width: 40.1146%;">Команда</td><td class="align-center" style="width: 11.3181%;">Доп. пар.</td><td class="align-center" style="width: 48.7106%;">Описание</td></tr></thead><tbody><tr><td style="width: 40.1146%;">kubectl get csidrivers.storage.k8s.io</td><td style="width: 11.3181%;"> </td><td style="width: 48.7106%;">Список CSI-драйверов</td></tr><tr><td style="width: 40.1146%;">kubectl get storageclass -o wide</td><td style="width: 11.3181%;"> </td><td style="width: 48.7106%;">Какой PROVISIONER используется</td></tr><tr><td style="width: 40.1146%;">kubectl get pods -n kube-system | grep csi</td><td style="width: 11.3181%;"> </td><td style="width: 48.7106%;">Поды CSI-драйверов. Какие поды отвечают за CSI</td></tr><tr><td style="width: 40.1146%;">kubectl get daemonsets -n kube-system</td><td style="width: 11.3181%;"> </td><td style="width: 48.7106%;">Где работает CSI-драйвер</td></tr></tbody></table>

</div>

# Config maps & secrets

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

```
kind: ConfigMap
apiVersion: v1
metadata:
  name: epl
data:
  Competition: epl
  Season: 2022-2023
  Champions: Manchester City
  test.conf: |
    env = plex-test
    endpoint = 0.0.0.0:31001
    char = utf8
    vault = PLEX/test
    log-size = 512M
```

Их можно использовать как переменные окружения, параметры запуска и файлы внутри контейнера.

**Использование ConfigMap**

**В виде переменных окружения:**

```
apiVersion: v1
kind: Pod
<Snip>
spec:
  containers:
    - name: ctr1
      env:
        - name: FIRSTNAME
          valueFrom:
            configMapKeyRef: <<==== a ConfigMap
              name: multimap <<==== called "multimap"
              key: given
```

Не изменяются после создания.

**В виде аргументов:**

Сначала как переменные окружения, затем в команду создания

```
spec:
containers:
- name: args1
  image: busybox
  env:
    - name: FIRSTNAME <<==== Environment variable called FIRSTNAME
      valueFrom: <<==== based on
        configMapKeyRef: <<==== a ConfigMap
          name: multimap <<==== called "multimap"
          key: given <<==== and populated by the value in the "given" field
    - name: LASTNAME <<==== Environment variable called LASTNAME
      valueFrom: <<==== based on
        configMapKeyRef: <<==== a ConfigMap
          name: multimap <<==== called "multimap"
          key: family <<==== and populated by the value in the "family" field
  command: [ "/bin/sh", "-c", "echo First name $(FIRSTNAME) last name $(LASTNAME)" ]

```

**В виде файлов:**

Создается том и привязывается к нему ConfigMap

```
apiVersion: v1
kind: Pod
metadata:
  name: cmvol
spec:
  volumes:
    - name: volmap <<==== Create a volume called "volmap"
      configMap: <<==== based on the ConfigMap
        name: multimap <<==== called "multimap"
  containers:
    - name: ctr
      image: nginx
      volumeMounts: <<==== These lines mount the
        - name: volmap <<==== the "volmap" volume into the
          mountPath: /etc/name <<==== container at "/etc/name"

```

Файлы будут созданы в соответствии именам в ConfigMap. При обновлении ConfigMap файлы в контейнере обновятся.

**Secrets**

Хранятся в Base64 но внутри контейнера хранятся в tmpfs виде обычного текста.

```
apiVersion: v1
kind: Secret
metadata:
  name: tkb-secret
  labels:
    chapter: configmaps
type: Opaque
data: <<==== Change to "stringData" for plain text
  username: bmlnZWxwb3VsdG9u
  password: UGFzc3dvcmQxMjM=

```

Использование в Pod:

```
apiVersion: v1
kind: Pod
metadata:
  name: secret-pod
  labels:
    topic: secrets
spec:
  volumes:
    - name: secret-vol <<==== Volume name
      secret: <<==== Volume type
        secretName: tkb-secret <<==== Populate volume with this Secret
  containers:
    - name: secret-ctr
      image: nginx
      volumeMounts:
        - name: secret-vol <<==== Mount the volume defined above
          mountPath: "/etc/tkb" <<==== into this path
```

В директории /etc/tkb будут храниться файлы, по одному на каждое значение.

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="width: 100.238%;"><thead><tr><td class="align-center" style="width: 25.2366%;">Команда</td><td class="align-center" style="width: 24.6057%;">Доп. пар.</td><td class="align-center" style="width: 49.8423%;">Описание</td></tr></thead><tbody><tr><td style="width: 25.2366%;">kubectl get cm</td><td style="width: 24.6057%;">  
</td><td style="width: 49.8423%;">Список ConfigMap</td></tr></tbody></table>

</div>

# StatefulSet

Очень похожи на Deployments, но StatefulSet дополнительные функции:

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

Отличие: Deployment создает поды сразу же, а StatefulSet по одному. Это критично для сохранения данных.

**Именование подов:** &lt;StatefulSet name&gt;-&lt;integer&gt;. Число от 0-...

Для каждого пода создается свой том, с соответствующим именованием. Созданные тома имеют свой жизненный цикл и они не удаляются при масштабировании подов.

**Удаление Statefulset:** Автоматического удаления подов нет. Сначала нужно снизить количество до 0. Также нужно использовать terminationGracePeriodSeconds около 10 секунд для безопасного завершения работы.

Создаем headless сервис ([Services](http://bobrobotirk.ru/books/docker-k8s/page/services "Services"))

# Безопасность

**Авторизация и аутентификация**

По умолчанию аутентификация на основе сертификата, но поддерживаются внешние источники.

Аутентификация на основе сертификата.

Авторизация RBAC (пользователь - действие - ресурс). По умолчанию запрещено все что не разрешено. Роли определяют правила, RoleBindings определяют принадлежность пользователей к ролям. Пример настройки ролей:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: shield
  name: read-deployments
rules:
- verbs: ["get", "watch", "list"] <<==== Allowed actions
  apiGroups: ["apps"] <<==== on resources
  resources: ["deployments"] <<==== of this type
```

Пример RoleBinding:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-deployments
  namespace: shield
subjects:
- kind: User
  name: sky <<==== Name of the authenticated user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: read-deployments <<==== This is the Role to bind to the user
  apiGroup: rbac.authorization.k8s.io
```

Свойства правил роли:

verbs \["get", "watch", "list", "create", "update", "patch", "delete"\]

ApiGroups (в пределах namespace):

<table border="1" id="bkmrk-apigroup-%D0%A0%D0%B5%D1%81%D1%83%D1%80%D1%81-%22%22-p" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><thead><tr><td class="align-center">apiGroup</td><td class="align-center">Ресурс</td></tr></thead><tbody><tr><td>""</td><td>pods, secrets</td></tr><tr><td>“storage.k8s.io”</td><td>storageclass</td></tr><tr><td>“apps”</td><td>deployments</td></tr></tbody></table>

Полный список API ресурсов:

```
kubectl api-resources --sort-by name -o wide
```

Можно использовать звездочку.

Все роли используются только в контексте namespace!

**Кластерные роли и привязки**

ClusterRoleBindings используется для создания шаблонов ролей и привязки их к конкретным ролям.

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole <<==== Cluster-scoped role
metadata:
  name: read-deployments
rules:
- verbs: ["get", "watch", "list"]
  apiGroups: ["apps"]
  resources: ["deployments"]

```

**Пользователи**

[Интересная статья](https://habr.com/ru/companies/flant/articles/470503/) [Еще одна, тоже стоит почитать](https://devopstales.github.io/kubernetes/k8s-user-accounts/)

Обычных пользователей нельзя добавить через вызовы API. Возможные варианты:

- Базовая аутентификация *(basic auth)*: 
    - передача конфигурации API-серверу со следующим (или похожим) содержимым: password, username, uid, group;
- Клиентский сертификат X.509: 
    - создание секретного ключа пользователя и запроса на подпись сертификата;
    - заверение его в центре сертификации (Kubernetes CA) для получения сертификата пользователя;
- Bearer-токены (JSON Web Tokens, JWT): 
    - OpenID Connect;
    - слой аутентификации поверх OAuth 2.0;
    - веб-хуки *(webhooks)*.

Структура файла ~/.kube/config

- Clusters - список кластеров. Сертификат кластера, адрес и внутреннее имя
- Users - пользователи. Внутреннее имя, сертификат и ключ
- Contexts - объединение пользователя и кластера. Внутреннее имя, внутреннее имя кластера и внутреннее имя пользователя
- Current-context - имя текущего контекста

---

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

---

**Пример создания пользователя с авторизацией через X.509 сертификат.**

Создаем директорию хранения информации о пользователях и генерируем в нее ключ

```
mkdir -p users/sergey/.certs
openssl genrsa -out ~/users/sergey/.certs/sergey.key 2048
```

Генерируем запрос на сертификат

```
openssl req -new -key ~/users/sergey/.certs/sergey.key -out ~/users/sergey/.certs/sergey.csr -subj "/CN=sergey/O=testgroup"
```

Обработка запроса на сертификат

```
openssl x509 -req -in ~/users/sergey/.certs/sergey.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/users/sergey/.certs/sergey.crt -days 500
```

В некоторых ресурсах говорится, что команда kubectl config set-credentials ... создает пользователя в кластере Kubernetes. Но это не так, команда kubectl config ... создает/модифицирует файл .kube/config, поэтому нужно быть осторожным и не побить свой файл. А Kubernetes авторизует всех пользователей, чей сертификат подписан его центром сертификации.

Добавляем пользователя sergey

```
kubectl config set-credentials sergey \
--client-certificate=/root/users/sergey/.certs/sergey.crt \
--client-key=/root/users/sergey/.certs/sergey.key \
--embed-certs=true
```

Если нужно - создали бы настройки кластера, но у нас есть, поэтому создаем контекст с существующим кластером. Namespace, если нужно, указывается в настройках контекста.

```
kubectl config set-context sergey-context --cluster=kubernetes --user=sergey --namespace=sergey-ns
```

Теперь осталось пользователю определить права.

Например определим роль sergey-ns-full с полным доступом к namespace sergey-ns

```
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: sergey-ns-full
  namespace: sergey-ns
rules:
  - apiGroups: [ "*" ]
    resources: [ "*" ]
    verbs: [ "*" ]
```

Сейчас вместо привязки пользователя, привяжем группу к роли.

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: testgroup-rolebinding  # Название RoleBinding
  namespace: sergey-ns         # Namespace, где применяется
subjects:
- kind: Group                 # Тип субъекта — группа
  name: testgroup             # Название группы
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role                  # Тип привязываемой роли (Role или ClusterRole)
  name: sergey-ns-full        # Название роли
  apiGroup: rbac.authorization.k8s.io
```

Переключаемся на контекст и проверяем

```
kubectl config use-context sergey-context
```

Создаем простой под

```
kind: Pod
apiVersion: v1
metadata:
  name: hello-pod
  labels:
    zone: prod
    version: v1
spec:
  containers:
  - name: hello-ctr
    image: nigelpoulton/k8sbook:1.0
    ports:
    - containerPort: 8080
    resources:
      limits:
        memory: 128Mi
        cpu: 0.5
```

Проверяем факт создания пода

```
kubectl get pods
```

Удалось!

**Основные команды управления пользователями**

<span style="color: rgb(224, 62, 45);">Если при создании ... указать флаг --embed-certs=true то тогда вместо путей к файлам сертификатов, в файл настройки будут встроено содержание сертификатов в Base64.</span>

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki-1" style="width: 100%; height: 524.109px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 33.4923%; height: 29.7969px;">Команда</td><td class="align-center" style="width: 15.9789%; height: 29.7969px;">**Доп. параметры**  
</td><td class="align-center" style="width: 50.5288%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 63.3906px;"><td style="width: 33.4923%; height: 63.3906px;">kubectl get clusterroles.rbac.authorization.k8s.io --all-namespaces</td><td style="width: 15.9789%; height: 63.3906px;">  
</td><td style="width: 50.5288%; height: 63.3906px;">Список пользователей</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config view</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Показать текущую конфигурацию (.kube/config)</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config current-context</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Показать текущий активный контекст</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config get-contexts</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Список всех контекстов</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config use-context cont\_name</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Переключиться на контекст cont\_name</td></tr><tr style="height: 99.359px;"><td style="width: 33.4923%; height: 99.359px;">kubectl config set-cluster clast\_name</td><td style="width: 15.9789%; height: 99.359px;">  
</td><td style="width: 50.5288%; height: 99.359px;">Добавить/изменить кластер ```
kubectl config set-cluster my-new-cluster \
  --server=https://10.0.0.1:6443 \
  --certificate-authority=./ca.crt
```

</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config set-credentials</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Добавить/изменить учетные данные пользователя ```
kubectl config set-credentials sergey \
--client-certificate=/root/users/sergey/.certs/sergey.crt \
--client-key=/root/users/sergey/.certs/sergey.key \
--embed-certs=true
```

</td></tr><tr style="height: 63.3906px;"><td style="width: 33.4923%; height: 63.3906px;">kubectl config set-context cont\_name</td><td style="width: 15.9789%; height: 63.3906px;">--cluster=dev-cluster \\  
--user=dev-user \\  
--namespace=dev-ns</td><td style="width: 50.5288%; height: 63.3906px;">Создать/изменить контекст</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config delete-context</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Удалить контекст</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config delete-cluster </td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Удалить кластер</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config delete-user user\_name</td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Удалить пользователя</td></tr><tr style="height: 29.7969px;"><td style="width: 33.4923%; height: 29.7969px;">kubectl config rename-context </td><td style="width: 15.9789%; height: 29.7969px;">  
</td><td style="width: 50.5288%; height: 29.7969px;">Переименовать контекст</td></tr></tbody></table>

**Безопасность, общая теория**

[Инструменты](https://habr.com/ru/companies/flant/articles/465141/)

**Запрет передачи ключей SA**

Каждому поду по умолчанию передаются ключи сервисный аккаунт. Поэтому при получении доступа к поду можно получить доступ вплоть до всего кластера. Обычно подам не нужно управлять кластером. Поэтому можно запретить передачу ключей.

```
apiVersion: v1
kind: Pod
metadata:
  name: service-account-example-pod
spec:
  serviceAccountName: some-service-account
  automountServiceAccountToken: false <<==== This line
<Snip>
```

Также можно передавать временные ключи, но это потом.

**Контроль целостности ресурсов**

- Ограничьте доступ к серверам, на которых запущены компоненты Kubernetes, особенно к компонентам control plane
- Ограничьте доступ к репозиториям, хранящим конфигурационные файлы Kubernetes
- Передача файлов и управление только через SSH
- Проверка контрольной суммы после скачивания
- Ограничьте доступ к регистру образов и связанным хранилищам

**Файловая система пода в read-only режим**

```
apiVersion: v1
kind: Pod
metadata:
  name: readonly-test
spec:
  securityContext:
    readOnlyRootFilesystem: true <<==== R/O root filesystem
    allowedHostPaths: <<==== Make anything below
      - pathPrefix: "/test" <<==== this mount point
        readOnly: true <<==== read-only (R/O)
<Snip>
```

**Лог действий на кластере и связанной инфраструктуре**

**Защита данных кластера**

Cluster store (обычно etcd) хранит все данные. Необходимо ограничить и контролировать доступ к серверам, на которых работает Cluster store.

**DoS**

Подвергается API сервер. Должно быть минимум 3 Control plane сервера и 3 worker ноды. Изоляция etcd на сетевом уровне. Ограничения ресурсов для подов и количества подов.

```
apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-quota
  namespace: skippy
spec:
  hard:
    pods: "100"
```

Доп. опция podPidsLimit ограничивает количество процессов одним подом. Также можно ограничить кол-во подов на одной ноде.

По умолчанию etcd устанавливается на сервер с control plane. На production кластере нужно разделять.

Запретить сетевое взаимодействие между подами и внешние взаимодействия (где это не нужно) при помощи сетевых политик Kubernetes.

**Защита подов и контейнеров**

Запрет запуска процессов от root

```
apiVersion: v1
kind: Pod
metadata:
  name: demo
spec:
  securityContext: <<==== Applies to all containers in this Pod
    runAsUser: 1000 <<==== Non-root user
  containers:
    - name: demo
      image: example.io/simple:1.0

```

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

```
apiVersion: v1
kind: Pod
metadata:
  name: demo
spec:
  securityContext: <<==== Applies to all containers in this Pod
    runAsUser: 1000 <<==== Non-root user
  containers:
    - name: demo
      image: example.io/simple:1.0
      securityContext:
        runAsUser: 2000 <<==== Overrides the Pod-level setting
```

Рутовые права складываются примерно из 30 capabilities. Простой способ - в тестовом окружении ограничить все и по логам добавлять нужные. Естественно финальное тестирование должно быть максимально всеобъемлющим. Пример разрешений:

```
apiVersion: v1
kind: Pod
metadata:
  name: capability-test
spec:
  containers:
    - name: demo
      image: example.io/simple:1.0
      securityContext:
        capabilities:
          add: ["NET_ADMIN", "CHOWN"]

```

Фильтрация системных вызовов.

Похоже на capabilities, но фильтрует системные вызовы. Способы поиска минимальных разрешений: разрешить все + логгирование, запрет + постепенное разрешение.

Также есть Pod Security Standarts (PSS) и Pod Security Admission (PSA). PSA применяют PSS при старте пода.

**Основные команды**

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%C2%A0k" style="width: 100%; height: 119.188px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 45.1534%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 54.8466%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 45.1534%; height: 29.7969px;"> kubectl describe clusterrole role\_name</td><td style="width: 54.8466%; height: 29.7969px;">Описание роли</td></tr><tr style="height: 29.7969px;"><td style="width: 45.1534%; height: 29.7969px;">kubectl get clusterrolebindings | grep role\_name</td><td style="width: 54.8466%; height: 29.7969px;">Список пользователей с такой ролью</td></tr><tr style="height: 29.7969px;"><td style="width: 45.1534%; height: 29.7969px;">kubectl describe clusterrolebindings role\_name</td><td style="width: 54.8466%; height: 29.7969px;">Информация по сопоставлению</td></tr><tr><td style="width: 45.1534%;">Аналогично для ролей (clusterrolebindings -&gt; rolebindings)</td><td style="width: 54.8466%;">  
</td></tr></tbody></table>

# Job, cronjob

**Job**

Выполнения разовой задачи. Если запуск задачи завершается с ошибкой, Job перезапускает поды до успешного выполнения или до истечения таймаутов. Когда задача выполнена, Job считается завершённым и больше никогда в кластере не запускается.

Параметры в spec:

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-ki-1" style="width: 99.0476%; height: 89.3907px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 25.4658%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 74.5342%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 25.4658%; height: 29.7969px;">activeDeadlineSeconds</td><td style="width: 74.5342%; height: 29.7969px;">количество секунд, которое отводится всему Job (не для одного пода) на выполнение.</td></tr><tr style="height: 29.7969px;"><td style="width: 25.4658%; height: 29.7969px;">backoffLimit</td><td style="width: 74.5342%; height: 29.7969px;">количество попыток. Если указать 2, то Job дважды попробует запустить под и остановится.</td></tr><tr><td style="width: 25.4658%;">ttlSecondsAfterFinished</td><td style="width: 74.5342%;">через сколько секунд специальный TimeToLive контроллер должен удалить завершившийся Job вместе с подами и их логами</td></tr></tbody></table>

После успешного завершения задания манифесты (Job и созданные поды) остаются в кластере навсегда. Все поля Job имеют статус Immutable, и поэтому при создании Job из автоматических сценариев сначала удаляют Job, который остался от предыдущего запуска. Генерация уникальных имен для Job приведет к накоплению ненужных манифестов. Обязательно указание ttlseconds...

При создании бесконечного цикла через activeDeadlineSeconds будет отправлен sigterm, затем через 30 секунд sigkill.

<span style="color: rgb(224, 62, 45);">Если указать backoffLimit без restartPolicy, то при ошибке Job будет выполняться бесконечно.</span>

**Cronjob**

Создание Job по расписанию.

Параметры в spec:

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-sc" style="width: 99.0476%; height: 89.3907px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 25.4658%; height: 29.7969px;">Параметр</td><td class="align-center" style="width: 74.5342%; height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 25.4658%; height: 29.7969px;">schedule</td><td style="width: 74.5342%; height: 29.7969px;">Расписание в виде строчки в cron-формате.</td></tr><tr style="height: 29.7969px;"><td style="width: 25.4658%; height: 29.7969px;">startingDeadlineSeconds</td><td style="width: 74.5342%; height: 29.7969px;">Опциональный. Если по прошествии этого времени job не стартовал, старт отменяется. Желательно вместе с Forbid.</td></tr><tr><td style="width: 25.4658%;">concurrencyPolicy</td><td style="width: 74.5342%;">Одновременное выполнение заданий.

  
Allow позволяет подам следующего задания запускаться. Если задание ежеминутное, за минуту Job не отработал, все равно будет создан ещё один. Одновременно могут выполняться несколько Job’ов. Есть риск перегрузки.

Replace заменяет запущенную нагрузку: старый Job убивается, запускается новый. Не самый лучший вариант, этот вариант осознанно.

  
Forbid запрет запуска новых Job’ов, пока не отработает предыдущий. С этой политикой можно быть уверенным, что всегда запускается только один экземпляр задачи. Используют наиболее часто

</td></tr><tr><td style="width: 25.4658%;">successfulJobsHistoryLimit</td><td style="width: 74.5342%;">Глубина истории хранения удачных job, по умолчанию 3

</td></tr><tr><td style="width: 25.4658%;">failedJobsHistoryLimit</td><td style="width: 74.5342%;">Глубина истории хранения неудачных job, по умолчанию 1

</td></tr></tbody></table>

CronJob использовать аккуратно. Должны быть независимы и иметь возможность работать параллельно. В качестве альтернативы CronJob можно использовать под, в котором запущен самый обычный crond.

**Основные команды**

<div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF.-%D0%BF%D0%B0%D1%80.-%D0%9E%D0%BF"><table border="1" style="width: 100%;"><thead><tr><td class="align-center" style="width: 26.2217%;">Команда</td><td class="align-center" style="width: 19.4417%;">Доп. пар.</td><td class="align-center" style="width: 54.3367%;">Описание</td></tr></thead><tbody><tr><td style="width: 26.2217%;">kubectl get job</td><td style="width: 19.4417%;"> </td><td style="width: 54.3367%;">список job</td></tr><tr><td style="width: 26.2217%;"> </td><td style="width: 19.4417%;">--all-namespaces</td><td style="width: 54.3367%;"> </td></tr><tr><td style="width: 26.2217%;">kubectl delete job jname</td><td style="width: 19.4417%;">  
</td><td style="width: 54.3367%;">Удалить jname</td></tr><tr><td style="width: 26.2217%;">kubectl get cronjobs.batch</td><td style="width: 19.4417%;">  
</td><td style="width: 54.3367%;">Список cronjob</td></tr></tbody></table>

</div>**Примеры**

Job

```
apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  backoffLimit: 2
  activeDeadlineSeconds: 60
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: hello
        image: busybox
        args: 
        - /bin/sh
        - -c
        - date; echo Hello from the Kubernetes cluster
      restartPolicy: Never
```

Cronjob

```
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Allow
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 100
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: Never
```