Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05881e8d74 |
@@ -634,6 +634,7 @@ make custom-images # справка по собственным
|
||||
### Основная документация
|
||||
|
||||
- **[docs/getting-started.md](docs/getting-started.md)** - Быстрый старт
|
||||
- **[docs/quickstart-for-dummies.md](docs/quickstart-for-dummies.md)** - Пошаговое руководство для новичков (роли, плейбук, линт, тесты, пресеты, инвентори, деплой)
|
||||
- **[docs/molecule-guide.md](docs/molecule-guide.md)** - Руководство по Molecule
|
||||
- **[docs/creating-roles.md](docs/creating-roles.md)** - Создание ролей
|
||||
- **[docs/devops-role.md](docs/devops-role.md)** - Универсальная роль devops для настройки пользователей и SSH
|
||||
|
||||
@@ -35,8 +35,12 @@ RUN apt-get install -y \
|
||||
RUN wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_arm64 \
|
||||
&& chmod +x /usr/local/bin/yq
|
||||
|
||||
# Устанавливаем Docker CLI
|
||||
RUN apt-get install -y docker.io docker-compose
|
||||
# Устанавливаем Podman (вместо Docker). Для Ubuntu 22.04 — из universe
|
||||
RUN apt-get install -y software-properties-common \
|
||||
&& add-apt-repository -y universe \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y podman \
|
||||
&& apt-get clean
|
||||
|
||||
# Устанавливаем kubectl
|
||||
RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
|
||||
@@ -46,10 +50,10 @@ RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/s
|
||||
# Устанавливаем Helm
|
||||
RUN curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
|
||||
# Устанавливаем Kind
|
||||
RUN curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 \
|
||||
&& chmod +x ./kind \
|
||||
&& mv ./kind /usr/local/bin/
|
||||
# Устанавливаем Minikube (вместо Kind, для использования с драйвером podman)
|
||||
RUN curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \
|
||||
&& chmod +x minikube-linux-amd64 \
|
||||
&& mv minikube-linux-amd64 /usr/local/bin/minikube
|
||||
|
||||
## Устанавливаем Istio CLI
|
||||
#RUN curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.22.1 sh - \
|
||||
@@ -79,18 +83,17 @@ RUN chown -R ansible:ansible /ansible
|
||||
# Переключаемся на пользователя ansible
|
||||
USER ansible
|
||||
|
||||
# Устанавливаем дополнительные роли
|
||||
RUN ansible-galaxy install geerlingguy.docker \
|
||||
&& ansible-galaxy install geerlingguy.kubernetes
|
||||
# Устанавливаем дополнительные роли (коллекция containers.podman в requirements.yml)
|
||||
RUN ansible-galaxy install geerlingguy.kubernetes
|
||||
|
||||
# Устанавливаем molecule как root
|
||||
RUN pip3 install ansible ansible-core ansible-lint molecule molecule-docker passlib
|
||||
# Устанавливаем molecule как root (delegated driver для Podman)
|
||||
RUN pip3 install ansible ansible-core ansible-lint molecule passlib
|
||||
|
||||
# Проверяем, что molecule установлен
|
||||
RUN which molecule || echo "molecule not found"
|
||||
|
||||
# Настройки для работы с Docker
|
||||
ENV DOCKER_HOST=unix:///var/run/docker.sock
|
||||
# Настройки для работы с Podman (сокет монтируется с хоста)
|
||||
ENV CONTAINER_HOST=unix:///run/podman/podman.sock
|
||||
ENV ANSIBLE_FORCE_COLOR=1
|
||||
ENV ANSIBLE_STDOUT_CALLBACK=yaml
|
||||
ENV ANSIBLE_CALLBACKS_ENABLED=profile_tasks
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Ansible Controller для Podman (сокет Podman)
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
@@ -7,10 +10,10 @@ services:
|
||||
privileged: true
|
||||
command: sleep infinity
|
||||
environment:
|
||||
DOCKER_HOST: unix:///var/run/docker.sock
|
||||
CONTAINER_HOST: unix:///run/podman/podman.sock
|
||||
ANSIBLE_VAULT_PASSWORD_FILE: /ansible/vault/.vault
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /run/podman/podman.sock:/run/podman/podman.sock
|
||||
- .:/ansible
|
||||
working_dir: /ansible
|
||||
networks:
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
---
|
||||
# Ansible Collections for Molecule Universal
|
||||
# Ansible Collections for Molecule Universal (Podman)
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
collections:
|
||||
- name: community.docker
|
||||
version: ">=3.0.0"
|
||||
- name: containers.podman
|
||||
version: ">=1.10.0"
|
||||
- name: community.general
|
||||
version: ">=7.0.0"
|
||||
- name: ansible.posix
|
||||
|
||||
86
docs/podman.md
Normal file
86
docs/podman.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Работа с Podman (ветка podman)
|
||||
|
||||
**Автор:** Сергей Антропов
|
||||
**Сайт:** https://devops.org.ru
|
||||
|
||||
В ветке `podman` проект переведён на использование **только Podman** (без Docker и Docker Compose).
|
||||
|
||||
## Требования
|
||||
|
||||
- **Podman** установлен и запущен (rootful: сокет `/run/podman/podman.sock`)
|
||||
- **podman compose** (встроен в Podman 4.1+ или пакет `podman-compose`)
|
||||
- Для K8s: **Minikube** и **kubectl** на хосте
|
||||
|
||||
## Основные изменения
|
||||
|
||||
### Molecule
|
||||
|
||||
- Драйвер: `delegated` (создание/удаление контейнеров в `create.yml` / `destroy.yml` через Ansible).
|
||||
- Используется коллекция **containers.podman** (`podman_network`, `podman_container`).
|
||||
- Сокет: `/run/podman/podman.sock` (DOoD-узлы заменены на POoD с монтированием сокета Podman).
|
||||
- Инвентарь: `ansible_connection=containers.podman.podman`.
|
||||
|
||||
### Makefile
|
||||
|
||||
- Все вызовы `docker` заменены на `podman`.
|
||||
- Сборка образов: `podman build --platform ...` (без buildx).
|
||||
- Compose: `podman compose` (вместо `docker-compose`).
|
||||
- Сокет в целях контроллера: `PODMAN_SOCKET ?= /run/podman/podman.sock`, монтируется в контейнер ansible-controller.
|
||||
|
||||
### Ansible-controller
|
||||
|
||||
- Образ собирается и запускается через Podman.
|
||||
- В образе установлен **Podman** (вместо Docker).
|
||||
- При запуске монтируется сокет хоста: `-v $(PODMAN_SOCKET):/run/podman/podman.sock`.
|
||||
- Переменная окружения: `CONTAINER_HOST=unix:///run/podman/podman.sock`.
|
||||
|
||||
### Kubernetes
|
||||
|
||||
- Вместо **Kind** используется **Minikube** с драйвером `podman`.
|
||||
- Кластер создаётся на хосте: `minikube start --driver=podman`.
|
||||
- Скрипт: `scripts/create_minikube_cluster.py` (читает пресет из `molecule/presets/k8s/*.yml`).
|
||||
- В пресетах K8s: `minikube_profile`, `minikube_addons`, `minikube_cpus`, `minikube_memory` (вместо `kind_clusters`).
|
||||
|
||||
## Локальные образы (без доступа к registry)
|
||||
|
||||
Во всех пресетах при использовании Podman **используются только локальные образы**. Выгрузка из registry отключена.
|
||||
|
||||
1. Соберите все образы локально один раз:
|
||||
```bash
|
||||
make buildall
|
||||
```
|
||||
2. Либо один образ для нужного пресета:
|
||||
```bash
|
||||
make buildall-image IMAGE=ubuntu22
|
||||
make buildall-image IMAGE=debian11
|
||||
```
|
||||
3. После этого Molecule при `create` проверяет наличие образов локально и не выполняет `podman pull`. Если какого-то образа нет — выведет сообщение с указанием выполнить `make buildall`.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
```bash
|
||||
# 1. Собрать локальные образы (без registry)
|
||||
make buildall
|
||||
|
||||
# 2. Сеть labnet для compose (если нужен controller)
|
||||
podman network create labnet 2>/dev/null || true
|
||||
|
||||
# 3. Запуск ansible-controller
|
||||
make controller run
|
||||
|
||||
# 4. Тест ролей (контейнеры создаются через Podman из локальных образов)
|
||||
make role test minimal
|
||||
|
||||
# Minikube (на хосте)
|
||||
make k8s create k8s-minimal
|
||||
make k8s status
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
## Переменные
|
||||
|
||||
- **PODMAN_SOCKET** — путь к сокету Podman на хосте (по умолчанию `/run/podman/podman.sock` для rootful).
|
||||
|
||||
## CI/CD
|
||||
|
||||
Секция CI/CD (**cicd/**) в этой ветке **не менялась** — рассчитана на окружения с Docker. Для Podman в пайплайнах нужно отдельно устанавливать Podman и вызывать `podman` / `podman compose` в job’ах.
|
||||
332
docs/quickstart-for-dummies.md
Normal file
332
docs/quickstart-for-dummies.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# DevOpsLab: пошаговое руководство для новичков
|
||||
|
||||
**Автор:** Сергей Антропов
|
||||
**Сайт:** https://devops.org.ru
|
||||
|
||||
Это руководство описывает по шагам: создание роли, объединение ролей в плейбук, линт, тестирование на контейнерах (подах) с разным количеством и разными ОС, а также формирование инвентори для деплоя на реальные серверы. Подходит и для **Docker**, и для **Podman** (отличия указаны в конце).
|
||||
|
||||
---
|
||||
|
||||
## Что нужно установить
|
||||
|
||||
- **Make** (обычно уже есть в Linux/macOS).
|
||||
- **Podman** (ветка podman) или **Docker** (ветка main) — для контейнеров.
|
||||
- **Ansible** не обязателен на хосте: тесты и деплой можно запускать через контейнер из Makefile.
|
||||
|
||||
Клонируйте репозиторий и перейдите в каталог проекта. Все команды ниже выполняются из корня проекта.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 1. Создать новую роль
|
||||
|
||||
Роль — это набор задач (tasks), шаблонов и переменных для одной цели (например, установка nginx или настройка пользователей).
|
||||
|
||||
**Команда:**
|
||||
|
||||
```bash
|
||||
make role create
|
||||
```
|
||||
|
||||
Скрипт спросит имя роли (латиницей, например `nginx` или `myapp`). Будет создана структура:
|
||||
|
||||
- `roles/<имя_роли>/tasks/main.yml` — основные задачи
|
||||
- `roles/<имя_роли>/handlers/main.yml`
|
||||
- `roles/<имя_роли>/defaults/main.yml` — переменные по умолчанию
|
||||
- `roles/<имя_роли>/meta/main.yml`
|
||||
- и др.
|
||||
|
||||
**Что сделать после создания:**
|
||||
|
||||
1. Открыть `roles/<имя_роли>/tasks/main.yml` и добавить свои задачи (модули `package`, `copy`, `template`, `service` и т.д.).
|
||||
2. При необходимости задать переменные в `roles/<имя_роли>/defaults/main.yml`.
|
||||
|
||||
Роль автоматически попадёт в общий плейбук развёртывания после следующего шага (обновления плейбуков).
|
||||
|
||||
---
|
||||
|
||||
## Шаг 2. Объединить роли в один плейбук
|
||||
|
||||
Все роли, которые должны выполняться на серверах, собираются в один плейбук: **`roles/deploy.yml`**.
|
||||
|
||||
**Вариант А — обновить плейбук автоматически (все роли из `roles/`):**
|
||||
|
||||
```bash
|
||||
make update-playbooks
|
||||
```
|
||||
|
||||
Скрипт перезапишет `roles/deploy.yml`: для каждой роли в каталоге `roles/` будет добавлен свой play (hosts: all, одна роль в play).
|
||||
|
||||
**Вариант Б — править вручную:**
|
||||
|
||||
Откройте `roles/deploy.yml`. Каждая роль описывается своим блоком:
|
||||
|
||||
```yaml
|
||||
- name: Установка роли repo
|
||||
hosts: all
|
||||
become: true
|
||||
roles:
|
||||
- repo
|
||||
|
||||
- name: Установка роли devops
|
||||
hosts: all
|
||||
become: true
|
||||
roles:
|
||||
- devops
|
||||
```
|
||||
|
||||
- Чтобы **включить** роль — добавьте такой блок или раскомментируйте существующий.
|
||||
- Чтобы **выключить** роль — закомментируйте блок (перед каждой строкой поставьте `#`).
|
||||
- Порядок блоков задаёт порядок применения ролей.
|
||||
|
||||
Итог: в `roles/deploy.yml` перечислены все роли, которые будут и тестироваться в контейнерах, и деплоиться на реальные серверы (см. шаги 4 и 6).
|
||||
|
||||
---
|
||||
|
||||
## Шаг 3. Запуск линт-проверок
|
||||
|
||||
Линт проверяет синтаксис и типичные ошибки в ролях (и в плейбуках, которые их вызывают).
|
||||
|
||||
**Проверить все роли:**
|
||||
|
||||
```bash
|
||||
make role lint
|
||||
```
|
||||
|
||||
**Проверить одну роль:**
|
||||
|
||||
```bash
|
||||
make role lint <имя_роли>
|
||||
# Пример:
|
||||
make role lint repo
|
||||
make role lint devops
|
||||
```
|
||||
|
||||
При ошибках будут указаны файл и строка. Исправьте замечания и запустите линт снова. Без ошибок линт завершится без падения.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 4. Тестирование ролей на контейнерах (подах)
|
||||
|
||||
Тесты поднимают контейнеры с разными ОС, применяют к ним плейбук (в т.ч. ваши роли) и проверяют результат. Контейнеры в этом шаге — это и есть «поды» (тестовые хосты).
|
||||
|
||||
### 4.1. Подготовка образов (чтобы не качать из registry)
|
||||
|
||||
**Если используете Podman (ветка podman):**
|
||||
|
||||
Образы должны быть собраны **локально**. Один раз выполните:
|
||||
|
||||
```bash
|
||||
make buildall
|
||||
```
|
||||
|
||||
Собираются все образы (Ubuntu, Debian, CentOS и т.д.). Долго, но делается один раз. Для одного образа, например для минимального теста:
|
||||
|
||||
```bash
|
||||
make buildall-image IMAGE=ubuntu22
|
||||
make buildall-image IMAGE=debian12
|
||||
```
|
||||
|
||||
**Если используете Docker (ветка main):**
|
||||
|
||||
```bash
|
||||
make docker build
|
||||
```
|
||||
|
||||
(при необходимости сначала `make docker setup-builder`).
|
||||
|
||||
### 4.2. Запуск тестов
|
||||
|
||||
**С пресетом по умолчанию (обычно 2 хоста):**
|
||||
|
||||
```bash
|
||||
make role test
|
||||
```
|
||||
|
||||
**С конкретным пресетом:**
|
||||
|
||||
```bash
|
||||
make role test minimal
|
||||
make role test default
|
||||
make role test all-images
|
||||
```
|
||||
|
||||
Что происходит: создаются контейнеры по выбранному пресету, к ним применяется плейбук (converge), затем они удаляются. Результат виден в выводе команды.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 5. Менять количество подов и ОС (пресеты)
|
||||
|
||||
Количество «подов» (контейнеров) и их ОС задаются **пресетами** — файлами в `molecule/presets/` и `molecule/presets/examples/`.
|
||||
|
||||
### 5.1. Посмотреть доступные пресеты
|
||||
|
||||
```bash
|
||||
make presets list
|
||||
```
|
||||
|
||||
Будет выведен список пресетов и краткое описание (в т.ч. количество хостов).
|
||||
|
||||
### 5.2. Что задаёт пресет
|
||||
|
||||
В пресете задаётся:
|
||||
|
||||
- **hosts** — список хостов (контейнеров): имя, семейство ОС (`family`), группы.
|
||||
- **images** — соответствие `family` и образа (например `ubuntu22: "inecs/ansible-lab:ubuntu22-latest"`).
|
||||
- Общие настройки (сеть, systemd и т.д.).
|
||||
|
||||
От количества элементов в **hosts** и от выбранных **family** зависит, сколько подов поднимется и какие ОС будут использоваться.
|
||||
|
||||
### 5.3. Примеры пресетов по количеству и ОС
|
||||
|
||||
| Пресет | Описание | Кол-во хостов |
|
||||
|-------------|------------------------------------|----------------|
|
||||
| `minimal` | Один хост (быстрый тест) | 1 |
|
||||
| `default` | Два хоста (например Ubuntu + Debian) | 2 |
|
||||
| `all-images`| Максимум образов/ОС | много |
|
||||
|
||||
Запуск:
|
||||
|
||||
```bash
|
||||
make role test minimal
|
||||
make role test default
|
||||
make role test all-images
|
||||
```
|
||||
|
||||
### 5.4. Создать свой пресет (свои поды и ОС)
|
||||
|
||||
1. Скопируйте существующий пресет, например:
|
||||
|
||||
```bash
|
||||
cp molecule/presets/default.yml molecule/presets/my-preset.yml
|
||||
```
|
||||
|
||||
2. Откройте `molecule/presets/my-preset.yml`.
|
||||
|
||||
3. В секции **hosts** укажите свои хосты. Формат одного хоста:
|
||||
|
||||
```yaml
|
||||
hosts:
|
||||
- name: web1
|
||||
family: ubuntu22
|
||||
groups: [web, test]
|
||||
- name: db1
|
||||
family: centos9
|
||||
groups: [db, test]
|
||||
- name: lb1
|
||||
family: debian12
|
||||
groups: [lb, test]
|
||||
```
|
||||
|
||||
- **name** — уникальное имя контейнера (пода).
|
||||
- **family** — ключ из секции **images** (ubuntu20, ubuntu22, ubuntu24, debian9–debian12, centos7–centos9, alma, rocky, rhel, redos, astra, alt9, alt10 и т.д.).
|
||||
- **groups** — группы для инвентори (можно использовать в плейбуках через `hosts: web` или `hosts: db`).
|
||||
|
||||
4. Сохраните файл. Запуск теста с вашим пресетом:
|
||||
|
||||
```bash
|
||||
make role test my-preset
|
||||
```
|
||||
|
||||
Количество подов = количество элементов в списке **hosts**. Разные ОС = разные **family** при наличии соответствующих образов в **images** (и собранных локально при Podman через `make buildall`).
|
||||
|
||||
---
|
||||
|
||||
## Шаг 6. Инвентори и деплой на реальные серверы
|
||||
|
||||
Когда роли и плейбук готовы и протестированы в контейнерах, их можно применять к реальным серверам. Для этого нужен **инвентори** и команды деплоя.
|
||||
|
||||
### 6.1. Где лежит инвентори
|
||||
|
||||
Файл инвентори по умолчанию:
|
||||
|
||||
- **`inventory/hosts.ini`**
|
||||
|
||||
Именно его используют команды `make role deploy` и `make role dryrun` (см. ниже).
|
||||
|
||||
### 6.2. Как сформировать инвентори
|
||||
|
||||
Откройте `inventory/hosts.ini`. Формат — обычный ini-инвентори Ansible.
|
||||
|
||||
**Пример минимального инвентори:**
|
||||
|
||||
```ini
|
||||
# Группа веб-серверов
|
||||
[web_servers]
|
||||
web1 ansible_host=192.168.1.10 ansible_user=deploy
|
||||
web2 ansible_host=192.168.1.11 ansible_user=deploy
|
||||
|
||||
# Группа БД
|
||||
[db_servers]
|
||||
db1 ansible_host=192.168.1.20 ansible_user=deploy
|
||||
|
||||
# Общие переменные для всех хостов
|
||||
[all:vars]
|
||||
ansible_ssh_private_key_file=~/.ssh/id_rsa
|
||||
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
|
||||
```
|
||||
|
||||
- **Группы** — `[web_servers]`, `[db_servers]`. Имена можно менять и добавлять свои.
|
||||
- **Хосты** — каждая строка: имя хоста + переменные. Обязательно укажите **ansible_host** (IP или hostname) и **ansible_user** (пользователь для SSH).
|
||||
- **all:vars** — переменные для всех хостов (ключ SSH, опции подключения и т.д.).
|
||||
|
||||
Чтобы применять плейбук только к части серверов, в плейбуке можно использовать группы (например `hosts: web_servers`) или ограничивать запуск через `--limit` (см. ниже).
|
||||
|
||||
### 6.3. Проверка без изменений (dry-run)
|
||||
|
||||
Перед реальным деплоем полезно проверить, что плейбук выполнится без ошибок и что изменения именно те, что нужны:
|
||||
|
||||
```bash
|
||||
make role dryrun <имя_роли>
|
||||
# Пример:
|
||||
make role dryrun repo
|
||||
```
|
||||
|
||||
Команда запускает плейбук в режиме **check** (без реальных изменений) для указанной роли. Убедитесь, что в `inventory/hosts.ini` указаны ваши серверы и что с них есть SSH-доступ.
|
||||
|
||||
### 6.4. Реальный деплой плейбука на серверы
|
||||
|
||||
Когда инвентори заполнен и dry-run устраивает:
|
||||
|
||||
```bash
|
||||
make role deploy
|
||||
```
|
||||
|
||||
Будет выполнен плейбук **roles/deploy.yml** на всех хостах из **inventory/hosts.ini** (с подтверждением перед применением). При необходимости можно запускать Ansible вручную, например:
|
||||
|
||||
- Только на одной группе:
|
||||
```bash
|
||||
ansible-playbook -i inventory/hosts.ini roles/deploy.yml --limit web_servers
|
||||
```
|
||||
- Только проверка (аналог dry-run для всего плейбука):
|
||||
```bash
|
||||
ansible-playbook -i inventory/hosts.ini roles/deploy.yml --check --diff
|
||||
```
|
||||
|
||||
(Если Ansible запускается через контейнер в Makefile, пути и вызов могут отличаться; логика та же: тот же инвентори и тот же плейбук.)
|
||||
|
||||
### 6.5. Кратко по шагам деплоя
|
||||
|
||||
1. Заполнить **inventory/hosts.ini** (группы, хосты, ansible_host, ansible_user, ключ SSH).
|
||||
2. Проверить доступ: `ansible -i inventory/hosts.ini all -m ping` (если Ansible установлен локально).
|
||||
3. Запустить **make role dryrun <роль>** для проверки.
|
||||
4. Запустить **make role deploy** для применения плейбука к реальным серверам.
|
||||
|
||||
---
|
||||
|
||||
## Универсальность: Docker и Podman
|
||||
|
||||
Один и тот же сценарий (роли, плейбук, пресеты, инвентори) работает и с Docker, и с Podman. Отличия только в подготовке образов и в том, как вызываются контейнеры внутри Makefile.
|
||||
|
||||
| Действие | Podman (ветка podman) | Docker (ветка main) |
|
||||
|-----------------------|---------------------------|----------------------------|
|
||||
| Сборка образов | `make buildall` | `make docker build` |
|
||||
| Один образ | `make buildall-image IMAGE=ubuntu22` | `make docker build-image IMAGE=ubuntu22` |
|
||||
| Запуск тестов | `make role test [preset]` | то же |
|
||||
| Линт | `make role lint` | то же |
|
||||
| Деплой / инвентори | `make role deploy`, `inventory/hosts.ini` | то же |
|
||||
|
||||
- **Пресеты, роли, deploy.yml, inventory** — одни и те же.
|
||||
- **Линт, тесты, деплой** — одни и те же команды `make role ...`.
|
||||
- Разница только в том, как и откуда берутся образы (локальная сборка через `buildall` у Podman или сборка/пулл через `docker build` у Docker).
|
||||
|
||||
K8s в этом руководстве не рассматривается; см. отдельную документацию по Kubernetes при необходимости.
|
||||
@@ -10,8 +10,9 @@
|
||||
# Проверяем сначала в папке k8s, затем в основной папке presets
|
||||
preset_file: "{{ '/workspace/molecule/presets/k8s/' + preset_name + '.yml' if (preset_name in ['k8s-minimal', 'kubernetes', 'k8s-full'] or preset_name.startswith('k8s-')) else '/workspace/molecule/presets/' + preset_name + '.yml' }}"
|
||||
|
||||
# Fallback значения если preset файл не найден
|
||||
# Fallback значения если preset файл не найден (Podman использует ту же сеть)
|
||||
docker_network: labnet
|
||||
podman_network: "{{ docker_network }}"
|
||||
generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini"
|
||||
images:
|
||||
alt9: "inecs/ansible-lab:alt9-latest"
|
||||
@@ -89,20 +90,20 @@
|
||||
hosts: "{{ filtered_hosts | default(hosts) }}"
|
||||
|
||||
# =============================================================================
|
||||
# СЕТЕВОЕ ПОДКЛЮЧЕНИЕ
|
||||
# СЕТЕВОЕ ПОДКЛЮЧЕНИЕ (Podman)
|
||||
# =============================================================================
|
||||
- name: Network setup
|
||||
debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
НАСТРОЙКА СЕТИ
|
||||
НАСТРОЙКА СЕТИ (Podman)
|
||||
================================================================================
|
||||
Network: {{ docker_network }}
|
||||
Network: {{ podman_network | default(docker_network) }}
|
||||
================================================================================
|
||||
|
||||
- name: Ensure network exists
|
||||
community.docker.docker_network:
|
||||
name: "{{ docker_network }}"
|
||||
containers.podman.podman_network:
|
||||
name: "{{ podman_network | default(docker_network) }}"
|
||||
state: present
|
||||
|
||||
# =============================================================================
|
||||
@@ -117,39 +118,39 @@
|
||||
Count: {{ hosts | selectattr('type','undefined') | list | length }}
|
||||
================================================================================
|
||||
|
||||
- name: Pull systemd images with correct platform
|
||||
command: "docker pull --platform {{ ansible_architecture }} {{ images[item.family] }}"
|
||||
# Только локальные образы (registry запрещён). Сборка: make buildall
|
||||
- name: Проверка наличия локальных образов (Podman)
|
||||
command: "podman image exists {{ images[item.family] }}"
|
||||
loop: "{{ hosts | selectattr('type','undefined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
when: item.family is defined and images[item.family] is defined
|
||||
register: pull_result
|
||||
ignore_errors: yes
|
||||
register: image_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Display pull results
|
||||
debug:
|
||||
msg: "Pulled {{ item.item.name }}: {{ 'OK' if (item.rc is defined and item.rc == 0) else 'SKIPPED (not available for this platform)' }}"
|
||||
loop: "{{ pull_result.results | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.name }}"
|
||||
- name: Остановка при отсутствии локальных образов
|
||||
fail:
|
||||
msg: |
|
||||
Локальные образы не найдены (доступ к registry запрещён).
|
||||
Выполните: make buildall
|
||||
Отсутствуют образы: {{ image_check.results | default([]) | selectattr('rc', 'ne', 0) | map(attribute='item') | map(attribute='family') | list | join(', ') }}
|
||||
when: image_check.results is defined and (image_check.results | selectattr('rc', 'ne', 0) | list | length > 0)
|
||||
|
||||
- name: Start systemd nodes
|
||||
community.docker.docker_container:
|
||||
containers.podman.podman_container:
|
||||
name: "{{ item.name }}"
|
||||
image: "{{ images[item.family] }}"
|
||||
networks:
|
||||
- name: "{{ docker_network }}"
|
||||
network: "{{ podman_network | default(docker_network) }}"
|
||||
privileged: "{{ systemd_defaults.privileged }}"
|
||||
command: "{{ '/bin/bash -c \"while true; do sleep 30; done\"' if item.family in ['alt10', 'alt9'] else systemd_defaults.command }}"
|
||||
volumes: "{{ systemd_defaults.volumes | default([]) + (item.volumes | default([])) + ['/Users/inecs/PycharmProjects/DevOpsLab/vault:/workspace/vault:ro', '/Users/inecs/PycharmProjects/DevOpsLab/files:/workspace/files:ro', '/Users/inecs/PycharmProjects/DevOpsLab/roles:/workspace/roles:ro'] }}"
|
||||
volume: "{{ systemd_defaults.volumes | default([]) + (item.volumes | default([])) + ['/workspace/vault:/workspace/vault:ro', '/workspace/files:/workspace/files:ro', '/workspace/roles:/workspace/roles:ro'] }}"
|
||||
tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}"
|
||||
capabilities: "{{ systemd_defaults.capabilities | default([]) }}"
|
||||
published_ports: "{{ item.publish | default([]) }}"
|
||||
cap_add: "{{ systemd_defaults.capabilities | default([]) }}"
|
||||
ports: "{{ item.publish | default([]) }}"
|
||||
env: "{{ item.env | default({}) }}"
|
||||
# Специальные настройки для Astra Linux и RedOS
|
||||
security_opts: "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}"
|
||||
platform: "{{ item.docker_platform | default(item.platform) | default(omit) }}"
|
||||
security_opt: "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}"
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
restart_policy: "unless-stopped"
|
||||
loop: "{{ hosts | selectattr('type','undefined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
when: item.family is defined and images[item.family] is defined
|
||||
@@ -160,75 +161,44 @@
|
||||
seconds: 10
|
||||
when: hosts | length > 0
|
||||
|
||||
# Проверка готовности контейнеров
|
||||
# Проверка готовности контейнеров (Podman)
|
||||
- name: Wait for containers to be running
|
||||
community.docker.docker_container_info:
|
||||
name: "{{ item.name }}"
|
||||
command: "podman inspect --format '{{ '{{' }}.State.Running{{ '}}' }}' {{ item.name }}"
|
||||
register: container_info
|
||||
loop: "{{ hosts | selectattr('type','undefined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
when: item.family is defined and images[item.family] is defined
|
||||
retries: 10
|
||||
delay: 5
|
||||
until: container_info.container.State.Running | default(false)
|
||||
until: container_info.stdout == 'true'
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# DIND NODES - Создание контейнеров Docker-in-Docker
|
||||
# POoD NODES - Создание контейнеров Podman-out-of-Podman (сокет Podman)
|
||||
# =============================================================================
|
||||
- name: DinD nodes setup
|
||||
- name: POoD nodes setup
|
||||
debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
DIND NODES - Создание контейнеров Docker-in-Docker
|
||||
================================================================================
|
||||
Count: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }}
|
||||
================================================================================
|
||||
|
||||
- name: Start DinD nodes (docker:27-dind)
|
||||
community.docker.docker_container:
|
||||
name: "{{ item.name }}"
|
||||
image: "docker:27-dind"
|
||||
networks:
|
||||
- name: "{{ docker_network }}"
|
||||
privileged: true
|
||||
env:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
published_ports: "{{ item.publish | default([]) }}"
|
||||
volumes: "{{ (item.volumes | default([])) + [item.name + '-docker:/var/lib/docker'] }}"
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
|
||||
# =============================================================================
|
||||
# DOOD NODES - Создание контейнеров Docker-out-of-Docker
|
||||
# =============================================================================
|
||||
- name: DOoD nodes setup
|
||||
debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
DOOD NODES - Создание контейнеров Docker-out-of-Docker
|
||||
POoD NODES - Контейнеры с монтированием сокета Podman
|
||||
================================================================================
|
||||
Count: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }}
|
||||
================================================================================
|
||||
|
||||
- name: Start DOoD nodes (systemd + docker.sock mount)
|
||||
community.docker.docker_container:
|
||||
- name: Start POoD nodes (systemd + podman.sock mount)
|
||||
containers.podman.podman_container:
|
||||
name: "{{ item.name }}"
|
||||
image: "{{ images[item.family] }}"
|
||||
networks:
|
||||
- name: "{{ docker_network }}"
|
||||
network: "{{ podman_network | default(docker_network) }}"
|
||||
privileged: "{{ systemd_defaults.privileged }}"
|
||||
command: "{{ systemd_defaults.command }}"
|
||||
volumes: "{{ (systemd_defaults.volumes | default([])) + ['/var/run/docker.sock:/var/run/docker.sock'] + (item.volumes | default([])) + ['/Users/inecs/PycharmProjects/DevOpsLab/vault:/workspace/vault:ro', '/Users/inecs/PycharmProjects/DevOpsLab/files:/workspace/files:ro', '/Users/inecs/PycharmProjects/DevOpsLab/roles:/workspace/roles:ro'] }}"
|
||||
volume: "{{ (systemd_defaults.volumes | default([])) + ['/run/podman/podman.sock:/run/podman/podman.sock'] + (item.volumes | default([])) + ['/workspace/vault:/workspace/vault:ro', '/workspace/files:/workspace/files:ro', '/workspace/roles:/workspace/roles:ro'] }}"
|
||||
tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}"
|
||||
capabilities: "{{ systemd_defaults.capabilities | default([]) }}"
|
||||
published_ports: "{{ item.publish | default([]) }}"
|
||||
env: "{{ item.env | default({}) }}"
|
||||
platform: "{{ item.docker_platform | default(item.platform) | default(omit) }}"
|
||||
cap_add: "{{ systemd_defaults.capabilities | default([]) }}"
|
||||
ports: "{{ item.publish | default([]) }}"
|
||||
env: "{{ (item.env | default({})) | combine({'CONTAINER_HOST': 'unix:///run/podman/podman.sock'}) }}"
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
restart_policy: "unless-stopped"
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
when: item.family is defined and images[item.family] is defined
|
||||
@@ -264,7 +234,7 @@
|
||||
set_fact:
|
||||
inv_content: |
|
||||
[all:vars]
|
||||
ansible_connection=community.docker.docker
|
||||
ansible_connection=containers.podman.podman
|
||||
ansible_remote_tmp=/tmp/.ansible-tmp
|
||||
|
||||
{% for group, members in (groups_map | dictsort) %}
|
||||
|
||||
@@ -56,71 +56,37 @@
|
||||
Count: {{ hosts | length }} containers
|
||||
================================================================================
|
||||
|
||||
- name: Stop and remove containers
|
||||
community.docker.docker_container:
|
||||
- name: Stop and remove containers (Podman)
|
||||
containers.podman.podman_container:
|
||||
name: "{{ item.name }}"
|
||||
state: absent
|
||||
force_kill: true
|
||||
cleanup: true
|
||||
force_delete: true
|
||||
loop: "{{ hosts }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
ignore_errors: true
|
||||
|
||||
- name: Force remove any remaining containers
|
||||
shell: |
|
||||
docker ps -a --filter "name={{ item.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f
|
||||
podman ps -a --filter "name={{ item.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true
|
||||
loop: "{{ hosts }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove DinD volumes
|
||||
community.docker.docker_volume:
|
||||
name: "{{ item.name }}-docker"
|
||||
state: absent
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
ignore_errors: true
|
||||
|
||||
- name: Remove custom volumes
|
||||
community.docker.docker_volume:
|
||||
name: "{{ item.volumes | default([]) | select('match', '^[^:]+$') | list }}"
|
||||
state: absent
|
||||
loop: "{{ hosts }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
ignore_errors: true
|
||||
when: item.volumes is defined
|
||||
|
||||
# =============================================================================
|
||||
# ОЧИСТКА СЕТИ - Удаление Docker сети
|
||||
# =============================================================================
|
||||
- name: Network cleanup
|
||||
debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
ОЧИСТКА СЕТИ - Удаление Docker сети
|
||||
================================================================================
|
||||
Network: {{ docker_network }}
|
||||
================================================================================
|
||||
|
||||
- name: Remove network
|
||||
community.docker.docker_network:
|
||||
name: "{{ docker_network }}"
|
||||
containers.podman.podman_network:
|
||||
name: "{{ podman_network | default(docker_network) }}"
|
||||
state: absent
|
||||
ignore_errors: true
|
||||
|
||||
- name: Force cleanup all project containers
|
||||
shell: |
|
||||
# Удаляем все контейнеры из загруженного пресета
|
||||
{% for host in hosts %}
|
||||
docker ps -a --filter "name={{ host.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f 2>/dev/null || true
|
||||
podman ps -a --filter "name={{ host.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true
|
||||
{% endfor %}
|
||||
# Удаляем все контейнеры с образами ansible-lab
|
||||
docker ps -a --filter "ancestor=inecs/ansible-lab" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f 2>/dev/null || true
|
||||
# Удаляем все контейнеры с сетью labnet
|
||||
docker ps -a --filter "network=labnet" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f 2>/dev/null || true
|
||||
podman ps -a --filter "ancestor=inecs/ansible-lab" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true
|
||||
podman ps -a --filter "network=labnet" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true
|
||||
ignore_errors: true
|
||||
vars:
|
||||
# Используем переменную hosts из загруженного пресета
|
||||
hosts: "{{ hosts }}"
|
||||
|
||||
- name: Display cleanup summary
|
||||
@@ -131,8 +97,8 @@
|
||||
================================================================================
|
||||
Containers: {{ hosts | length }}
|
||||
Volumes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }}
|
||||
Network: {{ docker_network }}
|
||||
Clusters: {{ kind_clusters | default([]) | length }}
|
||||
Network: {{ podman_network | default(docker_network) }}
|
||||
Clusters: {{ minikube_profiles | default([]) | length }}
|
||||
================================================================================
|
||||
|
||||
- name: Display filtered hosts
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
|
||||
# Используем delegated: создание/удаление контейнеров в create.yml и destroy.yml через Podman
|
||||
# Автор: Сергей Антропов
|
||||
# Сайт: https://devops.org.ru
|
||||
driver:
|
||||
name: docker
|
||||
name: delegated
|
||||
|
||||
platforms:
|
||||
# Платформы будут созданы динамически через preset файлы
|
||||
|
||||
@@ -46,10 +46,8 @@
|
||||
Count: {{ hosts | selectattr('type','undefined') | list | length }}
|
||||
================================================================================
|
||||
|
||||
- name: Check systemd nodes status
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ item.name }}"
|
||||
command: systemctl is-system-running
|
||||
- name: Check systemd nodes status (Podman exec)
|
||||
command: "podman exec {{ item.name }} systemctl is-system-running"
|
||||
loop: "{{ hosts | selectattr('type','undefined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
register: systemd_status
|
||||
@@ -61,43 +59,23 @@
|
||||
loop: "{{ systemd_status.results | default([]) }}"
|
||||
when: systemd_status is defined
|
||||
|
||||
# Проверка DinD узлов
|
||||
- name: Check DinD nodes docker daemon
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ item.name }}"
|
||||
command: docker version --format '{{.Server.Version}}'
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
register: dind_status
|
||||
ignore_errors: true
|
||||
|
||||
- name: Display DinD nodes status
|
||||
debug:
|
||||
msg: "DinD node {{ item.0.name }}: Docker {{ item.1.stdout | default('not running') }}"
|
||||
loop: "{{ dind_status.results | default([]) }}"
|
||||
when: dind_status is defined
|
||||
|
||||
# Проверка DOoD узлов
|
||||
- name: Check DOoD nodes docker access
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ item.name }}"
|
||||
command: docker ps --format '{{.Names}}'
|
||||
# Проверка POoD узлов (Podman-out-of-Podman)
|
||||
- name: Check POoD nodes podman access
|
||||
command: "podman exec {{ item.name }} podman ps --format '{{'{{' }}.Names{{ '}}' }}'"
|
||||
loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
register: dood_status
|
||||
ignore_errors: true
|
||||
|
||||
- name: Display DOoD nodes status
|
||||
- name: Display POoD nodes status
|
||||
debug:
|
||||
msg: "DOoD node {{ item.0.name }}: Can access {{ item.1.stdout_lines | length | default(0) }} containers"
|
||||
msg: "POoD node {{ item.0.name }}: Can access {{ item.1.stdout_lines | length | default(0) }} containers"
|
||||
loop: "{{ dood_status.results | default([]) }}"
|
||||
when: dood_status is defined
|
||||
|
||||
# Проверка сетевого подключения
|
||||
- name: Test network connectivity between nodes
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ item.0.name }}"
|
||||
command: ping -c 1 {{ item.1.name }}
|
||||
command: "podman exec {{ item.0.name }} ping -c 1 {{ item.1.name }}"
|
||||
loop: "{{ hosts | subelements(hosts, 'name') }}"
|
||||
loop_control: { label: "{{ item.0.name }} -> {{ item.1.name }}" }
|
||||
when: item.0.name != item.1.name
|
||||
@@ -112,9 +90,7 @@
|
||||
|
||||
# Проверка портов
|
||||
- name: Check published ports
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ item.name }}"
|
||||
command: netstat -tlnp
|
||||
command: "podman exec {{ item.name }} netstat -tlnp 2>/dev/null || podman exec {{ item.name }} ss -tlnp"
|
||||
loop: "{{ hosts | selectattr('publish','defined') | list }}"
|
||||
loop_control: { label: "{{ item.name }}" }
|
||||
register: port_status
|
||||
@@ -139,11 +115,10 @@
|
||||
- name: Display verification summary
|
||||
debug:
|
||||
msg: |
|
||||
✅ Verification Summary:
|
||||
✅ Verification Summary (Podman):
|
||||
- Total hosts: {{ hosts | length }}
|
||||
- Systemd nodes: {{ hosts | selectattr('type','undefined') | list | length }}
|
||||
- DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }}
|
||||
- DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }}
|
||||
- POoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }}
|
||||
- Groups: {{ groups_map.keys() | list | join(', ') }}
|
||||
- Network: {{ docker_network }}
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ systemd_defaults:
|
||||
tmpfs: ["/run", "/run/lock"]
|
||||
capabilities: ["SYS_ADMIN"]
|
||||
|
||||
# Минимальный Kind кластер без аддонов
|
||||
kind_clusters:
|
||||
- name: minimal
|
||||
workers: 0 # Только control-plane
|
||||
api_port: 6443
|
||||
# Minikube (драйвер podman) — минимальный кластер без аддонов
|
||||
minikube_profile: minikube
|
||||
minikube_cpus: "2"
|
||||
minikube_memory: "4096"
|
||||
minikube_addons: []
|
||||
|
||||
hosts: []
|
||||
|
||||
74
scripts/create_minikube_cluster.py
Normal file
74
scripts/create_minikube_cluster.py
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Скрипт для создания Minikube кластера с драйвером Podman.
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
import sys
|
||||
import yaml
|
||||
import subprocess
|
||||
|
||||
|
||||
def run_cmd(cmd, check=True):
|
||||
"""Выполнить команду на хосте."""
|
||||
print(f"[run] {cmd}")
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if check and result.returncode != 0:
|
||||
print(f"[error] {result.stderr}")
|
||||
sys.exit(1)
|
||||
if result.stdout:
|
||||
print(result.stdout)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: create_minikube_cluster.py <preset_file>")
|
||||
print(" Создаёт Minikube кластер с драйвером podman и опционально включает аддоны из пресета.")
|
||||
sys.exit(1)
|
||||
|
||||
preset_file = sys.argv[1]
|
||||
print(f"📋 Читаю пресет: {preset_file}")
|
||||
|
||||
with open(preset_file, "r", encoding="utf-8") as f:
|
||||
preset = yaml.safe_load(f) or {}
|
||||
|
||||
profile = preset.get("minikube_profile", "minikube")
|
||||
addons = preset.get("minikube_addons", [])
|
||||
cpus = preset.get("minikube_cpus", "2")
|
||||
memory = preset.get("minikube_memory", "4096")
|
||||
|
||||
print(f"\n☸️ Создание Minikube кластера (драйвер: podman)")
|
||||
print(f" Профиль: {profile}")
|
||||
print(f" CPU: {cpus}, Memory: {memory}")
|
||||
|
||||
# Проверяем, запущен ли уже кластер
|
||||
result = subprocess.run(
|
||||
f"minikube profile list 2>/dev/null | grep -E '^{profile}' | grep 'Running'",
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
print(f"⚠️ Кластер с профилем '{profile}' уже запущен.")
|
||||
print(" Для пересоздания выполните: minikube delete -p " + profile)
|
||||
else:
|
||||
run_cmd(
|
||||
f"minikube start --driver=podman --profile={profile} --cpus={cpus} --memory={memory}"
|
||||
)
|
||||
print(f"✅ Minikube кластер '{profile}' создан и запущен.")
|
||||
|
||||
# Включаем аддоны из пресета
|
||||
if addons:
|
||||
print(f"\n📦 Включение аддонов: {', '.join(addons)}")
|
||||
for addon in addons:
|
||||
run_cmd(f"minikube addons enable {addon} -p {profile}", check=False)
|
||||
|
||||
print("\n🎉 Готово. Использование:")
|
||||
print(f" kubectl config use-context {profile}")
|
||||
print(" minikube kubectl -- get nodes")
|
||||
print(" minikube dashboard -p " + profile)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -29,11 +29,11 @@ def main():
|
||||
host_name = host['name']
|
||||
|
||||
# Проверяем существование контейнера
|
||||
result = subprocess.run(f"docker ps -a --format '{{{{.Names}}}}' | grep -x {host_name}",
|
||||
result = subprocess.run(f"podman ps -a --format '{{{{.Names}}}}' | grep -x {host_name}",
|
||||
shell=True, capture_output=True, text=True)
|
||||
if result.stdout.strip():
|
||||
print(f"🗑️ Удаление контейнера: {host_name}")
|
||||
subprocess.run(f"docker rm -f {host_name}", shell=True, capture_output=True, text=True)
|
||||
subprocess.run(f"podman rm -f {host_name}", shell=True, capture_output=True, text=True)
|
||||
print(f"✅ Контейнер '{host_name}' удален")
|
||||
else:
|
||||
print(f"⚠️ Контейнер '{host_name}' не найден")
|
||||
|
||||
@@ -9,23 +9,18 @@ import subprocess
|
||||
import json
|
||||
|
||||
def get_cluster_name():
|
||||
"""Получает имя кластера"""
|
||||
result = subprocess.run("docker exec k8s-controller kind get clusters | head -1", shell=True, capture_output=True, text=True)
|
||||
"""Получает имя текущего контекста (Minikube)."""
|
||||
result = subprocess.run(
|
||||
"kubectl config current-context 2>/dev/null", shell=True, capture_output=True, text=True
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return result.stdout.strip()
|
||||
return None
|
||||
|
||||
def run_kubectl_cmd(cmd):
|
||||
"""Выполняет команду kubectl внутри контейнера k8s-controller"""
|
||||
cluster_name = get_cluster_name()
|
||||
if cluster_name:
|
||||
# Используем прямой адрес control-plane
|
||||
server = f"https://{cluster_name}-control-plane:6443"
|
||||
cmd_with_server = f"--server={server} --insecure-skip-tls-verify {cmd}"
|
||||
else:
|
||||
cmd_with_server = cmd
|
||||
|
||||
full_cmd = f"docker exec k8s-controller kubectl {cmd_with_server}"
|
||||
def run_kubectl_cmd(cmd):
|
||||
"""Выполняет команду kubectl на хосте (контекст Minikube)."""
|
||||
full_cmd = f"kubectl {cmd}"
|
||||
result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
|
||||
return result.stdout
|
||||
|
||||
|
||||
@@ -12,11 +12,13 @@ import signal
|
||||
import time
|
||||
|
||||
def get_cluster_name():
|
||||
"""Получаем имя кластера из preset файла"""
|
||||
"""Получаем имя профиля Minikube из preset файла"""
|
||||
preset_file = "molecule/presets/k8s/kubernetes.yml"
|
||||
with open(preset_file, 'r') as f:
|
||||
preset = yaml.safe_load(f)
|
||||
return preset['kind_clusters'][0]['name']
|
||||
if not os.path.exists(preset_file):
|
||||
return "minikube"
|
||||
with open(preset_file, "r", encoding="utf-8") as f:
|
||||
preset = yaml.safe_load(f) or {}
|
||||
return preset.get("minikube_profile", "minikube")
|
||||
|
||||
def run_cmd(cmd):
|
||||
"""Выполняет команду и возвращает результат"""
|
||||
@@ -80,38 +82,24 @@ def clear_portforwards():
|
||||
pass
|
||||
|
||||
def create_portforwards():
|
||||
"""Создает port-forward для всех сервисов из preset на локальном компьютере"""
|
||||
# Загружаем preset
|
||||
"""Создает port-forward для всех сервисов из preset на локальном компьютере (Minikube)."""
|
||||
preset_file = "molecule/presets/k8s/kubernetes.yml"
|
||||
with open(preset_file, 'r') as f:
|
||||
preset = yaml.safe_load(f)
|
||||
preset = {}
|
||||
if os.path.exists(preset_file):
|
||||
with open(preset_file, "r", encoding="utf-8") as f:
|
||||
preset = yaml.safe_load(f) or {}
|
||||
# Поддержка minikube_profile и addon_ports (на верхнем уровне или в kind_clusters[0])
|
||||
cluster_name = preset.get("minikube_profile", "minikube")
|
||||
addon_ports = preset.get("addon_ports") or (preset.get("kind_clusters") or [{}])[0].get("addon_ports", {})
|
||||
|
||||
cluster_name = preset['kind_clusters'][0]['name']
|
||||
addon_ports = preset['kind_clusters'][0].get('addon_ports', {})
|
||||
|
||||
# Получаем kubeconfig из контейнера k8s-controller
|
||||
print(f"🔌 Создание port-forward для кластера: {cluster_name}")
|
||||
print("📋 Получение kubeconfig из контейнера k8s-controller...")
|
||||
|
||||
# Копируем kubeconfig из контейнера
|
||||
result = subprocess.run(
|
||||
f"docker exec k8s-controller kind get kubeconfig --name {cluster_name}",
|
||||
shell=True, capture_output=True, text=True
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"❌ Ошибка получения kubeconfig: {result.stderr}")
|
||||
# Minikube обновляет ~/.kube/config — используем его
|
||||
kubeconfig_file = os.environ.get("KUBECONFIG", os.path.expanduser("~/.kube/config"))
|
||||
if not os.path.exists(kubeconfig_file):
|
||||
print(f"❌ Kubeconfig не найден: {kubeconfig_file}. Запустите: make k8s create")
|
||||
return
|
||||
|
||||
# Сохраняем kubeconfig во временный файл
|
||||
kubeconfig_file = "/tmp/kubeconfig-lab.yaml"
|
||||
with open(kubeconfig_file, 'w') as f:
|
||||
f.write(result.stdout)
|
||||
|
||||
# Меняем server с 0.0.0.0 на localhost для локального доступа
|
||||
subprocess.run(f"sed -i.bak 's|server: https://0.0.0.0:6443|server: https://localhost:6443|g' {kubeconfig_file}", shell=True)
|
||||
|
||||
print("✅ Kubeconfig подготовлен")
|
||||
print(f"🔌 Создание port-forward для Minikube (профиль: {cluster_name})")
|
||||
print(f"📋 Kubeconfig: {kubeconfig_file}")
|
||||
|
||||
# Ingress HTTP (80)
|
||||
if addon_ports.get('ingress_http'):
|
||||
|
||||
Reference in New Issue
Block a user