# Docker & k8s

# Консоль и теория

# Общая информация и установка

<div class="align-center" id="bkmrk-%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0">**Установка**</div><div id="bkmrk-%D0%94%D0%BE%D0%BF.-%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0" style="text-align: justify;">[Доп. ссылка](https://dockerhosting.ru/blog/ustanovka-docker-na-debian-12/)</div><div id="bkmrk-%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB" style="text-align: justify;">Добавить пользователя, который впоследствии будет запускать контейнеры </div>```bash
apt-get install sudo
usermod -aG sudo sergey
sudo apt-get update
```

<div id="bkmrk-%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D1%81%D0%B5%D1%80%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82-" style="text-align: justify;">**Добавить сертификат и новое хранилище**</div><div id="bkmrk-%28%D0%92%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82-1%29" style="text-align: justify;">(Вариант 1 - Debian)</div>```bash
sudo apt-get install ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

(Вариант 2 - Ubuntu)

```bash
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

**Установка docker и docker compose**

(Вариант 1 Debian/Ubuntu)

```bash
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
```

<div id="bkmrk-" style="text-align: justify;"></div><div class="align-left" id="bkmrk-%28%D0%92%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82-2-alt-linux">(Вариант 2 Alt Linux, сертификаты ставить не надо) </div>```
sudo apt-get install -y docker-engine
sudo apt-get install -y docker-compose-v2
```

<div class="align-left" id="bkmrk-%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D1%83%D0%B4%D0%BE%D0%B1%D1%81">**Дополнительные удобства**</div><div class="align-left" id="bkmrk-%D0%97%D0%B0%D0%BF%D1%83%D1%81%D0%BA-docker-%D0%BF%D1%80%D0%B8-%D1%81%D1%82">Запуск docker при старте системы </div>```
sudo systemctl enable docker
```

Добавить пользователя в группу docker

```
sudo usermod -aG docker $USER
```

Информация о текущем статусе docker

```
docker info
```

Для Alt Linux

```
sudo gpasswd -a USER docker
sudo systemctl enable --now docker
sudo init 6
```

<div class="align-center" id="bkmrk-%D0%A2%D0%B5%D0%BE%D1%80%D0%B8%D1%8F">**Теория**</div><div id="bkmrk-%D0%9E%D0%B1%D1%80%D0%B0%D0%B7%D1%8B-%28images%29%3A-%D0%BD%D0%B5%D0%B8" style="text-align: justify;">Образы (Images): неизменяемые элементы, основанные как минимум на ядре и архитектуре. Контейнер: исполняемый образ. Назначение контейнера - запуск одного приложения. Аналогия: Образ=класс, контейнер=объект. Т.е. к образу в момент запуска добавляются параметры и он становится исполняемым контейнером, но изменения в контейнере не затрагивают образ. Контейнер в текущем состоянии можно превратить в образ, но после остановки (если не сохранить данные в файловой системе хоста или еще где-либо) все данные будут потеряны. Повторный запуск будет произведен как будто создался новый объект. В этом принципиальное отличие от виртуальной машины.</div><div class="align-center" id="bkmrk--1">[![docker_sheme.jpg](http://bobrobotirk.ru/uploads/images/gallery/2024-06/scaled-1680-/docker-sheme.jpg)](http://bobrobotirk.ru/uploads/images/gallery/2024-06/docker-sheme.jpg)</div><div id="bkmrk-%D0%9C%D0%BE%D0%B6%D0%B5%D1%82-%D0%B1%D1%8B%D1%82%D1%8C%3A-%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82">Может быть: архитектура ПК-ядро-образ-образ-образ-контейнер</div><div id="bkmrk-%D0%90%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0">**Архитектура**</div><div id="bkmrk-docker-client--%3E-doc">Docker client -&gt; Docker engine (daemon) -&gt; containerd -&gt; runc-&gt; shim</div><div id="bkmrk-runc-%D0%BB%D0%B5%D0%B3%D0%BA%D0%B0%D1%8F-%D0%BE%D0%B1%D0%B5%D1%80%D1%82%D0%BA%D0%B0-" style="text-align: justify;">runc легкая обертка libcontainer, задача - создание контейнеров. Для создании контейнера создается экземпляр runc, он создает контейнер, запускает и завершается. Контейнер передается shim. Shim сопоставляет процесс созданного контейнера с задачей в containerd, containerd логика выполнения контейнера, управления образами, томами, сетью, модульный, можно отключать</div><div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D0%B2%3A">**Пример процессов:**</div><div id="bkmrk-docker-run---name-ct">docker run --name ctr1 -it alpine:latest sh</div>- docker client преобразовывает в POST API запрос в Docker engine
- Запрос отправляется в сокет /var/run/docker.sock
- демон получает команду создания нового контейнера и передает вызов в containerd (CRUD-style API поверх gRPC)
- containerd преобразовывает Docker image и направляет запрос в runc для создания нового контейнера
- runc взаимодействует с ядром, создавая контейнер. Контейнер запускается как дочерний процесс runc. После запуска контейнера runc завершается.

<div id="bkmrk-docker-daemon-%D0%BC%D0%BE%D0%B6%D0%B5%D1%82-">Docker daemon может работать с сетью, non-TLS порт 2375, TLS port 2376</div><div id="bkmrk-%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80%D1%8B%3A">**Контейнеры:**</div><div id="bkmrk-%D0%9F%D0%BE%D1%81%D0%BB%D0%B5-%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%B0-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD">После запуска основным процессом становится приложение из параметров запуска</div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D0%BE%D1%82%D1%81%D1%83%D1%82%D1%81%D1%82%D0%B2%D0%B8%D0%B8%2C-%D0%B8%D1%81%D0%BF%D0%BE">При отсутствии, используется приложение сконфигурированное в образе как точка запуска (Entrypoint)</div><div class="align-center" id="bkmrk-%D0%92%D0%B7%D0%B0%D0%B8%D0%BC%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BC%D0%B5%D0%B6%D0%B4">**Взаимоотношение между способами работы с Docker.**</div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B5-%D1%81-%D0%B4%D0%BE%D0%BA%D0%B5%D1%80%D0%BE%D0%BC" style="text-align: justify;">При работе с докером используются как минимум три способа (возможно больше) взаимодействий: прямое взаимодействие через консоль, dockerfile, docker compose. Терминология общая, но способы работы и результаты отличаются. Из-за того, что в целях упрощения консоли docker (групп команд docker ...) эти способы перемешаны, сначала возникает путаница. В статьях видимо по умолчанию считается, что читатель это отличие полностью осознал. Сравнение способов:</div><div id="bkmrk-%D0%9A%D0%BE%D0%BD%D1%81%D0%BE%D0%BB%D1%8C-dockerfile-d"><table border="1" style="border-collapse: collapse; width: 100%; height: 219.969px;"><colgroup><col style="width: 16.5651%;"></col><col style="width: 26.7007%;"></col><col style="width: 28.8439%;"></col><col style="width: 28.0095%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">  
</td><td class="align-center" style="height: 29.7969px;">Консоль</td><td class="align-center" style="height: 29.7969px;">Dockerfile</td><td class="align-center" style="height: 29.7969px;">docker compose</td></tr></thead><tbody><tr style="height: 80.1875px;"><td style="height: 80.1875px;">**Мое понимание термина**</td><td style="height: 80.1875px;">Ввод команд настроек и запуска одного контейнера напрямую в консоли. Будет работать через bash скрипт, но удобнее так не делать.</td><td style="height: 80.1875px;">Сохранение команд настроек одного контейнера в файл и запуск через консоль.</td><td style="height: 80.1875px;">Настройка взаимодействия нескольких взаимосвязанных контейнеров при условии подготовленных образов. Запуск через консоль / планировщик</td></tr><tr style="height: 80.1875px;"><td style="height: 80.1875px;">**Применение**</td><td style="height: 80.1875px;">При тестовом запуске или для просмотра (корректировки) параметров работающего контейнера</td><td style="height: 80.1875px;">Шаблон одного контейнера. Нужно при создании образа для последующего использования в compose.</td><td style="height: 80.1875px;">Оркестрация образов.</td></tr><tr><td>**Стадия использования**</td><td>Тестовая, до MVP. Либо product для анализа проблем.</td><td>Тестирование, отладка одного образа. MVP - pre product.</td><td>Комплексный запуск сервиса. Pre product - product</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">**Формат**</td><td style="height: 29.7969px;">Bash</td><td style="height: 29.7969px;">ini</td><td style="height: 29.7969px;">YAML</td></tr><tr><td>**Точка использования**</td><td>Преимущественно на ПК разработчика, при обучении технологии или для выявления проблем с работающим контейнером на всех стадиях.</td><td>ПК разработчика или тестовый сервер.</td><td>Тестовый / product сервер.</td></tr><tr><td>**Необходимость серьезного изучения**</td><td>Скорее администраторам. </td><td>Да, если для сервиса потребуется создание отдельных образов.</td><td>Да. Данный способ можно использовать для одного контейнера, для product все равно потребуется.\*</td></tr></tbody></table>

</div><div id="bkmrk-%2A-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%B8%D0%B7-%D1%87%D0%B8%D1%81%D1%82" style="text-align: justify;">* При сборке из чистых образов (типа debian для запуска nginx+...) требуется доп. установка и настройка ПО, а данный вопрос проще решать через dockerfile. К тому же существует например ansible, позволяющий решить ряд вопросов. В книгах для примеров часто используют консоль (быстро, акцент на одном изучаемом аспекте). А через ansible возможно настроить и запустить контейнер (и оркестр) без dockerfile/docker compose. Но в случае ansible для compose используется терминология compose, а вид команд другой.</div><div id="bkmrk-%D0%A0%D0%B5%D0%B7%D1%8E%D0%BC%D0%B5%3A-%D1%8D%D1%82%D0%BE-%D0%BE%D0%B4%D0%BD%D0%B8-%D1%8F%D0%B9%D1%86">**Резюме:** это одни яйца, только с разных концов. Нужно общее понимание происходящего. Хотя формат compose чуть предпочтительнее.</div><div id="bkmrk--2"></div>

# Образы

<div id="bkmrk-%D0%9E%D0%B1%D1%80%D0%B0%D0%B7---%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80-%D1%82%D0%BE">Образ - контейнер только для чтения, содержащий все для запуска (минимальная ОС, приложение, зависимости, метаданные)</div><div id="bkmrk-%D0%9E%D0%B1%D1%80%D0%B0%D0%B7-%D1%81%D0%BE%D1%81%D1%82%D0%BE%D0%B8%D1%82-%D0%B8%D0%B7-%D1%81%D0%BB%D0%BE">Образ состоит из слоев, существующие слои общие для использования и могут быть в разных образах.</div><div id="bkmrk-%D0%9E%D0%B1%D1%80%D0%B0%D0%B7---manifest-%D1%84%D0%B0%D0%B9">Образ - manifest файл, в котором список слоев и метаданных.</div><div id="bkmrk-%D0%A1%D0%BB%D0%BE%D0%B9---%D1%86%D0%B5%D0%BB%D0%BE%D1%81%D1%82%D0%BD%D1%8B%D0%B9-%D0%BE%D0%B1%D1%8A">Слой - целостный объект. </div><div id="bkmrk-%D0%9A%D0%B0%D0%B6%D0%B4%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%B2-doc">Каждая команда в dockerfile, модифицирующая файловую систему контейнера, создает новый слой.</div><div id="bkmrk-%D0%9D%D0%B5%D0%BB%D1%8C%D0%B7%D1%8F-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%BE%D0%B1%D1%80%D0%B0%D0%B7">Нельзя удалить образ, пока последний контейнер не будет удален.</div><div id="bkmrk-%D0%9E%D0%B1%D1%80%D0%B0%D0%B7%D1%8B-%D0%BE%D0%B1%D1%8B%D1%87%D0%BD%D0%BE-%D1%85%D1%80%D0%B0%D0%BD%D1%8F%D1%82">Образы обычно хранятся в /var/lib/docker/&lt;storage-driver&gt;</div><div id="bkmrk-%21%D0%9D%D1%83%D0%B6%D0%BD%D0%BE-%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D1%87%D0%BD%D0%BE-%D0%BC%D0%B5"><span style="color: rgb(224, 62, 45);">!Нужно достаточно места в /var!</span></div><div id="bkmrk-%D0%A3-%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%B0-%D0%B5%D1%81%D1%82%D1%8C-hash-%28">У образа есть hash (digest), у каждого из слоев есть digest</div><div id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-distributio">Параметр distribution hash - hash сжатого образа или образа для размещения в hub</div><div id="bkmrk-%D0%94%D0%BB%D1%8F-%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%BA%D0%B8-%D1%80%D0%B0%D0%B7%D0%BD%D1%8B%D1%85">Для поддержки разных архитектур и платформ для одного названия (напр. golang:latest) используются manifest list и manifests</div><div id="bkmrk-%D0%94%D0%BB%D1%8F-%D0%BA%D0%B0%D0%B6%D0%B4%D0%BE%D0%B3%D0%BE-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5">Для каждого контейнера создается новый слой чтения/записи</div><div class="align-center" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B-%D1%81-%D0%BE%D0%B1%D1%80">**Команды работы с образами**  
</div><div id="bkmrk-%D0%91%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF."><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 17.9983%;"></col><col style="width: 17.1626%;"></col><col style="width: 64.8391%;"></col></colgroup><thead><tr><td class="align-center">Базовая команда</td><td class="align-center">Доп. парам.</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>**docker images**</td><td>  
</td><td>Список установленных образов</td></tr><tr><td>  
</td><td>-q</td><td>только отображение ID контейнеров</td></tr><tr><td>  
</td><td>--digests</td><td>добавляет столбец с хэшем образа</td></tr><tr><td>  
</td><td>dangling: true/false</td><td>Образы без тега. Происходит если при создании нового образа сохраняется тег старого. У старого обнуляется тэг, у нового остается.</td></tr><tr><td>  
</td><td>label: &lt;label&gt;</td><td>Фильтрует на основе наличия метки или ярлыка и значения. Команда docker images не отображает метки в своих выходных данных.</td></tr><tr><td>**docker search**</td><td>&lt;слово&gt;</td><td>поиск в dockerhub по слову &lt;слово&gt;</td></tr><tr><td>  
</td><td>--filter ""</td><td>Доп. фильтр ```bash
docker search alpine --filter "is-official=true"
```

</td></tr><tr><td>  
</td><td>--limit число</td><td>кол-во выдачи, максимум 100</td></tr><tr><td>**docker image pull**

Синоним:

**docker pull**

</td><td>&lt;repository&gt;:&lt;tag&gt;</td><td>Загрузка образа из репозитория

repository:

- В случае официального образа на docker hub: одно слово
- В случае неофициального образа на docker hub: слово/слово ```bash
    docker pull nigelpoulton/tu-demo:v2
    ```
- В случае внешнего хранилища: адрес хранилища без http(s) ```bash
    docker pull gcr.io/google-containers/git-sync:v3.1.5
    ```

</td></tr><tr><td>  
</td><td>&lt;digest&gt;</td><td>Хэш образа</td></tr><tr><td>**docker image push**

Синоним:

**docker push**

</td><td>&lt;repository&gt;:&lt;tag&gt;|&lt;digest&gt;</td><td>Загрузка образа на репозиторий, доп. параметры аналогичны pull</td></tr><tr><td>**docker image prune**</td><td>&lt;repository&gt;:&lt;tag&gt;</td><td>удаление образа из локального хранилища</td></tr><tr><td>  
</td><td>-a</td><td>удаление всех неиспользуемых контейнерами образов</td></tr><tr><td>**docker build**

**docker image build**

</td><td>-t тег образа. обычно имя:версия

&lt;путь к dockerfile&gt; - обязательный

</td><td>Создание образа из dockerfile ```
docker build -t myapp:1.0 .
docker build -t ${PROJECT_NAME}_db:${VERSION} ./database
```

</td></tr><tr><td>**docker save**

</td><td>-o имя\_файла.tar имя\_образа:тег

</td><td>Сохранение существующего образа в файл ```
docker save -o имя_файла.tar имя_образа:тег
```

</td></tr><tr><td>**docker load**

</td><td>-i имя\_файла.tar

</td><td>Загрузка образа из файла ```
docker load -i имя_файла.tar
```

</td></tr><tr><td>**docker image tag**

**docker tag**

</td><td>  
</td><td>Создать тэг TARGET\_IMAGE связанный с SOURCE\_IMAGE Это нужно только для публикации образа в dockerhub ```
docker tag ddd-book:ch8.1 nigelpoulton/ddd-book:ch8.1
```

</td></tr><tr><td>**docker rmi**</td><td>  
</td><td>Удаляет заданный образ или несколько образов.

Удаление всех образов:

```bash
docker rmi $(docker images -q) -f
```

Эта команда удаляет старые теги без удаления образа, если есть еще образы, ссылающиеся на этот образ.

</td></tr><tr><td>**docker inspect &lt;имя или ID образа&gt;**</td><td>  
</td><td>информация по образу

</td></tr><tr><td>**docker info | grep Storage**</td><td>  
</td><td>При использовании docker pull или docker-compose up -d образы размещаются в /var/lib/docker/overlay2

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

</div><div id="bkmrk-"></div>

# Контейнеры

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

<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: 16.5717%;">Доп. пар.</td><td class="align-center" style="width: 58.5177%;">Описание</td></tr></thead><tbody><tr><td style="width: 24.9106%;">**docker ps**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">список работающих контейнеров</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-a</td><td style="width: 58.5177%;">список остановленных но еще существующих контейнеров</td></tr><tr><td style="width: 24.9106%;">**docker logs &lt;name&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">список событий внутри контейнера name</td></tr><tr><td style="width: 24.9106%;">**docker start &lt;name&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">запуск остановленного контейнера</td></tr><tr><td style="width: 24.9106%;">**docker restart &lt;name&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">Перезапускает один или несколько контейнеров. Можно считать приблизительным аналогом выполнения для заданных контейнеров команды docker stop, за которой сразу следует команда docker start.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-t</td><td style="width: 58.5177%;">определяет интервал времени ожидания, необходимого для завершения работы контейнера, перед его остановом по сигналу SIGTERM.</td></tr><tr><td style="width: 24.9106%;">**docker stop &lt;name&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">Останавливает (но не удаляет) один или несколько контейнеров. После выполнения этой команды заданный контейнер переходит в состояние «остановлен».</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-t</td><td style="width: 58.5177%;">Аналогично restart</td></tr><tr><td style="width: 24.9106%;">**docker kill &lt;name&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">Сигнал основному процессу (PID=1) в контейнере. По умолчанию SIGKILL (немедленное завершение работы). Возвращает идентификатор контейнера.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-s</td><td style="width: 58.5177%;">другой сигнал</td></tr><tr><td style="width: 24.9106%;">**docker rm &lt;name или id&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">удаление остановленного контейнера</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-f</td><td style="width: 58.5177%;">позволяет удалять работающие контейнеры.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-v</td><td style="width: 58.5177%;">удалить тома, созданные удаляемым контейнером (если эти тома не смонтированы на каталоги и не используются другими контейнерами)

```bash
docker rm -v $(docker ps -aq -f status=exited)
```

удаляет все остановленные контейнеры

</td></tr><tr><td style="width: 24.9106%;">**docker top &lt;name или id&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">информация о процессах внутри контейнера</td></tr><tr><td style="width: 24.9106%;">**docker port &lt;name или id&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">номера портов, назначенные механизмом Docker</td></tr><tr><td style="width: 24.9106%;">**docker create &lt;name или id&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">Создает контейнер из образа, но не запускает его. Аргументы как у docker run.</td></tr><tr><td style="width: 24.9106%;">**docker run**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">создание и запуск контейнера из образа</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-it или -d</td><td style="width: 58.5177%;">сеанс интерактивной работы или запуск в фоновом режиме</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--link &lt;namecur:nameinnew&gt;</td><td style="width: 58.5177%;">соединение между новым контейнером и существующим контейнером myredis, в новом контейнере ссылка на существующий обозначена именем redis

```bash
--link myredis:redis 
```

</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--name</td><td style="width: 58.5177%;">имя для дальнейшего взаимодействия</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--hostname</td><td style="width: 58.5177%;">имя для обращения</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--mount</td><td style="width: 58.5177%;">монтирование</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--volumes-from CONTAINER</td><td style="width: 58.5177%;">использование томов контейнера</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-p HostPort:ContPort</td><td style="width: 58.5177%;">перенаправление с портов хоста на порт контейнера</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--rm</td><td style="width: 58.5177%;">удаление остановленного контейнера и файловой системы после запуска и выполнения команды. Несовместим с ключом -d.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--restart</td><td style="width: 58.5177%;"><div>Позволяет настроить образ действий при попытке Docker перезапустить остановленный контейнер. </div><div>- no запрещает любые попытки перезапуска контейнера.
- unless-stopped Будет перезагружаться до остановки. После перезагрузки демона Docker контейнер останется выключенным.
- always попытки перезапуска выполняются в любом случае вне зависимости от состояния контейнера после выхода. После перезагрузки демона Docker контейнер опять запустится.
- on-failure: перезапуск выполняются для контейнера, завершившего работу с ненулевым статусом. После перезагрузки демона Docker контейнер опять запустится. Может быть задан аргумент, определяющий максимальное количество попыток перезапуска (иначе попытки будут выполняться бесконечно).

</div>```bash
docker run --restart on-failure:10 postgres
```

</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-t, --tty</td><td style="width: 58.5177%;">Создает псевдоустройство TTY (терминал). Как правило, используется вместе с ключом -i для запуска контейнера в интерактивном режиме.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-e, --env</td><td style="width: 58.5177%;">Определяет переменные среды внутри контейнера. </td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--entrypoint</td><td style="width: 58.5177%;">Определяет точку входа для запускаемого контейнера в соответствии с заданным аргументом, заменяя содержимое любой инструкции ENTRYPOINT из Dockerfile.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-u, --user</td><td style="width: 58.5177%;">Определяет пользователя, от имени которого выполняются команды. Может быть задано как символьное имя пользователя или как числовой идентификатор UID. Заменяет содержимое инструкции USER из Dockerfile.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">-w, --workdir</td><td style="width: 58.5177%;">Устанавливает рабочий каталог в контейнере в соответствии с заданным путевым именем. Заменяет любые значения, определенные в файле Dockerfile.</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 16.5717%;">--add-host=docker:10.180.0.1</td><td style="width: 58.5177%;">ХЗ, узнать</td></tr><tr><td style="width: 24.9106%;">**docker pause/unpause**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">Временно приостанавливает/запускает все процессы внутри контейнера. Процессы не получают никаких сигналов приостановки(не могут быть остановлены и завершены или удалены).</td></tr><tr><td style="width: 24.9106%;">**docker commit &lt;container name&gt; &lt;repo name&gt;**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;"> образ контейнера. По умолчанию приостанавливаются, без приостановки --pause=false. </td></tr><tr><td style="width: 24.9106%;">**docker network ls**</td><td style="width: 16.5717%;">  
</td><td style="width: 58.5177%;">список сетей</td></tr></tbody></table>

</div>**Взаимодействие реального времени**

Ctrl-PQ (или Ctrl+P, Ctrl+Q) отключение от контейнера без остановки

<table border="1" 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-1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.7449%;"></col><col style="width: 15.9833%;"></col><col style="width: 58.2718%;"></col></colgroup><thead><tr><td class="align-center">Команда</td><td class="align-center">Доп. пар.</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>**docker attach &lt;name или id&gt;**</td><td>  
</td><td>наблюдать или взаимодействовать с основным процессом внутри контейнера.</td></tr><tr><td>**docker cp**</td><td>  
</td><td>Позволяет копировать файлы между файловыми системами контейнера и хоста.

```bash
docker cp /tmp/config.ini grafana:/usr/share/grafana/conf/
```

</td></tr><tr><td>**docker exec**</td><td>  
</td><td>Запускает заданную команду внутри контейнера (может быть работающий сейчас). Может использоваться для выполнения задач сопровождения или в качестве замены ssh при входе (регистрации) в контейнер. ```
docker exec -it ubuntu:latest bash
```

</td></tr><tr><td>**docker events**</td><td>  
</td><td>Выводит в реальном времени события от демона демону. Выхода Ctrl-C. </td></tr></tbody></table>

<div class="align-center" id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B">**Примеры**</div>- Запуск контейнера образа ubuntu:latest в интерактивном режиме с bash ```bash
    docker run -it ubuntu:latest /bin/bash 
    ```
- Подключение к работающему контейнеру и запуск в нем bash ```bash
    docker exec -it e37f24dc7e0a bash
    ```

<div id="bkmrk-%C2%A0-1"> </div><div id="bkmrk-">  
</div><div id="bkmrk-%C2%A0-2"></div>

# Тома (volumes)

Тома (volumes) – файлы или каталоги, смонтированные на хосте и не являющиеся частью каскадно-объединенной файловой системы.   
Другие контейнеры могут совместно использовать их, и все изменения будут сразу же фиксироваться в файловой системе хоста.

<span style="background-color: rgb(248, 202, 198);">Устаревшее:</span> В качестве точки монтирования можно определить любой другой каталог хоста в команде docker run (например, docker run -d -v /host/dir:/container/dir test/web-server).

В Dockerfile bind mounts не работает - а это и не надо делать, т к это определяется при старте образа/через compose.

Способы хранения данных:

1. Временное (удаляется при остановке контейнера) 
    1. по умолчанию изолировано на диске
    2. tmpfs в оперативной памяти
2. Постоянное 
    1. обычные тома Docker
    2. bind mounts - прямое монтирование папки в контейнер

**Драйвера volumes**

<table border="1" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80" style="border-collapse: collapse; width: 100%; height: 92.2188px;"><colgroup><col style="width: 26.4601%;"></col><col style="width: 73.5399%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Драйвер</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 32.625px;"><td style="height: 32.625px;">local</td><td style="height: 32.625px;">Драйвер по умолчанию. Только точки монтирования, доступные на хосте. </td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">И еще штук 30 драйверов, [список драйверов](https://docs.docker.com/engine/extend/legacy_plugins/#volume-plugins)</td></tr></tbody></table>

**Управление томами при запуске контейнера из консоли:**

Если тома нет - будет создан

<table border="1" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80-1" style="border-collapse: collapse; width: 100%; height: 398.984px;"><colgroup><col style="width: 22.0479%;"></col><col style="width: 16.8078%;"></col><col style="width: 61.1442%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Основная команда</td><td class="align-center" style="height: 29.7969px;">Параметр</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 103.625px;"><td style="height: 103.625px;">docker run ... --mount</td><td style="height: 103.625px;">type=</td><td style="height: 103.625px;">Тип тома:

- volume
- bind
- tmpfs

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">source(src)=</td><td style="height: 29.7969px;">Имя тома или не указывается для анонимных</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">destination(dst)=</td><td style="height: 29.7969px;">точка монтирования в контейнере</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">volume-driver=</td><td style="height: 29.7969px;">local по умолчанию, локальный том</td></tr><tr style="height: 99.7812px;"><td style="height: 99.7812px;">  
</td><td style="height: 99.7812px;">volume-opt=</td><td style="height: 99.7812px;">опция=значение

```bash
volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">readonly</td><td style="height: 29.7969px;">только для чтения</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">docker run ...

\--volumes-from ContID

</td><td style="height: 46.5938px;">  
</td><td style="height: 46.5938px;">связывание с томами контейнера</td></tr></tbody></table>

Пример:

```bash
--mount 'type=volume,src=data-volume,dst=/var/opt/project,volume-driver=local,readonly'
```

Без пробелов после запятых.  
**Создание и управление томами независимо от контейнеров**

<table border="1" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80-2" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.6925%;"></col><col style="width: 22.8789%;"></col><col style="width: 55.4287%;"></col></colgroup><thead><tr><td class="align-center">Основная команда</td><td class="align-center">Параметр</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>docker volume</td><td>create --name my\_volume

create my\_volume

</td><td>Создание тома.

/var/lib/docker/volumes/имя тома/\_data - расположение файлов

По умолчанию на хосте в каталоге установки Docker (обычно каталог /var/lib/docker/). /var/lib/docker/volumes/

</td></tr><tr><td>  
</td><td>ls</td><td>местоположение определенных томов, по имени или ID тома.</td></tr><tr><td>  
</td><td>inspect my\_volume</td><td>информация о томе</td></tr><tr><td>  
</td><td>rm my\_volume</td><td>удаление тома</td></tr><tr><td>  
</td><td>prune</td><td>удаление всех томов, которыми не пользуются контейнерами. Но иногда после удаления контейнера данные не обновляются</td></tr><tr><td>docker system prune</td><td>  
</td><td>очистка ресурсов docker. Потом - повторное удаление томов.</td></tr></tbody></table>

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B%3A">**Примеры:**</div><div id="bkmrk-%D0%90%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0">Автоматическое создание том с именем test-data в /var/lib/docker/volumes/test-data/_data</div>```bash
docker run -ti -d --name alpine-container -v test-data:/var/lib/app/content alpine
```

<div id="bkmrk-%D0%9C%D0%BE%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B2-%D0%B4%D1%80%D1%83%D0%B3%D1%83">**Монтирование в другую точку.**</div><div id="bkmrk-%D0%9D%D1%83%D0%B6%D0%BD%D0%BE-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C-%D0%B4%D0%B8%D1%80%D0%B5%D0%BA%D1%82">Нужно создать директорию.</div>```bash
mkdir /home/avimanyu/test-data
docker run -ti -d --name alpine-container -v /home/avimanyu/test-data:/var/lib/app/content alpine
```

<div id="bkmrk-%D0%90%D1%80%D1%85%D0%B8%D0%B2%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D1%82%D0%BE%D0%BC-dat">**Архивировать том datavol в dbdata и восстановить его в том dbdata2 контейнера dbstore2**  
Создание тома datavol в контейнере dbdata  
</div>```bash
docker run -it --mount 'type=volume,src=datavol,dst=/datavol' --name dbstore ubuntu /bin/bash
```

<div id="bkmrk-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE-">Создание временного контейнера, примонтирование тома из dbdata, создание тома backup и сохранение архива datavol в backup и удаление временного контейнера  
</div>```bash
docker run --rm --volumes-from dbstore --mount 'type=volume,src=backup,dst=/backup' --name tmpubn ubuntu tar cvf /backup/backup.tar /datavol
```

<div id="bkmrk-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%82%D0%BE%D0%BC%D0%B0-datavo">Создание тома datavol2 в контейнере dbdata2  
</div>```bash
docker run -it --mount 'type=volume,src=datavol2,dst=/datavol' --name dbstore2 ubuntu /bin/bash
```

<div id="bkmrk-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE--1">Создание временного контейнера, примонтирование тома из dbstore2, создание тома backup и сохранение архива datavol в backup и удаление временного контейнера  
</div>```bash
docker run --rm --volumes-from dbstore2 --mount 'type=volume,src=backup,dst=/backup' ubuntu bash -c "cd /datavol && tar xvf /backup/backup.tar --strip 1"
```

# Сеть (networking)

Docker поставляется со следующими сетевыми драйверами в рамках библиотеки libnetwork:

- single-host bridge networks (bridge)
- multi-host overlays (overlay)
- options for plugging into existing VLANs (macvlan)

Для тестов нужно установить

```
apt-get install bridge-utils
```

Docker регистрирует DNS сервис в пределах бриджа. Но в сети по умолчанию (docker0) DNS сервиса нет.

<table border="1" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80" style="border-collapse: collapse; width: 100%; height: 353.953px;"><colgroup><col style="width: 26.4601%;"></col><col style="width: 73.5399%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Команда</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 31.625px;"><td style="height: 31.625px;">brctl show</td><td style="height: 31.625px;">Список бриджей.

```
sudo brctl show
bridge name     bridge id               STP enabled     interfaces
br-62694f46296d         8000.7ee73b5c2894       no
br-8af5ede4ffdc         8000.aee56ab6b984       no
br-96dd8dcd216d         8000.5a64a8825202       no
docker0         8000.f220300c62b1       no
```

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

Также есть опция поиска сервисов и балансировка входной нагрузки.

<table border="1" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80-1" style="border-collapse: collapse; width: 100%; height: 298.391px;"><colgroup><col style="width: 22.0479%;"></col><col style="width: 16.8078%;"></col><col style="width: 61.1442%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Основная команда</td><td class="align-center" style="height: 29.7969px;">Параметр</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 31.625px;"><td style="height: 31.625px;">docker network </td><td style="height: 31.625px;">ls</td><td style="height: 31.625px;">Список сетей</td></tr><tr style="height: 34.7969px;"><td style="height: 34.7969px;">docker inspect ИмяСети</td><td style="height: 34.7969px;"> </td><td style="height: 34.7969px;">Выводит информацию по указанной сети. bridge - сеть по умолчанию.</td></tr><tr style="height: 82.9844px;"><td style="height: 82.9844px;">docker network create </td><td style="height: 82.9844px;">-d драйвер </td><td style="height: 82.9844px;"> Создает сеть

```
docker network create -d bridge localnet
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;"> </td><td style="height: 29.7969px;"> название сети</td><td style="height: 29.7969px;"> </td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;"> docker port ContName</td><td style="height: 29.7969px;"> </td><td style="height: 29.7969px;">Выводит map портов внутрь контейнера </td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">docker network prune</td><td style="height: 29.7969px;"> </td><td style="height: 29.7969px;">Удаляет неиспользуемые сети</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">docker network rm</td><td style="height: 29.7969px;">Название сети</td><td style="height: 29.7969px;">Удаляет конкретную сеть</td></tr></tbody></table>

**Single-host bridge networks**

Создается интерфейс на хосте docker.

[![docker_networks_0.JPG](http://bobrobotirk.ru/uploads/images/gallery/2025-03/scaled-1680-/docker-networks-0.JPG)](http://bobrobotirk.ru/uploads/images/gallery/2025-03/docker-networks-0.JPG)

Один порт может занимать только один контейнер. Взаимодействие контейнеров внутри хоста.

**Multi-host overlay networks**

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

**Plugging into existing VLANs**

Для прямого подключения к сетевому интерфейсу соответственно с независимым адресом. Необходим promiscuous mode на интерфейсе хоста (неразборчивый режим).

ipvlan с возможностью независимого адреса в пределах диапазона сетевой карты хоста заработал

```
services:
  condb:
    image: nginx
    networks:
      my_ipvlan:
        ipv4_address: 192.168.1.40

networks:
  my_ipvlan:
    driver: ipvlan
    driver_opts:
      parent: enp0s3
      ipvlan_mode: l2
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

```

macvlan напрямую не заработал. Из интернетов:

```
sudo ip link add mymacvlan link enp0s3 type macvlan mode bridge
sudo ip addr add 192.168.1.99/24 dev mymacvlan
sudo ip link set mymacvlan up
```

**Docker и firewall**

Тут у меня началась веселуха. Первая проблема в том, что в литературе по докеру сетевая подсистема описана слабо, и не говорится самого главного: один хрен ip/nf tables (для определенности далее будем использовать iptables, помня обо всех нюансах). Docker самостоятельно управляет записями в iptables, за счет них организуется сетевая подсистема. Поэтому

1. Все службы вне докера управляются цепочкой INPUT
2. Службы докера, транслируемые наружу (ports в compose файле) по умолчанию попадают в цепочку FORWARD, которая сначала отдает пакеты в цепочку DOCKER-USER, затем в DOCKER-FORWARD и далее блокируется

Цепочка DOCKER-USER служит для пользовательского управления пакетами. Автоматически самим Docker не изменяется. Поэтому в ней можно блокировать например внешние запросы к внутренним сервисам, оставляя доступ для определенных адресов.

# Yaml формат

\# - комментарии

Ключ-значение

```
first: second
```

Обязательный пробел после :

Вложенные ресурсы в python виде, два пробела

**Типы данных**

Строка. Может быть без кавычек если хотя-бы один нечисловой символ

Многостроковая переменная:

```
config: |
  server.port=8443
  logfile=/var/log
```

Числа - как числа

Логические true/false

Список:

```
ports:
  - 8080
  - 8443
```

Список словарей.

```
env:
  - name: FIRSTVAR
    value: ONE
  - name: SECONDVAR
    value: TWO
```

# Архивация образов и Хранилище образов (hub)

<span style="color: rgb(224, 62, 45);">**Первый в /projects\_ogrinizer/compose-files второй в /projects\_ogrinizer/compose-files/Dockerhub**</span>

**Терминология**

**реестр (registry или hub)** – сервис, отвечающий за хранение и распространение образов.  
**репозиторий (repository)** – набор взаимосвязанных образов, обычно различные версии приложения  
**тег (tag)** – алфавитно-цифровой идентификатор, присваиваемый образам внутри репозитория (например, 14.04 или stable). Тегов может быть много.  
**Пространства имен:**

- начинающиеся с текстовой строки и слеша (/), такие как amouat/revealjs В репозитории Docker Hub это образы, выгруженные конкретным пользователем.
- имена без префиксов и слешей, принадлежат пространству имен «root», управляемому компанией Docker Inc.
- имена с префиксами в виде имени хоста или IP-адреса представляют образы, хранящиеся в сторонних реестрах (не в Docker Hub). Например, localhost:5000/wordpress

Docker сохраняет аутентификационную информацию в файле .dockercfg, расположенном в вашем домашнем каталоге.

**Создание архива образа из существующего образа**

Создаем Dockerfile

```
FROM alpine:latest
ARG NODE_ENV=production2
ENV NODE_ENV=${NODE_ENV}
RUN mkdir /var/www
RUN echo $NODE_ENV > /var/www/first.txt
CMD ["cat", "/var/www/first.txt"]
```

Собираем образ

```
docker build -t bobrobot:1.0 .
```

 Сохраняем образ, копируем и устанавливаем на нужной системе, и запускаем

```
docker save bobrobot:1.0 > bobrobot:1.0.tar
docker load -i bobrobot\:1.0.tar
docker run bobrobot:1.0
```

**Команды**

<table border="1" 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" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.1474%;"></col><col style="width: 12.3973%;"></col><col style="width: 62.4553%;"></col></colgroup><thead><tr><td class="align-center">Команда</td><td class="align-center">Доп. пар.</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>**docker login**</td><td>  
</td><td>Регистрация/вход на сервер реестра. По умолчанию Docker Hub. ```
docker login https://hub.bobrobotirk.ru
```

</td></tr><tr><td>**docker logout**</td><td>  
</td><td>Выход из реестра Docker. По умолчанию Docker Hub.</td></tr><tr><td>**docker pull**</td><td>  
</td><td>Загружает заданный образ из реестра. Реестр определяется по имени образа, по умолчанию принимается Docker Hub.</td></tr><tr><td>**docker push**</td><td>  
</td><td>Выгружает образ или репозиторий в заданный реестр. При отсутствии тега выгружаются все образы указанного репозитория в заданный реестр. ```
docker push hub.bobrobotirk.ru/testimage
```

</td></tr><tr><td>**docker search**</td><td>  
</td><td>Выводит список общедоступных репозиториев из реестра Docker Hub</td></tr><tr><td>**docker build .**</td><td>  
</td><td>  
</td></tr><tr><td>  
</td><td>-t</td><td>Определение имени репозитория и тега</td></tr><tr><td>**docker tag &lt;current&gt; &lt;in hub&gt;**</td><td>  
</td><td>Устанавливается соответствие имени с образом, который ссылается на образ в репозитории Docker Hub. ```
docker tag testimage hub.bobrobotirk.ru/testimage
```

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

**Локальный hub (теория из разных источников, кое-что устаревшее)**

[Пример](https://www.dmosk.ru/miniinstruktions.php?mini=docker-local-repo)

На серверной стороне:

```bash
mkdir dockertest_certs
openssl req -newkey rsa:4096 -nodes -sha256 -subj "/CN=dockertest" -addext "subjectAltName = DNS:dockertest" -keyout dockertest_certs/domain.key -x509 -days 365 -out dockertest_certs/domain.crt
docker run -d -p 5000:5000 -v $(pwd)/dockertest_certs:/certs -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key --restart=always --name registry registry
docker run --entrypoint htpasswd httpd:2 -Bbn testuser testpassword >> auth/htpasswd #добавить пользователя
```

На клиентской стороне (из-под root):

```bash
mkdir -p /etc/docker/certs.d/dockertest:5000
#добавить в /etc/hosts запись о сервере dockertest
scp sergey@dockertest:/home/sergey/dockertest_certs/domain.crt /etc/docker/certs.d/dockertest:5000/ca.crt
service docker restart
```

Перелинковать на клиенте образ на новый хаб и загрузить его

```bash
docker tag amouat/identidock:0.1 dockertest:5000/identidock:0.1
docker push dockertest:5000/identidock:0.1
```

Пример docker-compose.yml для запуска локального реестра

```yaml
services:
 registry:
  restart: always
  image: registry
  ports:
   - "5000:5000"
  environment:
   REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
   REGISTRY_HTTP_TLS_KEY: /certs/domain.key
   REGISTRY_AUTH: htpasswd
   REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
   REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
   - ./dockertest_certs:/certs
   - ./auth:/auth
```

**Практический запуск регистра**

Поскольку это пока что тестовый хаб, решил сделать [сертификат LetsEncrypt](http://bobrobotirk.ru/books/linux/page/sertifikat-ot-letsencrypt "Сертификат от LetsEncrypt"). На dns сервере сделал запись сайта hub.bobrobotirk.ru. Прокинул порты 443 и 8090 до виртуальной машины. Создал папки

```
mkdir data
mkdir auth
mkdir certs
```

В папку certs скопировал сертификат и ключ. Для создания пользователей установил утилиту

```
sudo apt-get install apache2-utils
```

Создал пользователя testuser с паролем StrongPassword

```
htpasswd -Bbn testuser StrongPassword > auth/htpasswd
```

Для добавления пользователей:

```
htpasswd -Bb auth/htpasswd user2 password2
```

Создал docker-compose.yaml

```yaml
services:
  registry-server:
    image: registry:2.8.2
    container_name: registry-server
    restart: always
    ports:
      - "443:5000" # Перенаправление HTTPS-трафика
    environment:
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '["https://hub.bobrobotirk.ru"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '["HEAD", "GET", "OPTIONS", "DELETE"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '["true"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '["Authorization", "Accept", "Cache-Control"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '["Docker-Content-Digest"]'
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/cert.crt
      REGISTRY_HTTP_TLS_KEY: /certs/key.key
      REGISTRY_STORAGE_DELETE_ENABLED: "true"
    volumes:
      - ./data:/var/lib/registry # Для сохранения данных реестра
      - ./auth:/auth                     # Для файла htpasswd
      - ./certs:/certs                  # Для SSL сертификатов

  registry-ui:
    image: joxit/docker-registry-ui:main
    container_name: registry-ui
    restart: always
    ports:
      - "8090:80" # Интерфейс доступен по порту 8090
    environment:
      SINGLE_REGISTRY: "true"
      REGISTRY_TITLE: "BobRobotIRK Docker Registry"
      DELETE_IMAGES: "true"
      SHOW_CONTENT_DIGEST: "true"
      NGINX_PROXY_PASS_URL: "https://hub.bobrobotirk.ru" # URL реестра
      SHOW_CATALOG_NB_TAGS: "true"
      CATALOG_MIN_BRANCHES: "1"
      CATALOG_MAX_BRANCHES: "1"
      TAGLIST_PAGE_SIZE: "100"
      REGISTRY_SECURED: "true" # Реестр защищен авторизацией
      CATALOG_ELEMENTS_LIMIT: "1000"
    depends_on:
      - registry-server
```

И по адресу [http://hub.bobrobotirk.ru:8090/](http://hub.bobrobotirk.ru:8090/) появился web интерфейс с авторизацией.

Авторизовался с другого сервера

```
docker login https://hub.bobrobotirk.ru
```

Создал Dockerfile

```
FROM alpine:latest
ARG NODE_ENV=production2
ENV NODE_ENV=${NODE_ENV}
RUN mkdir /var/www
RUN echo $NODE_ENV > /var/www/first.txt
CMD ["cat", "/var/www/first.txt"]
```

Собрал образ (пора переходить на buildx)

```
docker build -t testimage .
```

Точка обозначает расположение Dockerfile)

Он появился в списке образов

```
docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
testimage    latest    1f3755f48bea   3 minutes ago   7.83MB
```

Добавил тег образу

```
docker tag testimage hub.bobrobotirk.ru/testimage
```

Загрузил образ на hub

```
docker push hub.bobrobotirk.ru/testimage
```

Да, образ отобразился в списке. Теперь на третьем сервере авторизовался на hub.bobrobotirk.ru и попробовал запустить контейнер из образа

```
services:
  testapp:
    image: hub.bobrobotirk.ru/testimage
```

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

P.s. Второй вариант, в случае наличия внешнего nginx reverse proxy + https termination

```yaml
services:
  http-registry-server:
    image: registry:2.8.2
    container_name: http-registry-server
    restart: no
    ports:
      - "${HUB_BACKEND_PORT}:5000" # Перенаправление HTTP-трафика
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_STORAGE_DELETE_ENABLED: "true"
      REGISTRY_HTTP_ADDR: 0.0.0.0:5000
      REGISTRY_HTTP_NET: "tcp"  # Явно указываем протокол
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '["*"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '["HEAD", "GET", "OPTIONS", "DELETE"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '["true"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '["Authorization", "Accept", "Cache-Control"]'
      REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '["Docker-Content-Digest"]'
    volumes:
      - ./data:/var/lib/registry # Для сохранения данных реестра
      - ./auth:/auth                     # Для файла htpasswd

  http-registry-ui:
    image: joxit/docker-registry-ui:main
    container_name: http-registry-ui
    restart: no
    ports:
      - "${HUB_BACKEND_WEB_PORT}:80"
    environment:
      NGINX_PROXY_PASS_URL: "http://${HUB_BACKEND_IP}:${HUB_BACKEND_PORT}"
      SINGLE_REGISTRY: "true"
      REGISTRY_TITLE: "${WEB_TITLE}"
      DELETE_IMAGES: "true"
      SHOW_CONTENT_DIGEST: "true"
      SHOW_CATALOG_NB_TAGS: "true"
      CATALOG_MIN_BRANCHES: "1"
      CATALOG_MAX_BRANCHES: "1"
      TAGLIST_PAGE_SIZE: "100"
      REGISTRY_SECURED: "true" # Реестр защищен авторизацией
      CATALOG_ELEMENTS_LIMIT: "1000"
    depends_on:
      - http-registry-server
```

**Примеры**

Загрузка образа с тегом latest в репозиторий amouat/revealjs из реестра Docker Hub.

```bash
docker pull amouat/revealjs:latest 
```

Загрузка образа из неофициального реестра.

```bash
docker pull gcr.io/google-containers/git-sync:v3.1.5
```

Определение имени репозитория identidock и тега 0.1 для образа, собранного из локального dockerfile.

```bash
docker build -t "identidock:0.1" .
```

Устанавливается соответствие имени amouat/identidock с образом, который ссылается на имя пользователя amouat в репозитории Docker Hub.

```bash
docker tag "identidock:0.1" "amouat/identidock:0.1"
```

**Ссылки**

[Настройка локального hub](https://www.dmosk.ru/miniinstruktions.php?mini=docker-local-repo)

# Dockerfile

<div id="bkmrk-dockerfile---%D0%BD%D0%B0%D0%B1%D0%BE%D1%80-%D0%B8">DockerFile - набор инструкций по установке и настройке контейнера. Также используется для создания преднастроенных образов. Обычно dockerfile расположен в директории, из которой вызывается команда. </div><div id="bkmrk-%D0%98%D0%BC%D1%8F-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%3A-dockerfil">Имя файла: Dockerfile (без расширения).</div><div id="bkmrk-%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D1%8F---">Контекст создания - набор локальных файлов/каталогов, к которым можно обращаться через copy/add. </div><div id="bkmrk-%2F-%D0%BD%D0%B5-%D1%81%D1%82%D0%BE%D0%B8%D1%82-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2">/ не стоит использовать как контекст, полностью включается в архив</div><div id="bkmrk-%23-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%80%D0%B8%D0%B8">\# комментарии</div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%B0-%D0%BF%D0%BE">При сборке образа попытки использования кэша идут до первого промаха. Затем кэш не проверяется.</div><div id="bkmrk-.dockerignore---%D1%84%D0%B0%D0%B9%D0%BB">.dockerignore - файл исключений из контекста</div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D0%B8%D1%80%D1%83">При сборке компилируемых приложений лучше использовать multi-stage build.</div><div id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.9511%;"></col><col style="width: 30.7509%;"></col><col style="width: 50.298%;"></col></colgroup><thead><tr><td class="align-center">Основная команда</td><td class="align-center">Параметры</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>docker build</td><td> </td><td>создание образа

Создание образа из docker файла . - контекст создания (текущая папка)

```bash
docker build -t test/cowsay-dockerfile .
```

После этого образ появляется в списке образов

</td></tr><tr><td>  
</td><td>-t</td><td>имя слоя</td></tr><tr><td>  
</td><td>-f</td><td>расположение слоя</td></tr><tr><td>  
</td><td>--squash</td><td>сжимает все слои в один</td></tr><tr><td>docker history</td><td>&lt;image&gt;</td><td>инструкции создания образа</td></tr><tr><td>docker buildx</td><td>  
</td><td>для компиляции под разные платформы</td></tr></tbody></table>

</div><div class="align-center" id="bkmrk-%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82-exec-%D0%B8-%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82">**Формат exec и формат командной оболочки**</div><div id="bkmrk-%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82%C2%A0-exec">**Формат exec**</div><div id="bkmrk-json-%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2">JSON-массив</div><div id="bkmrk-%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9-%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82-%D0%BC%D0%B0%D1%81%D1%81%D0%B8">первый элемент массива: имя выполняемого файла</div><div id="bkmrk-%D0%BE%D1%81%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5---%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80">остальные - параметры, передаваемые при запуске. </div><div id="bkmrk-%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82%C2%A0-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%BD%D0%BE%D0%B9-%D0%BE%D0%B1">**Формат командной оболочки** </div><div id="bkmrk-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B0-%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9-">строка произвольной формы, передаваемая для интерпретации в /bin/sh -c. </div><div id="bkmrk-%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9%D1%82%D0%B5-%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82-e">Используйте формат exec, чтобы избежать случайного искажения строк командной оболочки, или в тех случаях, когда образ не содержит /bin/sh.</div><div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%3A">Команды файла:</div><div id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80-1"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 17.1633%;"></col><col style="width: 15.4985%;"></col><col style="width: 67.3382%;"></col></colgroup><thead><tr><td class="align-center">Основная команда</td><td class="align-center">Параметры</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>FROM</td><td>&lt;name&gt;</td><td>название базового образа</td></tr><tr><td>MAINTAINER</td><td>&lt;name&gt;</td><td>имя поддерживающего пользователя</td></tr><tr><td>USER</td><td>&lt;name&gt;</td><td>!Всегда определять! Задает пользователя (по имени или по идентификатору UID) для использования во всех последующих инструкциях RUN, CMD, ENTRYPOINT.</td></tr><tr><td>WORKDIR </td><td>  
</td><td><div>рабочий каталог для последующих RUN, CMD, ENTRYPOINT, ADD, COPY. </div><div>можно использовать несколько раз </div><div>можно относительные пути, итоговый путь относительно предыдущего WORKDIR.</div></td></tr><tr><td>RUN</td><td>&lt;commands&gt;</td><td><div>команды при инициализации образа, обычно установка пакетов</div><div>каждая создает новый слой</div></td></tr><tr><td>CMD {''}</td><td>  
</td><td><div>команда с аргументами, выполняемая после запуска контейнера. Аргументы могут быть переопределены при запуске контейнера. В файле может присутствовать лишь одна инструкция CMD.</div></td></tr><tr><td>ENTRYPOINT</td><td>&lt;program name&gt;</td><td><div>Выполняемый файл, который будет вызываться для обработки аргументов, переданных в команду docker run </div>```
ENTRYPOINT ["/usr/games/cowsay", "-k"]
```

</td></tr><tr><td>COPY</td><td>&lt;&gt; &lt;&gt;</td><td><div>копирование файла из ФС ОС в ФС образа COPY . /src копирует все из текущей папки в папку /src</div></td></tr><tr><td>VOLUME</td><td>&lt;&gt;</td><td><div>Том в ФС</div></td></tr><tr><td>ARG</td><td>  
</td><td><div>Определяет переменные среды, доступные внутри образа при сборке. Неизменяемые.</div></td></tr><tr><td>ENV</td><td>  
</td><td><div>Определяет переменные среды внутри образа, но могут изменяться. На эти переменные можно ссылаться в последующих инструкциях.*</div>```yaml
ENV MY_VERSION 1.3
RUN apt-get install -y mypackage=$MY_VERSION
```

Переменные среды со значением по умолчанию

Файл first.sh

```
echo $NODE_ENV
```

Dockerfile

```yaml
FROM alpine:latest
ARG NODE_ENV=production2
ENV NODE_ENV=${NODE_ENV}
RUN mkdir /var/www
COPY first.sh /var/www/first.sh
CMD ["sh", "/var/www/first.sh"]
```

Собираем образ

```
docker build -t bobrobot:1.0 .
```

docker-compose.yml

```
services:
  bobrobot:
    image: bobrobot:1.0
    environment:
      NODE_ENV: "first2"
```

В итоге при указании NODE\_ENV выводится first2, иначе production2

</td></tr><tr><td>EXPOSE</td><td>  
</td><td><div><div>Сообщает механизму Docker, что в контейнере будет процесс, прослушивающий порт(ы)</div><div>не оказывает воздействия на сетевую среду</div><div>нужно для аргумента -p в docker run</div></div></td></tr><tr><td>ONBUILD</td><td>  
</td><td><div><div><div>инструкция, выполняемая когда образ будет использоваться как основной уровень для другого образа. Полезным при обработке данных, добавляемых в образ-потомок </div><div>(например, инструкция копирования дополнительного кода из заданного каталога и запуска скрипта сборки, обрабатывающего скопированные данные).</div></div></div></td></tr></tbody></table>

</div><div id="bkmrk-%2A-%D0%94%D0%BB%D1%8F-%D1%80%D0%B0%D0%B7%D0%BB%D0%B8%D1%87%D0%BD%D1%8B%D1%85-%D0%BE%D0%B1%D1%80%D0%B0">* Для различных образов могут использоваться доп. переменные, иногда обязательные. Детали нужно уточнять для конкретного образа.</div><div id="bkmrk-"></div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-%D0%B8">При сборке команды исполняются однократно. Затем при запуске образа они не повторяют исполнение. Поэтому для </div>```
FROM alpine:latest
ARG NODE_ENV=production2
ENV NODE_ENV=${NODE_ENV}
RUN mkdir /var/www
RUN echo $NODE_ENV > /var/www/first.txt
CMD ["cat", "/var/www/first.txt"]
```

<div id="bkmrk-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4-%D0%B2%D1%81%D0%B5%D0%B3%D0%B4%D0%B0-%D0%B1%D1%83%D0%B4%D0%B5%D1%82-p">вывод всегда будет production2.</div><div class="align-center" id="bkmrk-%D0%9C%D0%BD%D0%BE%D0%B3%D0%BE%D1%88%D0%B0%D0%B3%D0%BE%D0%B2%D0%B0%D1%8F-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B0-">**Многошаговая сборка (Multi-stage building)**</div><div id="bkmrk-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BE%D0%B1%D1%80%D0%B0">**Создание одного образа с участием нескольких временных**</div><div id="bkmrk-%D0%9D%D0%B5%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B8%D0%BC%D0%B0-%D0%B5%D1%81%D0%BB%D0%B8-%D0%BD%D1%83%D0%B6%D0%BD">Необходима если нужно при помощи временного образа скомпилировать исходники и результат сохранить в финальный образ. Временный образ удаляется. </div>```
FROM golang:1.20-alpine AS base
WORKDIR /src
COPY go.mod go.sum .
RUN go mod download
COPY . .

FROM base AS build-client
RUN go build -o /bin/client ./cmd/client

FROM base AS build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch AS prod
COPY --from=build-client /bin/client /bin/
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]
```

<div id="bkmrk--1"></div><div id="bkmrk--2"></div>Сборка образа:

```
docker build -t multi:stage .
```

**Создание нескольких образов**

Также можно создать несколько образов при помощи одного Dockerfile. Отличие в последней стадии:

```
...
FROM scratch AS prod-client
COPY --from=build-client /bin/client /bin/
ENTRYPOINT [ "/bin/client" ]

FROM scratch AS prod-server
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]
```

Сборка образов:

```
docker build -t multi:client --target prod-client -f Dockerfile-final .
docker build -t multi:server --target prod-server -f Dockerfile-final .
```

**Мультиплатформенная сборка**

Возможно, но пока не интересно

**Использование условий для создания разных образов**

Условия можно использовать только в конструкции RUN и CMD

```yaml
RUN if [ "$TARGET" = "test" ]; then \
      cp -r /files/src/tests .; \
    fi

# Разные команды для production и тестов
CMD if [ "$TARGET" = "production" ]; then \
      exec gunicorn app.main:app --bind 0.0.0.0:8000; \
    else \
      exec pytest -v; \
    fi
```

Затем собирается образ с указанной переменной окружения

```
docker build --build-arg TARGET=production -t myapp:prod .
```

Полный пример:

```
FROM alpine:latest
ARG BUILD_ENV
RUN mkdir /var/www
WORKDIR /var/www
COPY test.txt .
COPY prod.txt .
RUN if [ "$BUILD_ENV" = "test" ]; then \
       cat test.txt > first.txt; \
    else \
       cat prod.txt > first.txt; \
    fi
CMD ["cat", "/var/www/first.txt"]
```

**Использование multistage сборки для ограничения копирования**

if не работает в команде COPY. Но иногда бывает нужен образ для тестирования без содержимого директории, а в prod с содержимым. Пример:

```
# Общая стадия для подготовки файлов
FROM alpine:latest as base
COPY common_files /app/common_files

# Стадия для prod (копирует папку)
FROM base as prod
COPY prod_folder /app/prod_folder

# Стадия для test (не копирует папку)
FROM base as test
RUN echo "Running in test mode, no prod_folder copied"

# Выбираем финальную стадию через ARG
ARG TARGET_ENV=prod
FROM ${TARGET_ENV} as final

# Остальные инструкции...
```

**Сжатие итогового образа (многоуровневого) в один уровень**

Не рекомендуется, но если надо: --squash флаг при build

**Разное**

При apt-get install использовать no-install-recommends - сильно снижает объем.

# Docker compose

Инструмент для управления несколькими контейнерами при помощи одного файла.

**Настройка прав на директорию**

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

```
sudo groupadd docker-users
sudo usermod -aG docker-users user1
sudo usermod -aG docker-users user2
```

Создаем директорию проекта

```
sudo mkdir /var/dockerimages 
sudo chown root:docker-users /var/dockerimages       # Владелец — root, группа — docker-users
sudo chmod 775 /var/dockerimages                    # Права: rwxrwxr-x
```

Права на создаваемые директории

```
sudo chmod g+s /var/dockerimages
```

Добавление пользователей в группу Docker. В этом случае пользователи получат полный доступ к Docker.

```
sudo usermod -aG docker user1 user2 
```

**Политики перезапуска**

Применение compose файла с политикой restart always/unless-stopped нужно быть внимательным. Повторное применение может создать копию, которая будет постоянно перезагружаться и забивать ресурсы. Для диагностики docker ps Если такое произошло, то

```
docker update --restart=no 1945fa12ce27
```

Это обновит политику перезагрузки для контейнера и позволит разобраться в проблеме.

**Консольные команды:**

<table border="1" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%94%D0%BE%D0%BF" style="border-collapse: collapse; width: 100%; height: 208.578px;"><colgroup><col style="width: 20.9754%;"></col><col style="width: 20.0258%;"></col><col style="width: 58.9988%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Основная команда</td><td class="align-center" style="height: 29.7969px;">Доп. параметры</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">docker compose up</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">Запуск контейнеров через Compose файл.

Вывод журнальных записей объединяется в один поток.

Создает образы, если они не существовали ранее

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">-d</td><td style="height: 29.7969px;">запуск в фоновом режиме</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">-f</td><td style="height: 29.7969px;">определяет имя compose файла</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">&amp; в конце</td><td style="height: 29.7969px;">возвращает консоль, но продолжает выводить логи</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">docker compose build</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">Пересоздание образов из Dockerfile.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">docker compose ps</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">Вывод информации о состоянии контейнеров. Работает только в контексте файла в текущей директории. </td></tr><tr><td>docker compose run</td><td>  
</td><td>Одноразовый запуск с выполнением одной команды (не в качестве сервиса).

Также запускаются все контейнеры, с которыми должны быть установлены соединения, если не задан аргумент --no-deps.

(Команды, передаваемые через run, заменяют команды, определенные в файле конфигурации сервиса. Кроме того, по умолчанию команда run не создает портов, определенных в файле конфигурации сервиса.)

</td></tr><tr><td>docker compose top</td><td>  
</td><td>процессы во всех контейнерах

</td></tr><tr><td>docker compose stop</td><td>  
</td><td>Останов контейнеров без их удаления

</td></tr><tr><td>docker compose logs</td><td>  
</td><td>Вывод журнальных записей с цветной подсветкой, объединенный для всех контейнеров

</td></tr><tr><td>docker compose restart</td><td>  
</td><td>запускает остановленные контейнеры

</td></tr><tr><td>docker compose rm</td><td>  
</td><td>Удаление остановленных контейнеров.

</td></tr><tr><td>  
</td><td>-v</td><td>удаляет тома, управляемые механизмом Docker

</td></tr><tr><td>docker compose down</td><td>  
</td><td>это stop+rm

</td></tr><tr><td>  
</td><td>--volumes</td><td>удаляет и тома

</td></tr><tr><td>  
</td><td>--rmi all</td><td>удаляет образы

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

**Формат файла docker-compose.yaml**

Имя файла docker-compose.yml  
Язык yaml. Уровни вложенности определяются пробелами, параметр:значение, - список  
Compose перекрывает настройки docker файла при пересечении.  
Если микросервисы в одной сети (networks), то могут взаимодействовать по имени.  
\# комментарий, пустые строки игнорируются  
сначала создается сеть и тома, затем сервисы.

**Блоки конфигурации:**

```yaml
services #настройки запускаемых образов 
	firstapp:
		build: . #Расположение dockerfile, в данном случае в текущей папке
		command: python app.py #исполняемая комманда (точка входа)
		hostname: your-name
		ports:
			- target: 8080 #внутренний порт 
			  published: 5001 #внешний порт
		environment:
			- DEBUG=${DEBUG}
		networks:
			- counter-net #название подключенной сети, т е может быть разные подключенные сети. Подключенные к одной сети контейнеры видят друг друга по имени. Д б определена в networks
		volumes:
			- type: volume
			source: counter-vol #Д б определена в volumes
			target: /app
networks #Сеть
	counter-net:
		over-net: #имя сети
		driver: overlay	#драйвер
		attachable: true #разрешено подключаться из других докер контейнеров
volumes #Тома
```

**Внешние переменные окружения**

По умолчанию файл .env Формат файла:

```
VERSION=v1.3
PG_USERNAME="superadmin"
```

Кавычки:

- Тип кавычек после = определяет внешние кавычки, которые убираются.
- Внутри строки не может быть внешних неэкранированных кавычек
- Одинарные не разыменовывают $ (и скорее всего остальные спец. символы), двойные разыменовывают
- Одинарные поддерживают multiline

Использование внутри yml файла (без доступа во время выполнения):

```yaml
services:
  web:
    image: webapp:${VERSION}
```

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

```yaml
services:
  db:
    image: postgres
    environment:
      POSTGRES_USER: ${PG_USERNAME}
```

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

```
docker compose --env-file <file> up
```

Предварительный вывод итогового yml файла:

```
docker compose config
```

Приоритет переменных (от высокого к низкому):

1. Файл Compose.
2. Переменные среды оболочки.
3. Файл среды.
4. Dockerfile.
5. Переменная не определена.

**Именование контейнеров.**

В случае использования image, имя контейнера берется из имени сервиса. В случае build &lt;имя проекта&gt;-&lt;имя сервиса&gt; Переменные в имени сервиса не поддерживаются.

**Политика перезагрузки контейнера**

- always - всегда
- uness-stopped - если контейнер был остановлен, затем перезагрузилась служба Docker - он не будет запущен. При перезагрузке работающего контейнера с unless-stopped будет запущен.
- on-failure - перезагрузка при аварийном завершении работы. Контейнер запускается если был остановлен и служба Docker была перезапущена.

```
services:
  postgres:
    image: ${PROJECT_NAME}_db:${VERSION}
    restart: always
```

# Docker Swarm

Кластеризация приложений от Docker, упрощенный K8s.

Типы Nodes (хост Docker в Swarm кластере):

- manager: управление состоянием кластера и распределение задач по workers
- workers: получают и выполняют задачи

Конфигурация и состояние кластера хранится в распределенной хранимой в ОП БД, реплицированной по всем manager. Сервис (service) является атомарным элементом для управления. Сверху накручиваются фишки типа <span class="EzKURWReUAB5oZgtQNkl" data-src-align="17:7">масштабирования</span><span class="EzKURWReUAB5oZgtQNkl" data-src-align="24:1">,</span> постоянного <span class="EzKURWReUAB5oZgtQNkl" data-src-align="34:7">обновления</span> <span class="EzKURWReUAB5oZgtQNkl" data-src-align="43:3">и</span> восстановления.

Для Node желательно настройка dns имен (в моем случае manager1, manager2, worker1, worker2)

Роли можно совмещать на одном VPS, актуально для маленьких кластеров.

**Инициализация кластера**

Инициализация первого manager - добавление остальных manager - добавление worker. Инициализация первого manager:

```
docker swarm init
```

Затем при помощи команд ... join-token смотрим инструкцию по подключению node.

При размещении где-то, должны быть доступны следующие порты:

- 2377/tcp: для защищенного взаимодействия между нодами
- 7946/tcp and udp: взаимодействие менеджеров
- 4789/udp: VXLAN-based overlay сеть

**Доступность кластера**

Один manager активен в каждый момент времени. Это Leader manager. Остальные (Fallower managers) проксируют команды на лидера. Ситуации split-brain (в результате сбоя сети при котором одинаковое количество manager осталось в каждом сегменте и последующего восстановления связи) необходимо избегать. Желательно 3-5 manager.

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

```
docker swarm update --autolock=true
```

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

```
docker swarm unlock
```

Есть нюанс: для работы кластера требуется доступность более половины manager. Поэтому кластер из 2 manager точно плохой вариант)

**Сервисы (пользовательские приложения)**

По умолчанию исполняются на всех нодах. Для исключения manager нужно ввести команду

```
docker node update --availability drain mgr1
```

В списке node Active поменяется на Drain

```
ID                            HOSTNAME                  STATUS    AVAILABILITY   MANAGER STATUS   ENGINE 
m1bhltzuvvzippoetl7b26m3j     manager1.bobrobotirk.ru   Ready     Drain          Leader           28.0.1
yjmubzqzvrvbhqaqw70rlorzk *   manager2.bobrobotirk.ru   Ready     Drain          Reachable        28.0.1
3s0jzf6fcw9ik5g9sqlrpmmgo     worker1.bobrobotirk.ru    Ready     Active                          28.0.1
```

Сервисы создаются через интерактивные команды или через описание (compose file + доп. настройки).

Стандартный режим создания - реплика (количество, распределено по активным worker). Есть глобальный режим (mode global) при котором на каждом worker создается по одной реплике.

**Архивация и восстановление**

Ключ разблокировки очень важен. При его утере и перезагрузке всех manager node хер что сделаешь.

```
tar -czvf swarm.bkp /var/lib/docker/swarm/

rm -r /var/lib/docker/swarm
tar -zxvf swarm.bkp -C /
docker swarm init --force-new-cluster
```

После восстановления директорий обязательна реинициализация кластера.

**Примеры**

5 реплик, доступ через любую ноду

```
docker service create --name web-fe -p 8080:8080 --replicas 5 nigelpoulton/ddd-book:web0.1
или
docker service create --name web-fe -p 8080:8080 --mode global nigelpoulton/ddd-book:web0.1
docker service rm web-fe
```

Через overlay сеть + обновление

```
docker network create -d overlay uber-net
docker service create --name web-fe --network uber-net -p 8080:8080 --replicas 5 nigelpoulton/ddd-book:web0.1
#без указания сети, запрос на manager:8080 обрабатываться не будет

docker service update --image nigelpoulton/ddd-book:web0.2 --update-parallelism 2 --update-delay 20s web-fe
```

Compose файл

```
networks:
  counter-net:
    driver: overlay
    driver_opts:
      encrypted: 'yes'
volumes:
  counter-vol:

services:
  web-fe:
    image: nigelpoulton/ddd-book:swarm-app
    mem_limit: 250m
    command: python app.py
    deploy:
      replicas: 4
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
      placement:
        constraints:
          - 'node.role == worker'
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
    networks:
      - counter-net
    ports:
      - published: 5001
        target: 8080
    volumes:
      - type: volume
        source: counter-vol
        target: /app
  redis:
    image: "redis:alpine"
    networks:
      counter-net:
```

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

<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%;">docker swam init</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Инициализация первого manager кластера</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">--advertise-addr 10.0.0.1:2377</td><td style="width: 53.6411%;">Необязательный параметр. Нужен если есть внешний балансировщик нагрузки - тогда адрес балансировщика, и указать listen-addr. </td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">--listen-addr 10.0.0.1:2377</td><td style="width: 53.6411%;">Необязательный параметр. Нужен если много ip адресов. </td></tr><tr><td style="width: 24.9106%;">docker node ls</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Список node в кластере</td></tr><tr><td style="width: 24.9106%;">docker swarm join-token </td><td style="width: 21.4483%;">worker</td><td style="width: 53.6411%;">Инструкция по подключению worker</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">manager</td><td style="width: 53.6411%;">Инструкция по подключению manager</td></tr><tr><td style="width: 24.9106%;">docker swarm leave</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Исключение node из кластера</td></tr><tr><td style="width: 24.9106%;">docker swarm update </td><td style="width: 21.4483%;">--autolock=true</td><td style="width: 53.6411%;">Блокировка после перезагрузки / потере связи</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">--availability drain name\_manager</td><td style="width: 53.6411%;">Исключение manager из исполнения клиентских приложений.</td></tr><tr><td style="width: 24.9106%;">docker swarm unlock</td><td style="width: 21.4483%;">  
</td><td style="width: 53.6411%;">Разблокировка manager</td></tr><tr><td style="width: 24.9106%;">docker service</td><td style="width: 21.4483%;">ls</td><td style="width: 53.6411%;">список сервисов</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">ps name</td><td style="width: 53.6411%;">список работающих контейнеров с именем name</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">inspect --pretty name</td><td style="width: 53.6411%;">детальная информация по сервису name</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">scale name=10</td><td style="width: 53.6411%;">Изменение количества реплик сервиса name в режиме реального времени</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">rm name</td><td style="width: 53.6411%;">Удалить сервис</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">update

\--image imname

\--update-parallelism 2

\--update-delay 20s

name

</td><td style="width: 53.6411%;">Обновление сервиса name до образа imname

</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">logs nameserv

</td><td style="width: 53.6411%;">Отобразить логи сервиса nameserv

</td></tr><tr><td style="width: 24.9106%;">docker stack </td><td style="width: 21.4483%;">deploy -c name.yml nameofstack

</td><td style="width: 53.6411%;">Создает стэк nameofstack из файла name.yml

```
docker stack deploy -c compose.yaml ddd
```

Также используется для обновления существующего сервиса при обновлении compose файла

</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">rm nameofstack

</td><td style="width: 53.6411%;">Удаление стэка nameofstack

</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">ls

</td><td style="width: 53.6411%;">Список

</td></tr><tr><td style="width: 24.9106%;">  
</td><td style="width: 21.4483%;">ps

</td><td style="width: 53.6411%;">Детализация

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

</div>

# Дополнительные инструменты

<div class="align-center" id="bkmrk-swarm-%E2%80%93-%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B4%D0%B0">**Контейнеры**</div><div id="bkmrk-docker%C2%A0-compose%C2%A0-%E2%80%93%C2%A0-">**Docker Compose** – создание и выполнение приложений, скомпонованных из нескольких Docker-контейнеров. Такие компоновки используются главным образом при разработке и тестировании</div><div id="bkmrk-docker-machine---%D1%83%D1%81%D1%82">**Docker Machine** - устанавливает и конфигурирует Docker-хосты на локальных и удаленных ресурсах. Кроме того, Machine конфигурирует клиента Docker, упрощая процедуру переключения между средами.</div><div id="bkmrk-kitematic---%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%87%D0%B5%D1%81">**Kitematic** - графический пользовательский интерфейс для операционных систем Mac OS и Windows, обеспечивающий запуск и управление контейнеров Docker</div><div id="bkmrk-">  
</div><div class="align-center" id="bkmrk-%D0%A5%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B8-%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8">**Хранение и управление образами Docker**</div><div id="bkmrk-docker-trusted-regis">**Docker Trusted Registry** – локальная версия реестра Docker Hub, которую можно объединить с существующей инфраструктурой. Исходный код закрыт.</div><div id="bkmrk-enterprise-registry-">**[Enterprise Registry](https://coreos.com/products/enterprise-registry/)** от CoreOS</div><div id="bkmrk-artifactory-%D0%BE%D1%82-jfrog">**[Artifactory](http://www.jfrog.com/open-source/#os-arti)** от JFrog.</div><div id="bkmrk--1"></div><div class="align-center" id="bkmrk-%D1%81%D0%B5%D1%82%D0%B5%D0%B2%D0%B0%D1%8F-%D1%81%D1%80%D0%B5%D0%B4%D0%B0-%E2%80%93-%D1%81%D0%BE%D0%B7%D0%B4">**Сетевая среда**</div><div id="bkmrk-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D0%B5%D1%82%D0%B5%D0%B9-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5">Создание сетей контейнеров, распределенных между разными хостами</div><div id="bkmrk-weave">**[Weave](http://weave.works/net/)**</div><div id="bkmrk-weave-%28http%3A%2F%2Fweave.">**[Project Calico](http://www.projectcalico.org/)** </div><div class="align-center" id="bkmrk-%D0%BE%D0%B1%D0%BD%D0%B0%D1%80%D1%83%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%C2%A0-%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BE">**Обнаружение сервисов**</div><div id="bkmrk-%D0%9F%D0%BE%D0%B8%D1%81%D0%BA-%D0%B4%D1%80%D1%83%D0%B3%D0%B8%D1%85%C2%A0-%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81">Поиск других сервисов для взаимодействия с ними. </div><div id="bkmrk-consul%C2%A0">**[Consul](https://consul.io/)** </div><div id="bkmrk-registrator-%C2%A0">**[Registrator](https://github.com/gliderlabs/registrator)** </div><div id="bkmrk-registrator%C2%A0-%28https%3A">**[SkyDNS](https://github.com/skynetservices/skydns/)** </div><div id="bkmrk-skydns-%28https%3A%2F%2Fgith">**[etcd](https://github.com/coreos/etcd)** </div><div class="align-center" id="bkmrk-%D0%BE%D1%80%D0%BA%D0%B5%D1%81%D1%82%D1%80%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8-%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB">**Оркестровка и управление кластером**</div><div id="bkmrk-%D0%9A%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D1%8C-%D0%B8%C2%A0-%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD">Контроль и управления всей системой в целом.</div><div id="bkmrk-kubernetes-%28http%3A%2F%2Fk">**[Kubernetes](http://kubernetes.io)** от Google</div><div id="bkmrk-marathon-%28https%3A%2F%2Fgi">**[Marathon](https://github.com/me-sosphere/marathon)** </div><div id="bkmrk-%D1%84%D1%80%D0%B5%D0%B9%D0%BC%D0%B2%D0%BE%D1%80%D0%BA-%D0%B4%D0%BB%D1%8F-mesos-">фреймворк для **[Mesos](https://mesos.apache.org)** </div><div id="bkmrk-fleet-%28https%3A%2F%2Fgithu">**[Fleet](https://github.com/coreos/fleet)** от CoreOS</div><div id="bkmrk-docker-swarm">**[Docker Swarm](https://docs.docker.com/engine/swarm/)** – решение задачи кластеризации от Docker. Группирует несколько Docker-хостов, после чего пользователь может работать с этой группой как с единым ресурсом.</div>

# Примеры

# Контейнеризация приложения из git

Создаем папку app и переходим в нее.

Клонируем git

```bash
git clone https://github.com/sudaka/irksecrets.git
```

Создаем dockerfile

```ini
FROM ubuntu:latest
LABEL maintainer="..." 
RUN apt-get update
RUN apt install -y python3 python3-pip uvicorn
RUN mkdir /var/www
WORKDIR /var/www
COPY ./irksecrets /var/www
RUN python3 -m pip install -r requirements.txt
EXPOSE 8000
ENTRYPOINT ["uvicorn", "irksecrets:app", "--host", "0.0.0.0", "--port", "8000"]
```

Создаем образ

```bash
docker build -t irkscweb:2.0 .
```

Создаем контейнер

```bash
docker run -d --name fa2 -p 8000:8000 irkscweb:2.0
```

Сейчас контейнер сервиса должен заработать, по адресу 127.0.0.1/docs должна быть страница сервиса. Останавливаем сервис.

Информация по официальному образу postgres:

POSTGRES\_PASSWORD=mysecretpassword  
POSTGRES\_USER  
POSTGRES\_DB #при отсутствии будет создана БД  
POSTGRES\_HOST\_AUTH\_METHOD  
все файлы .sql .sh в папке docker-entrypoint-initdb.d исполняются при инициализации БД

Поднимаемся на один уровень и создаем папку db.

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

```sql
create role irksecrets with login superuser;
alter role irksecrets with encrypted password 'Password';
create table secrets (chash char(64) primary key, enctext bytea);
alter database irksecrets owner to irksecrets;
```

```ini
FROM postgres
LABEL maintainer="sa@yandex.ru"
ENV POSTGRES_PASSWORD 123458
ENV POSTGRES_HOST_AUTH_METHOD md5
ENV POSTGRES_DB: irksecrets
COPY *.sql /docker-entrypoint-initdb.d/ #инит скрипты, при наличии БД не запускаются
EXPOSE 5432
```

Создаем compose файл. Директории app db рядом с .yaml файлом

```yaml
services
		webapp:
			build: app/.
			ports:
				- target: 8000
				  published: 8000
			networks:
				- isnet
		dbhost:
			build: db/.
			environment:
				- POSTGRES_PASSWORD=Password
				- POSTGRES_HOST_AUTH_METHOD=md5
				- POSTGRES_DB=irksecrets
			ports:
				- target: 5432
				  published: 5432
			networks:
				- isnet
		networks:
			isnet:
```

# Запуск из консоли и простые dockerfile

**Запуск bash в контейнере:**

```bash
docker run -i -t debian /bin/bash
```

**Удаление всех остановленных контейнеров**

```bash
docker rm -v $(docker ps -aq -f status=exited)
```

**Создание контейнера, установка доп. приложения и запуск**

```bash
docker run -it --name cowsay --hostname cowsay debian bash
root@cowsay:/# apt-get update
root@cowsay:/# apt-get install -y cowsay fortune
root@cowsay:/# exit
docker commit cowsay test/cowsayimage
docker run test/cowsayimage /usr/games/cowsay "Moo"
```

**Создание образа из docker файла**

Dockerfile:

```ini
FROM debian:wheezy
RUN apt-get update && apt-get install -y cowsay fortune
ENTRYPOINT ["/usr/games/cowsay"]
```

Создание образа из dockerfile

```bash
docker build -t test/cowsay-dockerfile .
```

**Пример скрипта вызова разных приложений при установленной точке входа**

```bash
nano entrypoint.sh 
```

```bash
#!/bin/bash
if [ $# -eq 0 ]; then
  /usr/games/fortune | /usr/games/cowsay
else
  /usr/games/cowsay "$@"
```

```bash
chmod +x entrypoint.sh
```

DockerFile:

```ini
FROM debian
RUN apt-get update && apt-get install -y cowsay fortune
COPY entrypoint.sh /
ENTRYPOINT ["/entrtypoint.sh"]
```

# 3 образа

```yaml
services:
 identydock:
  build: . 			#build ссылается на docker file. Либо build, либо image.
  ports:
   - "5000:5000"
  environment:
   ENV: DEV
  volumes:
   - ./app:/app # старое описание
   - type: volume
     source: counter-vol
	 target: /app
  links:
   - dnmonster
   - redis

 dnmonster:
  image: amouat/dnmonster 

 redis:
  image: redis
volumes:
	counter-vol:
```

# Postgresql

[Основа инструкции на хабре](https://habr.com/ru/articles/578744/)

Минимальный compose файл с возможностью подключиться извне, логином и паролем:

```yaml
services:
  postgres:
    image: postgres:16.3
    environment:
      POSTGRES_DB: "testdb"
      POSTGRES_USER: "testuser"
      POSTGRES_PASSWORD: "testpass"
    ports:
      - "5432:5432"
```

Точка входа для инициализации базы данных: **docker-entrypoint-initdb.d** Все \*.sql или \*.sh файлы в этом каталоге - скрипты для инициализации БД. Детали использования:

1. если БД уже была проинициализирована ранее, то никакие изменения к ней применяться не будут;
2. если в каталоге присутствует несколько файлов, то они будут отсортированы по имени с использованием текущей локали (по умолчанию en\_US.utf8).

```yaml
services:
  postgres:
    image: postgres:16.3
    environment:
      POSTGRES_DB: "testdb"
      POSTGRES_USER: "testuser"
      POSTGRES_PASSWORD: "testpass"
    volumes:
      - .:/docker-entrypoint-initdb.d
    ports:
      - "5432:5432"
```

Для постоянного размещения БД нужно подмонтировать соответствующий каталог (куда будут сохраняться данные) в контейнер и при необходимости переопределить переменную окружения **PGDATA**

```yaml
services:
  postgres:
    image: postgres:16.3
    environment:
      POSTGRES_DB: "testdb"
      POSTGRES_USER: "testuser"
      POSTGRES_PASSWORD: "testpass"
      PGDATA: "/var/lib/postgresql/data/pgdata"
    volumes:
      - .:/docker-entrypoint-initdb.d
      - mydata:/var/lib/postgresql/data
    ports:
      - "5432:5432"
volumes:
  mydata:
```

Подключение к базе через psql

```bash
psql -d testdb -U testuser -W -h 127.0.0.1 -p 5430
```

# Nginx

Создать директорию nginx\_config.conf и внутри файл python\_microservices

```bash
server {
  listen 8080;

  location /api/firstendpoint {
    proxy_pass http://firstendpoint:8000/api/firstendpoint;
  }

  location /api/secondendpoint {
    proxy_pass http://secondendpoint:8000/api/secondendpoint;
  }
}
```

Compose:

```yaml
version: '3.7'

services:
  nginx:
    image: nginx:latest
    ports:
      - "8080:8080"
    volumes:
      - ./nginx_config.conf:/etc/nginx/conf.d/default.conf
```

[Источник](https://habr.com/ru/articles/799337/)

**NGINX reverse proxy с https терминацией**

Обслуживает несколько доменов, для одного из доменов путь /auth ведет на отдельный сервер

**Структура проекта:**

```
>certs
.env
docker-compose.yaml
nginx.conf.template
```

**Размещение статических файлов**

```
location /static {
    alias /app/static;
    # Опционально: настройки для статики
    expires 30d;
    add_header Cache-Control "public, immutable";
}
```

**Директория certs:** сертификаты в формате &lt;domain name&gt;.crt и &lt;domain name&gt;.key

**.env**

```
# Backend сервисы
WOOD_BACKEND_IP=192.168.1.194
WOOD_BACKEND_PORT=8000
HUB_BACKEND_IP=192.168.1.194
HUB_BACKEND_PORT=8021
HUB_BACKEND_WEB_PORT=8020

WOOD_AUTH_IP=192.168.1.194
WOOD_AUTH_PORT=8001

# Домены
HUB_DOMAIN=hub.bobrobotirk.ru
HUB_WEB_DOMAIN=hubui.bobrobotirk.ru
WOOD_DOMAIN=wood.bobrobotirk.ru
```

**nginx.conf.template**

```
worker_processes auto;

events {
    worker_connections 1024;
}

http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    # SSL-настройки
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;

    # Upstreams
    upstream hub_backend {
        server ${HUB_BACKEND_IP}:${HUB_BACKEND_PORT};
    }

    upstream hub_backend_web {
        server ${HUB_BACKEND_IP}:${HUB_BACKEND_WEB_PORT};
    }

    upstream wood_backend {
        server ${WOOD_BACKEND_IP}:${WOOD_BACKEND_PORT};
    }

    upstream wood_auth_backend {
        server ${WOOD_AUTH_IP}:${WOOD_AUTH_PORT};
    }

    # HTTP → HTTPS редирект
    server {
        listen 80;
        server_name ${HUB_DOMAIN} ${WOOD_DOMAIN};
        return 301 https://$host$request_uri;
    }

    # Конфиг для hub.bobrobotirk.ru
    server {
        listen 443 ssl;
        server_name ${HUB_DOMAIN};

        ssl_certificate /etc/nginx/certs/${HUB_DOMAIN}.crt;
        ssl_certificate_key /etc/nginx/certs/${HUB_DOMAIN}.key;

        location / {
            proxy_pass http://hub_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }

    server {
        listen 443 ssl;
        server_name ${HUB_WEB_DOMAIN};

        ssl_certificate /etc/nginx/certs/${HUB_WEB_DOMAIN}.crt;
        ssl_certificate_key /etc/nginx/certs/${HUB_WEB_DOMAIN}.key;

        location / {
            proxy_pass http://hub_backend_web;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            }
        }

    # Конфиг для wood.bobrobotirk.ru
    server {
        listen 443 ssl;
        server_name ${WOOD_DOMAIN};

        ssl_certificate /etc/nginx/certs/${WOOD_DOMAIN}.crt;
        ssl_certificate_key /etc/nginx/certs/${WOOD_DOMAIN}.key;

        location /auth {
            proxy_pass http://wood_auth_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        location / {
            proxy_pass http://wood_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
```

Алгоритм добавления еще одного endpoint:

Файл .env Константы, связанные с endpoint

```
ELSE_BACKEND_IP=192.168.1.198
ELSE_BACKEND_PORT=8000
```

Файл docker-compose .yaml

Добавляем эти константы в envsubst для машины nginx

```
envsubst '$${ELSE_BACKEND_IP} $${ELSE_BACKEND_PORT} ...'
```

И добавляем еще один сервис

```
  else-backend:
    image: else-backend:0.1
    container_name: else-backend
    #restart: unless-stopped  # Автоперезапуск при ошибках
    ports:
      - "${ELSE_BACKEND_PORT}:8000"  # host:container
    environment:
      - APP_ENV=production
      - LOG_LEVEL=info
      - WORKERS_PER_CORE=1  # Оптимально для CPU-bound приложений
      - MAX_WORKERS=4      # Лимит на случай автоскейлинга
      - TIMEOUT=120        # Макс. время выполнения запроса (сек)
      - KEEP_ALIVE=5       # Поддержка keep-alive соединений
    command: sh -c "uvicorn main:app --host=0.0.0.0 --port=8000 --reload"
    volumes:
      - ./backend/app:/app
```

В файле nginx.conf.template добавляем два раздела

```
    ...
    # Upstreams
    upstream else-backend {
        server ${ELSE_BACKEND_IP}:${ELSE_BACKEND_PORT};
    }
    ...
    location /else {
            proxy_pass http://else-backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
```

**docker-compose.yaml**

```yaml
services:
  nginx-proxy:
    image: nginx:latest
    container_name: nginx-proxy
    hostname: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf.template:/etc/nginx/templates/nginx.conf.template
      - ./certs:/etc/nginx/certs
    env_file: 
      - .env  # Подключаем переменные из файла
    command: >
      /bin/sh -c "
      envsubst '$${HUB_BACKEND_IP} $${HUB_BACKEND_PORT} $${HUB_BACKEND_WEB_PORT} $${WOOD_BACKEND_IP}
      $${WOOD_BACKEND_PORT} $${WOOD_AUTH_IP} $${WOOD_AUTH_PORT}
      $${HUB_DOMAIN} $${HUB_WEB_DOMAIN} $${WOOD_DOMAIN}'
      < /etc/nginx/templates/nginx.conf.template
      > /etc/nginx/nginx.conf
      && nginx -g 'daemon off;'
      "
    restart: no
```

# Файловый сервер

Один из самых простых S3-совместимых серверов - minio ([официальный сайт](https://min.io/)) Еще есть FreeNAS.

**Запуск сервера**

Compose файл для тестов:

```
services:
  minio:
    image: minio/minio:latest
    container_name: minio
    restart: unless-stopped
    volumes:
      - minio-storage:/data
      - minio-config:/root/.minio
    environment:
      - MINIO_ROOT_USER=miniuser
      - MINIO_ROOT_PASSWORD=admin123
    command: server /data --console-address ":9001"
    ports:
      - "9000:9000"
      - "9001:9001"
volumes:
  minio-storage:
  minio-config:
```

Запускает сервер API на 9000 порту, web интерфейс управления на 9001

[+ сертификаты](https://habr.com/ru/companies/veeam/articles/517392/)

[Тоже интересно](https://pressanybutton.ru/post/primenenie-docker/sobstvennoe-s3-hranilishe-na-baze-minio/)

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

Создается группа с правом readwrite, создается пользователь и привязывается к группе. Создается bucket.

Из-под пользователя создается ключ доступа.

# Mariadb

Установка клиента mariadb

```
sudo apt install mariadb-client
mysql -u user -p -h 127.0.0.1 -P 3306
```

Dockerfile

```
FROM mariadb:latest

# Устанавливаем переменные окружения
ENV MYSQL_ROOT_PASSWORD=...
ENV MYSQL_DATABASE=...
ENV MYSQL_USER=...
ENV MYSQL_PASSWORD=...
#ENV MYSQL_ROOT_HOST= '%'                    # Разрешить root-подключения с любого хоста (опционально)

# Копируем SQL-скрипт для дополнительных прав
COPY ./init.sql /docker-entrypoint-initdb.d/
```

init.sql

```
GRANT ALL PRIVILEGES ON wood_db.* TO 'wooduser'@'%';
FLUSH PRIVILEGES;
```

docker-compose.yaml

```yaml
services:
  mariadb:
    image: hub.bobrobotirk.ru/appone-db:0.1.0
    container_name: mariadb
    volumes:
      - ./dbdata:/var/lib/mysql
    ports:
      - "${MYSQL_PORT}:3306"
    restart: unless-stopped
```

Еще вариант:

```yaml
services:
  mariadb:
    image: mariadb:10.11
    container_name: mariadb
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: rootpassword
      MARIADB_DATABASE: mydb
      MARIADB_USER: myuser
      MARIADB_PASSWORD: mypassword
    ports:
      - "3306:3306"
    volumes:
      - ./mariadb_data:/var/lib/mysql
      - ./init:/docker-entrypoint-initdb.d
```

# 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
```

# Helm

# Helm

Один из самых популярных пакетных менеджеров для Kubernetes.

**Управление helm**

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

```
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
```

**Репозиторий**

<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: 101.548%;"><thead><tr><td class="align-center" style="width: 20.8582%;">Команда</td><td class="align-center" style="width: 21.6994%;">Доп. пар.</td><td class="align-center" style="width: 57.4424%;">Описание</td></tr></thead><tbody><tr><td style="width: 20.8582%;">helm repo</td><td style="width: 21.6994%;">list</td><td style="width: 57.4424%;">Список репозиториев</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 21.6994%;">add repo\_name repo\_url</td><td style="width: 57.4424%;">Добавить репозиторий repo\_name с адресом repo\_url

```
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
```

Часто используют bitnami, но в России он сейчас закрыт. Есть зеркало:

```
helm repo add bitnami https://raw.githubusercontent.com/bitnami/charts/archive-full-index/bitnami
```

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 21.6994%;">update</td><td style="width: 57.4424%;">обновить репозиторий

</td></tr><tr><td style="width: 20.8582%;">helm search   
</td><td style="width: 21.6994%;">repo keyword</td><td style="width: 57.4424%;">Поиск чартов по репозиториям ключевого слова keyword

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 21.6994%;">hub keyword</td><td style="width: 57.4424%;">В официальном репозитории

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 21.6994%;">--max-col-width=0</td><td style="width: 57.4424%;">\+ hub/repo полный вывод текста

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 21.6994%;">--output yaml</td><td style="width: 57.4424%;">+hub/repo вывод в yaml

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 21.6994%;">--versions</td><td style="width: 57.4424%;">Отсортировать по версиям чарта

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

</div>**Плагины**

Он сам по себе мощный, но [ссылка на плагины](https://helm.sh/docs/community/related/)

<table border="1" 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-1" style="width: 101.548%;"><thead><tr><td class="align-center" style="width: 20.8582%;">Команда</td><td class="align-center" style="width: 18.5973%;">Доп. пар.</td><td class="align-center" style="width: 60.5446%;">Описание</td></tr></thead><tbody><tr><td style="width: 20.8582%;">helm plugin install url</td><td style="width: 18.5973%;"> </td><td style="width: 60.5446%;">Установка плагина</td></tr><tr><td style="width: 20.8582%;">helm plugin list</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">Список плагинов</td></tr><tr><td style="width: 20.8582%;">helm plugin update pl\_name</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">Обновление плагина</td></tr><tr><td style="width: 20.8582%;">helm plugin unistall pl\_name  
</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">Удаление плагина</td></tr></tbody></table>

**Переменные окружения**

Зависит от переменных окружения. Основные переменные:

<table border="1" 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-2" style="width: 100%;"><thead><tr><td class="align-center" style="width: 25.6257%;">Переменная</td><td class="align-center" style="width: 74.3743%;">Описание</td></tr></thead><tbody><tr><td style="width: 25.6257%;">XDG\_CACHE\_HOME</td><td style="width: 74.3743%;">Размещение кешированных данных. По умолчанию ~/.cache/helm</td></tr><tr><td style="width: 25.6257%;">XDG\_CONFIG\_HOME</td><td style="width: 74.3743%;">Размещение конфигурационного файла По умолчанию ~/.config/helm</td></tr><tr><td style="width: 25.6257%;">XDG\_DATA\_HOME</td><td style="width: 74.3743%;">Размещение плагинов helm По умолчанию ~/.local/share/helm</td></tr><tr><td style="width: 25.6257%;">HELM\_DRIVER</td><td style="width: 74.3743%;">Драйвер для хранения данных. Secret - хранение авторизационных данных в файле, может быть configmap и memory</td></tr><tr><td style="width: 25.6257%;">HELM\_NO\_PLUGINS</td><td style="width: 74.3743%;">Отключить плагины</td></tr><tr><td style="width: 25.6257%;">KUBECONFIG</td><td style="width: 74.3743%;">Размещение конфигурационного файла kubectl</td></tr></tbody></table>

**Charts**

<table border="1" 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-3" style="width: 101.548%;"><thead><tr><td class="align-center" style="width: 20.8582%;">Команда</td><td class="align-center" style="width: 18.5973%;">Доп. пар.</td><td class="align-center" style="width: 60.5446%;">Описание</td></tr></thead><tbody><tr><td style="width: 20.8582%;">helm install</td><td style="width: 18.5973%;">name\_chart repo</td><td style="width: 60.5446%;">Установить из репозитория repo чарт name\_chart</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 18.5973%;">--...</td><td style="width: 60.5446%;">Переменные внутри чарта ```
helm install kubeapps --namespace kubeapps bitnami/kubeapps
```

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 18.5973%;">--debug --dry-run pr\_name path\_to\_ch</td><td style="width: 60.5446%;">протестировать без установки чарта</td></tr><tr><td style="width: 20.8582%;">helm inspect values   
</td><td style="width: 18.5973%;">name\_chart &gt; ...</td><td style="width: 60.5446%;">Сохранение чарта в файл

```
helm inspect values stable/kube-ops-view > kube-ops-view.yaml
```

</td></tr><tr><td style="width: 20.8582%;">helm lint --strict path-to-chart</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">Проверить соответствие values схеме

</td></tr><tr><td style="width: 20.8582%;">helm fetch name\_chart</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">Скачать чарт в tar

</td></tr><tr><td style="width: 20.8582%;">  
</td><td style="width: 18.5973%;">--untar</td><td style="width: 60.5446%;">И распаковать

```
helm fetch bitnami/wordpress --untar
```

</td></tr><tr><td style="width: 20.8582%;">helm ls   
</td><td style="width: 18.5973%;">--namespace namespace</td><td style="width: 60.5446%;">список установленных чартов

</td></tr><tr><td style="width: 20.8582%;">helm upgrade</td><td style="width: 18.5973%;">...</td><td style="width: 60.5446%;">Обновление

</td></tr><tr><td style="width: 20.8582%;">helm rollback ch\_name count</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">Откатить чарт ch\_name на count назад

```
helm rollback redis 1 --namespace=redis
```

</td></tr><tr><td style="width: 20.8582%;">helm uninstall name\_ch</td><td style="width: 18.5973%;">  
</td><td style="width: 60.5446%;">```
helm uninstall kubeapps --namespace kubeapps
```

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

**Общая структура чарта**

Helm автоматически определяет последовательность применения шаблонов в чарте.

<table border="1" id="bkmrk-%D0%94%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D1%8F%2F%D1%84%D0%B0%D0%B9%D0%BB-%D0%9E%D0%BF%D0%B8%D1%81" style="width: 101.548%; height: 357.563px;"><thead><tr style="height: 29.7969px;"><td class="align-center" style="width: 19.0725%; height: 29.7969px;">Директория/файл</td><td class="align-center" style="width: 55.1826%; height: 29.7969px;">Описание</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">Обяз.</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">Chart.yaml</td><td style="width: 55.1826%; height: 29.7969px;">Метаданные чарта</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">+</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">templates/</td><td style="width: 55.1826%; height: 29.7969px;">Ресурсы кубера в формате yaml helm (yaml с переменными)

Но файлы начинающиеся с \_ не обрабатываются, \_\*.tpl обрабатываются как helper файлы.

</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">+ если не составной</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">templates/NOTES.txt</td><td style="width: 55.1826%; height: 29.7969px;">Инструкции по использованию</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">values.yaml</td><td style="width: 55.1826%; height: 29.7969px;">Переменные по умолчанию</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">.helmignore</td><td style="width: 55.1826%; height: 29.7969px;">Файлы исключения при упаковке чарта</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">charts/</td><td style="width: 55.1826%; height: 29.7969px;">Зависимости (другие чарты)</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-, при отсутствии helm их сгенерирует в соответствии с Chart.yaml</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">Chart.lock</td><td style="width: 55.1826%; height: 29.7969px;">Первично примененные зависимости.</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-, будет создан автоматически</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">crds/</td><td style="width: 55.1826%; height: 29.7969px;">Зависимости, которые должны быть собраны до основного чарта</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">README.md</td><td style="width: 55.1826%; height: 29.7969px;">Описание</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">LICENSE</td><td style="width: 55.1826%; height: 29.7969px;">Лицензия</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr style="height: 29.7969px;"><td style="width: 19.0725%; height: 29.7969px;">value.shema,json</td><td style="width: 55.1826%; height: 29.7969px;">Шаблон в json формате</td><td class="align-center" style="width: 25.7449%; height: 29.7969px;">-</td></tr><tr><td style="width: 19.0725%;">files/</td><td style="width: 55.1826%;">Дополнительные файлы</td><td style="width: 25.7449%;">  
</td></tr></tbody></table>

**templates/**

Измененные yaml. Добавлены переменные в формате Go шаблонизации. Переменные берутся из файла values.yaml

```
apiVersion: v1
kind: ConfigMap
metadata: 
  name: {{ .Release.Name }}
data:
  configuration.txt: |-
    {{ .Values.configurationData }}
```

Родительские пространства имен у переменных:

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%B0%D0%BD%D1%81%D1%82%D0%B2%D0%BE-%D0%B8%D0%BC%D0%B5%D0%BD-%D0%9E%D0%BF" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 30.0596%;"></col><col style="width: 70.0596%;"></col></colgroup><thead><tr><td class="align-center">Пространство имен</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>.Release</td><td>Переменные, связанные с релизом в устанавливаемой системе. .Release.Name - имя релиза .Release.Namespace - пространство имен релиза .Release.Revision - номер версии  
</td></tr><tr><td>.Values</td><td>Переменные, размещенные в файле values.yaml</td></tr><tr><td>.Chart</td><td>Переменные, получаемые из файла Chart.yaml Например, .Chart.Name, .Chart.Version .Chart.AppVersion   
</td></tr><tr><td>.Files</td><td>Работа с файлами в директории из директории files. Если файл не существует - вернется ошибка.

.Files.Get - Извлекает содержимое файлов

.Files.AsSecrets - Возвращает Base-64 закодированную строку для создания secret

.Files.AsConfig - Возвращает данные для использования в виде ConfigMap

</td></tr><tr><td>.Subcharts</td><td>Пространство имен дочерних чартов. Например .Subchart.MyChart.firstvalue

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

**values.yaml**

В виде обычного key: value yaml

**Chart.yaml**

Чарты бывают application и library. Application используются для деплоя приложений, library - для предоставления именованных шаблонов, используемых в других чартах. В library чартах не может быть ни одного шаблона, только helper файлы.

Обязательные поля:

<table border="1" id="bkmrk-%D0%9F%D0%BE%D0%BB%D0%B5-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-apiver" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 30.0596%;"></col><col style="width: 70.0596%;"></col></colgroup><thead><tr><td class="align-center">Поле</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>apiVersion</td><td>Версия. В helm 3 формате используется v2</td></tr><tr><td>name</td><td>Имя чарта. Должно совпадать с именем директории чарта. В именах стоит использовать только -, например first-chart</td></tr><tr><td>version</td><td>Версия. Формат X.Y.Z</td></tr></tbody></table>

Пример файла

<table border="1" 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-4" style="width: 101.548%;"><thead><tr><td class="align-center" style="width: 24.6677%;">Команда</td><td class="align-center" style="width: 17.883%;">Доп. пар.</td><td class="align-center" style="width: 57.4494%;">Описание</td></tr></thead><tbody><tr><td style="width: 24.6677%;">helm create chart\_name</td><td style="width: 17.883%;"> </td><td style="width: 57.4494%;">Создание шаблона чарта</td></tr><tr><td style="width: 24.6677%;">helm install proj\_name path</td><td style="width: 17.883%;">  
</td><td style="width: 57.4494%;">Создание проекта с названием proj\_name используя чарт по пути path</td></tr><tr><td style="width: 24.6677%;">  
</td><td style="width: 17.883%;">-f par\_file</td><td style="width: 57.4494%;">ссылка на другой файл параметров</td></tr><tr><td style="width: 24.6677%;">  
</td><td style="width: 17.883%;">--set foo=bar</td><td style="width: 57.4494%;">Ручная установка параметров</td></tr><tr><td style="width: 24.6677%;">helm get manifest proj\_name</td><td style="width: 17.883%;">  
</td><td style="width: 57.4494%;">Получить манифест проекта proj\_name</td></tr></tbody></table>

**Зависимости чарта**

Заполняются в разделе dependencies файла Chart.yaml

<table border="1" 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-5" style="width: 101.548%;"><thead><tr><td class="align-center" style="width: 24.6677%;">Команда</td><td class="align-center" style="width: 17.883%;">Доп. пар.</td><td class="align-center" style="width: 57.4494%;">Описание</td></tr></thead><tbody><tr><td style="width: 24.6677%;">helm dependency build</td><td style="width: 17.883%;">путь до Chart.yaml</td><td style="width: 57.4494%;">Перестроить зависимые чарты, базируясь на файле Chart.lock Если этого файла нет - то же что и update</td></tr><tr><td style="width: 24.6677%;">helm dependency list</td><td style="width: 17.883%;"> </td><td style="width: 57.4494%;">Список зависимостей</td></tr><tr><td style="width: 24.6677%;">helm dependency update</td><td style="width: 17.883%;"> </td><td style="width: 57.4494%;">Обновление чартов и генерация Chart.lock</td></tr></tbody></table>

В зависимостях могут быть условия:

```
dependencies:
  - name: dependency1
    repository: https://example.com
    version: 1.x.x
    condition: dependency1.enabled
    tags:
      - monitoring: true
  - name: dependency2
    repository: https://example.com
    version: 2.x.x
    condition: dependency2.enabled
    tags:
      - monitoring: true
```

В данном случае переменные dependency1.enabled и dependency2.enabled должны быть установлены в values.yaml файле. Можно несколько переменных через запятую, но лучше завести одну общую переменную и в values ее заполнять. Раздел tags разделяет по группам: если в родительском чарте переменная monitoring не выставлена - данные зависимости установлены не будут. Может быть несколько тэгов, но если хотя бы один подходит - будет добавлено.

Дочерние параметры чарта могут быть переопределены.

Можно импортировать параметры из дочернего чарта (если его параметры отмечены как экспортируемые).

Также есть хуки, позволяющие выполнять что-то при достижении определенной стадии.

# Go templates

Элементы шаблонизации заключены в двойные фигурные скобки, остальные - статический текст. Элементы шаблонизации могут включать переменные, условия, циклы, функции.

Конструкция {{- удаляет строку в которой функция.

**Условия:**

```
{{ if ConditionOne }}
# Do something
{{ else if ConditionTwo }}
# Do something else
{{ else }}
# Default case
{{ end }}
```

```
{{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}
```

**With**

Работает так же как и в python

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
```

**Циклы**

values.yaml:

```
favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions
  - pineapple
```

cur.yaml:

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
  toppings: |-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}  
```

**Функции:**

Можно использовать pipeline: {{ .Values.favorite.drink | quote }}

<table border="1" 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-2" style="width: 100%;"><thead><tr><td class="align-center" style="width: 25.6257%;">Функция</td><td class="align-center" style="width: 74.3743%;">Описание</td></tr></thead><tbody><tr><td style="width: 25.6257%;">quote</td><td style="width: 74.3743%;">Добавляет кавычки ```
{{ quote .Values.favorite.food }}
```

  
</td></tr><tr><td style="width: 25.6257%;">upper</td><td style="width: 74.3743%;">В верхний регистр</td></tr><tr><td style="width: 25.6257%;">repeat n</td><td style="width: 74.3743%;">Повтор значения n раз

```
{{ .Values.favorite.drink | repeat 5 | quote }}
```

  
</td></tr><tr><td style="width: 25.6257%;">default "some\_hy"</td><td style="width: 74.3743%;">Присвоить значение по умолчанию если отсутствует

```
drink: {{ .Values.favorite.drink | default "tea" | quote }}
```

</td></tr><tr><td style="width: 25.6257%;">eq, ne, lt, gt, and, or</td><td style="width: 74.3743%;">Логические функции

</td></tr><tr><td style="width: 25.6257%;">indent n</td><td style="width: 74.3743%;">Поставить n пробелов перед конструкцией

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

[Список функций](https://helm.sh/docs/chart_template_guide/function_list/)

**Именованные шаблоны**

С шаблонами нужно запускать

```
helm install --dry-run --disable-openapi-validation
```

Шаблон внутри \_helpers.tpl файла:

```
{{- define "first.labels" -}}
labels:
  'app.kubernetes.io/instacce': {{ .Release.Name }}
  'app.kubernetes.io/managed-by': {{ .Release.Service }}
{{- end }}

{{- define "first.nameofchart" -}}
{{- printf "mycurname" -}}
{{ - end }}
```

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "first.nameofchart" . }}
{{- include "first.labels" . | nindent 2 }}
data:
  myvalue: "Hello World"
  food: {{ .Values.favourite.food }}
  drink: {{ .Values.favourite.drink }}
```

Точка означает передачу внутрь шаблона всех переменных.

Разница между include и template: проще использовать include, template не дает использовать дополнительные функции через pipeline

# Пример проекта

**Задача:** helm чарт приложения guestbook с БД redis

Создаем namespace для теста

```
kubectl create namespace guestbook-learn
```

Создаем шаблон структуры папок

```
helm create guestbook
```

Добавляем зеркало проекта bitnami и находим последнюю версию чарта

```
helm repo add bitnami https://raw.githubusercontent.com/bitnami/charts/archive-full-index/bitnami
helm search repo redis --versions
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
bitnami/redis           20.11.4         7.4.2           Redis(R) is an open source, advanced key-value ...
bitnami/redis           20.11.3         7.4.2           Redis(R) is an open source, advanced key-value ...
bitnami/redis           20.11.2         7.4.2           Redis(R) is an open source, advanced key-value ...

```

 В моем случае это 20.11.4. Добавляем зависимость в Chart.yaml

```yaml
dependencies:
  - name: redis
    version: 20.11.x
    repository: https://raw.githubusercontent.com/bitnami/charts/archive-full-index/bitnami

```

Попробуем загрузить зависимость

```
guestbook# helm dependency update .
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading redis from repo https://raw.githubusercontent.com/bitnami/charts/archive-full-index/bitnami
Pulled: registry-1.docker.io/bitnamicharts/redis:20.11.4
Digest: sha256:51ee4afc621d0e0b26109d41c32bf23f3db114f15dd816c1acf8d1ddbf8d57ed
Deleting outdated charts
```

Действительно, в папке charts появился архив

```
guestbook# ls charts/
redis-20.11.4.tgz
```

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

```
helm show values charts/redis-20.11.4.tgz
```

Это выдало портянку (если убрать комментарии) в 698 строк. Был задан вопрос ИИ. Ответ отличался от приведенного варианта в книге и прямое использование не поехало бы. В values.yaml было добавлено