первый фикс

This commit is contained in:
Sergey Antropoff
2026-04-17 08:58:26 +03:00
parent 095b276cb3
commit d9a35478a6
9 changed files with 312 additions and 150 deletions

View File

@@ -30,6 +30,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
# ── Docker CLI (для Molecule docker driver) ───────────────────────────────────
RUN install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/debian/gpg \
-o /etc/apt/keyrings/docker.asc \
&& 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/debian bookworm stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt-get update \
&& apt-get install -y --no-install-recommends docker-ce-cli \
&& rm -rf /var/lib/apt/lists/*
# ── Python зависимости (Ansible + плагины) ────────────────────────────────────
COPY requirements-python.txt /tmp/requirements-python.txt
RUN pip install --no-cache-dir -r /tmp/requirements-python.txt

View File

@@ -23,6 +23,15 @@ BOLD := \033[1m
NC := \033[0m
# ── Базовая команда запуска контейнера ────────────────────────────────────────
# Molecule запускается тоже из контейнера — монтируем Docker socket для DinD
DOCKER_RUN_MOLECULE := docker run --rm -it \
--name $(CONTAINER_NAME)-molecule \
-v $(PWD):/ansible \
-v /var/run/docker.sock:/var/run/docker.sock \
-e ANSIBLE_FORCE_COLOR=1 \
-e MOLECULE_NO_LOG=0 \
$(IMAGE_NAME)
DOCKER_RUN := docker run --rm -it \
--name $(CONTAINER_NAME) \
--network host \
@@ -45,7 +54,7 @@ DOCKER_RUN := docker run --rm -it \
molecule-k3s molecule-prometheus molecule-istio molecule-all molecule-lint \
vault-create vault-edit vault-view vault-encrypt-string \
clean clean-all \
_check_env _check_image _check_molecule
_check_env _check_image
# ── DEFAULT ───────────────────────────────────────────────────────────────────
.DEFAULT_GOAL := help
@@ -246,32 +255,31 @@ vault-encrypt-string: _check_image ## Зашифровать строку: make
# Требования: pip install -r requirements-python.txt (molecule, molecule-plugins[docker])
# ═══════════════════════════════════════════════════════════════════════════════
molecule-k3s: _check_molecule ## Тест роли k3s (Docker, ~5 мин)
@printf "$(CYAN)Тестирую роль k3s...$(NC)\n"
cd roles/k3s && molecule test
molecule-k3s: _check_image ## Тест роли k3s — 3 контейнера (Ubuntu+Debian), ~8-12 мин
@printf "$(CYAN)Тестирую роль k3s (3 ноды: master01, worker01, rpi01)...$(NC)\n"
$(DOCKER_RUN_MOLECULE) molecule k3s
@printf "$(GREEN)✓ k3s role: OK$(NC)\n"
molecule-prometheus: _check_molecule ## Тест роли prometheus-stack (шаблоны + PVC)
molecule-prometheus: _check_image ## Тест роли prometheus-stack (шаблоны + PVC), ~2-3 мин
@printf "$(CYAN)Тестирую роль prometheus-stack...$(NC)\n"
cd roles/prometheus-stack && molecule test
$(DOCKER_RUN_MOLECULE) molecule prometheus-stack
@printf "$(GREEN)✓ prometheus-stack role: OK$(NC)\n"
molecule-istio: _check_molecule ## Тест роли istio + kiali (шаблоны)
molecule-istio: _check_image ## Тест роли istio + kiali (шаблоны), ~2-3 мин
@printf "$(CYAN)Тестирую роль istio...$(NC)\n"
cd roles/istio && molecule test
$(DOCKER_RUN_MOLECULE) molecule istio
@printf "$(GREEN)✓ istio role: OK$(NC)\n"
molecule-all: _check_molecule ## Запустить все Molecule тесты последовательно
molecule-all: _check_image ## Запустить все Molecule тесты последовательно (~15-20 мин)
@printf "$(CYAN)$(BOLD)Запуск всех Molecule тестов...$(NC)\n"
$(MAKE) molecule-k3s
$(MAKE) molecule-prometheus
$(MAKE) molecule-istio
@printf "$(GREEN)$(BOLD)Все тесты прошли успешно$(NC)\n"
molecule-lint: _check_molecule ## Только линтинг (yamllint + ansible-lint), без Docker
molecule-lint: _check_image ## Линтинг (yamllint + ansible-lint) в контейнере, ~30 сек
@printf "$(CYAN)Запуск линтинга...$(NC)\n"
yamllint .
ansible-lint
$(DOCKER_RUN_MOLECULE) molecule-lint
@printf "$(GREEN)✓ Линтинг прошёл$(NC)\n"
# ═══════════════════════════════════════════════════════════════════════════════
@@ -318,9 +326,4 @@ _check_image:
$(MAKE) build; \
fi
_check_molecule:
@if ! python3 -c "import molecule" 2>/dev/null; then \
printf "$(RED)✗ Molecule не установлен!$(NC)\n"; \
printf "$(YELLOW) Запусти: pip install -r requirements-python.txt$(NC)\n"; \
exit 1; \
fi

325
README.md
View File

@@ -47,7 +47,7 @@
│ │ workloads ✓ │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
│ │
│ StorageClass: nfs-client (default)
│ StorageClass: nfs-master01 (default) │
│ └── /srv/nfs/k8s на master01 │
└──────────────────────────────────────────────────────────────┘
```
@@ -77,6 +77,27 @@
| Helm | 3.14.4 | Внутри Docker контейнера |
| kubectl | v1.29.3 | Внутри Docker контейнера |
### CNI плагин
По умолчанию используется встроенный в K3S **Flannel**. Переключение — одна переменная в `group_vars/all/main.yml`:
```yaml
k3s_cni: "flannel" # flannel (по умолч.) | calico | cilium
```
| CNI | Режим | Особенности |
|---|---|---|
| `flannel` | встроен в K3S | VXLAN overlay, минимальная настройка |
| `calico` | Tigera operator | Network Policy, IPAM, BGP, VXLAN/IPIP |
| `cilium` | Helm | eBPF dataplane, Hubble observability, L7 policy |
При `calico` или `cilium` — Flannel автоматически отключается в конфиге K3S (`flannel-backend: none`, `disable-network-policy: true`).
Установить отдельно:
```bash
make install-cni K3S_CNI=calico # или cilium
```
---
## Требования
@@ -89,12 +110,7 @@
| **make** | любая | `apt install make` / `brew install make` |
| **SSH ключ** | — | `ssh-keygen -t ed25519` |
Ansible, Helm, kubectl — устанавливать **не нужно**, они внутри Docker.
Для Molecule-тестирования дополнительно нужен Python 3.9+:
```bash
pip install -r requirements-python.txt
```
Ansible, Helm, kubectl, Molecule — устанавливать **не нужно**. Всё работает внутри Docker-контейнера.
### На серверах кластера
@@ -254,9 +270,13 @@ master01
Отредактируй `group_vars/all/main.yml`. Обязательные поля:
```yaml
kube_vip_address: "192.168.1.100" # свободный IP, не в DHCP пуле
kube_vip_interface: "eth0" # узнать: ssh ubuntu@192.168.1.10 "ip -br a"
# kube_vip_interface: "" # пусто = автоопределение (eth0/enp3s0/end0/...)
# переопредели только если авто не работает
k3s_cni: "flannel" # flannel (умолч.) | calico | cilium
```
`kube_vip_interface` определяется автоматически через `ansible_default_ipv4.interface` — работает для Ubuntu, Debian, Raspberry Pi OS без дополнительных настроек.
### Шаг 6 — Vault с секретами
```bash
@@ -286,16 +306,18 @@ make build
### Шаг 8 — Прогнать тесты (рекомендуется)
Перед деплоем убедись что роли корректны:
Перед деплоем убедись что роли корректны. Molecule запускается **внутри Docker** — устанавливать ничего не нужно.
```bash
# Установить зависимости для Molecule (один раз)
pip install -r requirements-python.txt
# Убедись что образ собран (если ещё не сделал на шаге 7):
# make build
# Запустить все тесты (~10-15 минут)
# Запустить все тесты (~15-20 минут)
make molecule-all
```
Что поднимается: 3 контейнера (master01/worker01 на Ubuntu 22.04 + rpi01 на Debian 12), тест конфигурации K3S HA, шаблонов Prometheus и Istio.
Если всё зелёное — можно деплоить. Если есть ошибки — смотри раздел [Тестирование через Molecule](#тестирование-через-molecule).
### Шаг 9 — Проверить SSH и dry-run
@@ -316,13 +338,15 @@ make install
```
Плейбук выполняет всё последовательно (`serial: 1`):
1. master01: prereqs → K3S server (cluster-init) → ждёт готовности API → kube-vip
1. master01: prereqs → K3S server (cluster-init) → ждёт готовности API
2. worker01: prereqs → K3S server (join) → ждёт готовности ноды
3. rpi01: prereqs + cgroups → K3S server (join) → применяет taint NoSchedule
4. NFS server на master01
5. CSI NFS Driver на всех нодах
6. ingress-nginx через Helm
7. Финальная проверка: nodes / pods / svc / storageclass
4. CNI плагин (если не Flannel — устанавливается Calico или Cilium)
5. kube-vip: VIP + LoadBalancer (интерфейс определяется автоматически)
6. NFS server на master01
7. CSI NFS Driver на всех нодах → StorageClass `nfs-master01`
8. ingress-nginx через Helm
9. Финальная проверка: nodes / pods / svc / storageclass
Ожидаемое время: **15-25 минут** в зависимости от скорости интернета.
@@ -345,8 +369,8 @@ kubectl get svc -n ingress-nginx
# ingress-nginx-controller LoadBalancer 192.168.1.100 80:xxx/443:xxx
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE
# nfs-client (default) nfs.csi.k8s.io Delete Immediate
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE
# nfs-master01 (default) nfs.csi.k8s.io Delete Immediate
```
### Шаг 12 (опционально) — Установить Istio + мониторинг
@@ -411,18 +435,47 @@ make install ANSIBLE_TAGS=monitoring # Prometheus stack
k3s_version: "v1.29.3+k3s1"
k3s_cluster_cidr: "10.42.0.0/16"
k3s_service_cidr: "10.43.0.0/16"
k3s_flannel_backend: "vxlan" # vxlan | wireguard-native | host-gw
k3s_flannel_backend: "vxlan" # vxlan | wireguard-native | host-gw (только для Flannel)
k3s_disable_traefik: true # ОБЯЗАТЕЛЬНО true при ingress-nginx
# CNI выбирается здесь:
k3s_cni: "flannel" # flannel | calico | cilium
```
### CNI
**Flannel** (по умолчанию) — ничего дополнительно настраивать не нужно.
**Calico:**
```yaml
k3s_cni: "calico"
calico_version: "v3.28.0"
calico_encapsulation: "VXLAN" # VXLAN | IPIP | None
```
**Cilium:**
```yaml
k3s_cni: "cilium"
cilium_version: "1.15.5"
cilium_hubble_enabled: true # Hubble observability
cilium_hubble_ui_enabled: false # Hubble UI (ресурсоёмко)
# cilium_k8s_service_host автоматически = kube_vip_address
```
При смене CNI необходима переустановка кластера — CNI нельзя поменять "горячей" заменой.
### kube-vip
```yaml
kube_vip_address: "192.168.1.100" # Свободный IP — обязательно задать!
kube_vip_interface: "eth0" # Интерфейс master01 (ip a)
kube_vip_interface: "" # пусто = автоопределение
# Ansible определит eth0/enp3s0/end0 автоматически
# Переопредели только если нужно: "eth0"
kube_vip_mode: "arp" # arp (L2) для домашних сетей | bgp (L3)
```
Автоопределение интерфейса работает через `ansible_default_ipv4.interface` — корректно определяет интерфейс на Ubuntu, Debian, Raspberry Pi OS. Если у разных нод разные имена интерфейсов — задай `kube_vip_interface` принудительно.
### NFS / CSI
```yaml
@@ -432,6 +485,10 @@ nfs_exports:
nfs_allowed_network: "192.168.1.0/24"
csi_nfs_reclaim_policy: "Delete" # Delete | Retain
# StorageClass именуется автоматически: nfs-<hostname NFS сервера>
# Например: nfs-master01 (если NFS на master01)
# Переопредели только если нужно другое имя:
# csi_nfs_storageclass_name: "my-nfs"
```
### ingress-nginx
@@ -682,6 +739,7 @@ make lint # Проверить синтаксис плейбуков
```bash
make install # Полный базовый стек
make install-k3s # Только K3S HA кластер
make install-cni # CNI плагин (make install-cni K3S_CNI=calico|cilium)
make install-kubevip # Только kube-vip
make install-nfs # NFS + CSI
make install-ingress # ingress-nginx
@@ -689,14 +747,14 @@ make install-istio # Istio + Kiali (нужен istio_enabled: true в var
make install-monitoring # Prometheus + Grafana (нужен prometheus_stack_enabled: true)
```
### Тестирование (Molecule)
### Тестирование (Molecule — всё в Docker, pip не нужен)
```bash
make molecule-k3s # Тест роли k3s (~5-8 мин)
make molecule-k3s # Тест роли k3s — 3 контейнера (Ubuntu+Debian), ~8-12 мин
make molecule-prometheus # Тест роли prometheus-stack (~2-3 мин)
make molecule-istio # Тест роли istio (~2-3 мин)
make molecule-all # Все тесты последовательно (~15 мин)
make molecule-lint # Только линтинг YAML+ansible-lint (без Docker, ~30 сек)
make molecule-all # Все тесты последовательно (~15-20 мин)
make molecule-lint # Линтинг YAML+ansible-lint в контейнере (~30 сек)
```
### Обновление и диагностика
@@ -737,22 +795,22 @@ EXTRA_VARS="k3s_version=v1.30.0+k3s1" make install-k3s # Доп. пер
## Тестирование через Molecule
Molecule — стандартный инструмент для тестирования Ansible ролей. Каждая роль запускается в Docker-контейнере, проходит набор автоматических проверок и удаляется. Реальные серверы при этом **не нужны**.
Molecule — стандартный инструмент для тестирования Ansible ролей. Каждая роль запускается в Docker-контейнерах, проходит набор автоматических проверок и удаляется. Реальные серверы при этом **не нужны**.
### Установка
### Требования
Только **Docker** — Molecule, Python и зависимости уже внутри образа `k3s-ansible`. Устанавливать ничего дополнительно не нужно.
```bash
# Установить зависимости (один раз)
pip install -r requirements-python.txt
# Убедись что образ собран:
make build
# Проверить
molecule --version
# molecule 6.x.x ...
docker --version
# Docker version 24.x.x ...
# Запустить тесты (всё в Docker):
make molecule-k3s
```
Как это работает: `make molecule-*` запускает контейнер `k3s-ansible` с примонтированным Docker socket (`/var/run/docker.sock`). Внутри этого контейнера Molecule создаёт тестовые контейнеры для каждой роли — **Docker внутри Docker без демона** (только socket от хоста).
### Жизненный цикл теста
Команда `molecule test` выполняет следующие фазы по порядку:
@@ -774,31 +832,36 @@ destroy → удалить Docker-контейнер
### Что тестирует каждая роль
#### Роль `k3s` (privileged Docker, ~5-8 мин)
#### Роль `k3s` (3 контейнера, privileged Docker, ~8-12 мин)
Использует Docker-контейнер с **systemd** в привилегированном режиме — это необходимо для тестирования `apt`, `sysctl` и модулей ядра.
Поднимает **3 контейнера**, точно как в inventory:
| Контейнер | Образ | Соответствует |
|---|---|---|
| `master01` | `geerlingguy/docker-ubuntu2204-ansible` | Ubuntu 22.04 x86_64 |
| `worker01` | `geerlingguy/docker-ubuntu2204-ansible` | Ubuntu 22.04 x86_64 |
| `rpi01` | `geerlingguy/docker-debian12-ansible` | Debian 12 (Raspberry Pi OS) |
Все контейнеры — privileged с systemd (нужно для `apt`, `sysctl`, модулей ядра).
`rpi01` получает `ansible_python_interpreter=/usr/bin/python3` и `NoSchedule` taint — как в реальном inventory.
**Тестируемые задачи:**
- `prereqs.yml`установка пакетов (`curl`, `ca-certificates`, `iptables`), отключение swap, загрузка модулей ядра (`overlay`, `br_netfilter`), настройка sysctl
**Рендеринг шаблона:**
- `k3s-server-config.yaml.j2``/etc/rancher/k3s/config.yaml`
- `prereqs.yml`пакеты, swap, модули ядра, sysctl
- Рендеринг `k3s-server-config.yaml.j2``/etc/rancher/k3s/config.yaml`
**Что проверяет `verify.yml`:**
| Проверка | Что именно |
|---|---|
| Директория `/etc/rancher/k3s` | Создана роль prereqs |
| Файл `config.yaml` | Существует и имеет права `0600` |
| YAML синтаксис | Файл парсится как корректный YAML |
| `token` | Установлен из переменной |
| `cluster-cidr` | Равен `10.42.0.0/16` |
| `service-cidr` | Равен `10.43.0.0/16` |
| `cluster-init: true` | Первый мастер инициализирует кластер |
| `disable: [traefik]` | Traefik выключен (используется ingress-nginx) |
| `curl` установлен | Системный пакет |
| `iptables` установлен | Системный пакет |
| `net.ipv4.ip_forward = 1` | sysctl параметр применён |
| Проверка | Нода | Что именно |
|---|---|---|
| Директория `/etc/rancher/k3s` | все | Создана роль prereqs |
| Файл `config.yaml` | все | Существует, права `0600` |
| `cluster-init: true` | master01 | Первый мастер инициализирует кластер |
| `server: https://...:6443` | worker01, rpi01 | Присоединяются к master01 |
| `cluster-cidr` | все | Равен `10.42.0.0/16` |
| `disable: [traefik]` | все | Traefik выключен |
| `curl`, `iptables` | все | Системные пакеты |
| `net.ipv4.ip_forward = 1` | все | sysctl применён |
#### Роль `prometheus-stack` (lightweight Docker, ~2-3 мин)
@@ -844,18 +907,16 @@ destroy → удалить Docker-контейнер
### Запуск тестов: пошагово
#### Быстрая проверка (lint, без Docker)
#### Линтинг (в контейнере, ~30 сек)
```bash
make molecule-lint
```
Запускает `yamllint .` и `ansible-lint` на всём проекте. Выполняется за ~30 секунд, Docker не нужен. Используй перед каждым коммитом.
Запускает `yamllint .` и `ansible-lint` на всём проекте внутри Docker-контейнера. Используй перед каждым коммитом.
```
Запуск линтинга...
yamllint .
ansible-lint
✓ Линтинг прошёл
```
@@ -865,70 +926,58 @@ ansible-lint
make molecule-k3s
```
Вывод (успешный):
Вывод (успешный, сокращён):
```
INFO default scenario test matrix: dependency, lint, cleanup, destroy, syntax, create, prepare, converge, idempotency, verify, cleanup, destroy
INFO Performing prerun with role_name_check=1...
INFO Running default > create
INFO Sanity checks: 'docker'
TASK [Create instance(s)] ****
changed: [localhost] => (item=master01)
changed: [localhost] => (item=worker01)
changed: [localhost] => (item=rpi01)
INFO Running default > prepare
PLAY [Prepare k3s test environment] ****
TASK [Wait for systemd to start] ***
ok: [k3s-node]
TASK [Install Python3] ***
changed: [k3s-node]
ok: [master01]
ok: [worker01]
ok: [rpi01]
INFO Running default > converge
PLAY [Converge — k3s role unit tests] ****
TASK [Mock k3s binary] ***
changed: [k3s-node]
TASK [Test prereqs — install packages] ***
changed: [k3s-node] => (item=curl)
changed: [k3s-node] => (item=iptables)
changed: [master01]
changed: [worker01]
changed: [rpi01]
TASK [Test server config template rendering] ***
changed: [k3s-node]
changed: [master01]
changed: [worker01]
changed: [rpi01]
INFO Running default > idempotency
PLAY [Converge — k3s role unit tests] ****
...
PLAY RECAP
k3s-node : ok=12 changed=0 unreachable=0 failed=0
master01 : ok=12 changed=0 unreachable=0 failed=0
worker01 : ok=12 changed=0 unreachable=0 failed=0
rpi01 : ok=12 changed=0 unreachable=0 failed=0
INFO Idempotency completed successfully.
INFO Running default > verify
PLAY [Verify — k3s role] ****
TASK [Assert cluster-init is set (только master01)] ***
ok: [master01] => {"msg": "All assertions passed"}
skipping: [worker01]
skipping: [rpi01]
TASK [Assert config directory] ***
ok: [k3s-node] => {"changed": false, "msg": "All assertions passed"}
TASK [Assert config file exists] ***
ok: [k3s-node] => {"changed": false, "msg": "All assertions passed"}
TASK [Assert cluster-init is set] ***
ok: [k3s-node] => {"changed": false, "msg": "All assertions passed"}
TASK [Assert traefik is disabled] ***
ok: [k3s-node] => {"changed": false, "msg": "All assertions passed"}
TASK [Assert server URL is set (worker01 и rpi01)] ***
skipping: [master01]
ok: [worker01] => {"msg": "All assertions passed"}
ok: [rpi01] => {"msg": "All assertions passed"}
TASK [Assert ip_forward is 1] ***
ok: [k3s-node] => {"changed": false, "msg": "All assertions passed"}
TASK [Summary] ***
ok: [k3s-node] => {
"msg": "Все проверки прошли успешно для ноды k3s-node"
}
ok: [master01]
ok: [worker01]
ok: [rpi01]
INFO Running default > destroy
INFO Pruning extra files from scenario ephemeral directory
✓ k3s role: OK
```
@@ -939,7 +988,7 @@ make molecule-all
```
```
Тестирую роль k3s...
Тестирую роль k3s (3 ноды: master01, worker01, rpi01)...
...
✓ k3s role: OK
@@ -958,29 +1007,37 @@ make molecule-all
### Отладка упавших тестов
Если тест упал — контейнер удаляется автоматически. Чтобы оставить контейнер живым для ручной отладки, используй отдельные фазы:
Если тест упал — контейнеры удаляются автоматически. Чтобы оставить контейнеры живыми для ручной отладки, войди в интерактивный shell внутри Molecule-контейнера и запускай отдельные фазы:
```bash
# Перейти в директорию роли
cd roles/prometheus-stack
# Открыть shell внутри ansible-runner контейнера с Docker socket
docker run --rm -it \
-v $(pwd):/ansible \
-v /var/run/docker.sock:/var/run/docker.sock \
--entrypoint bash \
k3s-ansible
# Только создать контейнер и запустить задачи (без удаления)
molecule converge
# Внутри контейнера — перейти к роли и запускать фазы вручную:
cd /ansible/roles/prometheus-stack
# Проверить вручную внутри контейнера
molecule login
# Теперь ты внутри Docker-контейнера:
cat /tmp/prometheus-stack-values.yaml
python3 -c "import yaml; yaml.safe_load(open('/tmp/prometheus-stack-values.yaml'))"
exit
molecule converge # создать контейнер и запустить задачи (без удаления)
molecule login # войти в тестовый контейнер
# Внутри: cat /tmp/prometheus-stack-values.yaml
# Внутри: exit
# Запустить только verify (assertions)
molecule verify # только assertions
molecule converge -- -vvv # подробный вывод
molecule destroy # удалить тестовый контейнер
```
Для роли k3s, которая поднимает 3 контейнера:
```bash
cd /ansible/roles/k3s
molecule converge # поднимет master01, worker01, rpi01
molecule login --host master01 # войти в конкретный контейнер
molecule login --host rpi01
molecule verify
# Посмотреть полный вывод с -vvv
molecule converge -- -vvv
# Удалить контейнер когда закончил
molecule destroy
```
@@ -995,18 +1052,23 @@ molecule destroy
| `ansible-lint: no-changed-when` | Таск shell/command без `changed_when` | Добавь `changed_when: <условие>` к задаче |
| `sysctl: Operation not permitted` | Контейнер не privileged | Убедись что в `molecule.yml` стоит `privileged: true` |
#### Запуск конкретной фазы вручную
#### Фазы Molecule (внутри runner-контейнера)
```bash
cd roles/k3s
# Войти в runner-контейнер:
docker run --rm -it -v $(pwd):/ansible -v /var/run/docker.sock:/var/run/docker.sock \
--entrypoint bash k3s-ansible
cd /ansible/roles/k3s # или prometheus-stack / istio
molecule lint # только линтинг
molecule syntax # только синтаксис
molecule create # только создать контейнер
molecule create # только создать тестовые контейнеры
molecule prepare # только prepare.yml
molecule converge # только converge.yml (основной тест)
molecule idempotency # только проверка идемпотентности
molecule verify # только verify.yml (assertions)
molecule destroy # только удалить контейнер
molecule destroy # только удалить тестовые контейнеры
```
---
@@ -1104,9 +1166,13 @@ server: "https://192.168.1.10:6443"
- Не занят другим устройством
- Не выдаётся DHCP сервером
**Автоопределение интерфейса:** Ansible определяет сетевой интерфейс каждой master-ноды через `ansible_default_ipv4.interface`. На Ubuntu это может быть `enp3s0`, на Raspberry Pi — `end0`, на VM — `eth0`. Принудительное задание: `kube_vip_interface: "eth0"` в `group_vars/all/main.yml`.
### NFS + CSI
NFS сервер разворачивается на master01. Каждый PVC создаёт отдельную папку внутри `/srv/nfs/k8s`:
NFS сервер разворачивается на master01. StorageClass автоматически именуется `nfs-<hostname NFS сервера>` — например, `nfs-master01`. Это позволяет сразу понять на какой сервер указывает StorageClass при `kubectl get sc`. Если NFS вынесен на отдельный хост с hostname `storage01`, StorageClass будет `nfs-storage01`.
Каждый PVC создаёт отдельную папку внутри `/srv/nfs/k8s`:
```
/srv/nfs/k8s/
@@ -1162,13 +1228,16 @@ k3s_node_taints: []
## Docker образ
Образ собирается один раз через `make build` и содержит всё необходимое для управления кластером:
Образ собирается один раз через `make build` и содержит всё необходимое — как для управления кластером, так и для Molecule-тестирования:
```
FROM python:3.12-slim-bookworm
├── openssh-client, curl, jq, git
├── docker-ce-cli (Docker CLI для Molecule)
├── ansible-core 2.16, ansible 9.x
├── kubernetes, openshift Python пакеты
├── molecule >= 6.0 + molecule-plugins[docker]
├── yamllint, ansible-lint
├── Helm 3.14.4
├── kubectl v1.29.3
└── Ansible Collections:
@@ -1177,13 +1246,19 @@ FROM python:3.12-slim-bookworm
└── kubernetes.core >= 3.0
```
При запуске монтируются:
При обычном запуске (`make install`, `make ping`):
| Путь в контейнере | Источник | Назначение |
|---|---|---|
| `/ansible` | `$(PWD)` | Весь проект |
| `/root/.ssh` | `~/.ssh` (read-only) | SSH ключи |
При Molecule-запуске (`make molecule-*`) дополнительно:
| Путь в контейнере | Источник | Назначение |
|---|---|---|
| `/var/run/docker.sock` | хост | Docker socket для создания тестовых контейнеров |
---
## Обновление K3S
@@ -1252,7 +1327,7 @@ metadata:
name: app-data
spec:
accessModes: [ReadWriteMany]
storageClassName: nfs-client
storageClassName: nfs-master01 # или имя твоего NFS сервера
resources:
requests:
storage: 5Gi

View File

@@ -248,6 +248,33 @@ case "${COMMAND}" in
exec ansible all -m ping "${ansible_args[@]}" "$@"
;;
# ── Molecule тестирование ролей ───────────────────────────────────────────
molecule)
ROLE="${2:-}"
shift 2 || true
if [[ -z "${ROLE}" ]]; then
err "Укажи роль: molecule k3s | molecule prometheus-stack | molecule istio"
echo ""
echo " Пример: make molecule-k3s"
exit 1
fi
if [[ ! -d "/ansible/roles/${ROLE}" ]]; then
err "Роль не найдена: /ansible/roles/${ROLE}"
exit 1
fi
log "Тестирую роль: ${ROLE}"
cd "/ansible/roles/${ROLE}"
exec molecule "${@:-test}"
;;
molecule-lint)
log "Запуск линтинга (yamllint + ansible-lint)..."
cd /ansible
yamllint .
ansible-lint
ok "Линтинг прошёл"
;;
# ── Прямые вызовы ─────────────────────────────────────────────────────────
ansible-playbook)
exec ansible-playbook "$@"

View File

@@ -3,7 +3,8 @@ driver:
name: docker
platforms:
- name: istio-test
# master01 — единственная нода нужна, шаблоны деплоятся с первого мастера
- name: master01
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true

View File

@@ -15,6 +15,7 @@
k3s_service_cidr: "10.43.0.0/16"
k3s_cluster_dns: "10.43.0.10"
k3s_flannel_backend: "vxlan"
k3s_cni: "flannel"
k3s_install_dir: /usr/local/bin
k3s_config_dir: /etc/rancher/k3s
k3s_data_dir: /var/lib/rancher/k3s

View File

@@ -3,7 +3,8 @@ driver:
name: docker
platforms:
- name: k3s-node
# master01 — первый сервер, Ubuntu 22.04, запускает cluster-init
- name: master01
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true
privileged: true
@@ -15,6 +16,32 @@ platforms:
- k3s_master
- k3s_cluster
# worker01 — второй сервер, Ubuntu 22.04, присоединяется к master01
- name: worker01
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
cgroupns_mode: host
command: /lib/systemd/systemd
groups:
- k3s_master
- k3s_cluster
# rpi01 — Raspberry Pi OS (Debian-based), NoSchedule taint
- name: rpi01
image: geerlingguy/docker-debian12-ansible:latest
pre_build_image: true
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
cgroupns_mode: host
command: /lib/systemd/systemd
groups:
- k3s_master
- k3s_cluster
provisioner:
name: ansible
playbooks:
@@ -25,9 +52,15 @@ provisioner:
defaults:
interpreter_python: auto_silent
inventory:
group_vars:
k3s_master:
k3s_master_ip: "{{ hostvars[groups['k3s_master'][0]]['ansible_host'] | default('127.0.0.1') }}"
host_vars:
rpi01:
ansible_python_interpreter: /usr/bin/python3
k3s_node_taints:
- "node-type=raspberry-pi:NoSchedule"
k3s_extra_server_args: |
kubelet-arg:
- "kube-reserved=cpu=50m,memory=128Mi"
- "system-reserved=cpu=50m,memory=128Mi"
verifier:
name: ansible

View File

@@ -56,10 +56,19 @@
that: k3s_config['service-cidr'] == '10.43.0.0/16'
fail_msg: "Неверный service-cidr: {{ k3s_config['service-cidr'] }}"
- name: Assert cluster-init is set (первый мастер)
- name: Assert cluster-init is set (только master01)
ansible.builtin.assert:
that: k3s_config['cluster-init'] == true
fail_msg: "cluster-init должен быть true для первого мастера"
when: inventory_hostname == groups['k3s_master'][0]
- name: Assert server URL is set (worker01 и rpi01)
ansible.builtin.assert:
that:
- k3s_config.server is defined
- "'6443' in k3s_config.server"
fail_msg: "server URL должен быть задан для worker01/rpi01, получено: {{ k3s_config }}"
when: inventory_hostname != groups['k3s_master'][0]
- name: Assert traefik is disabled
ansible.builtin.assert:

View File

@@ -3,7 +3,8 @@ driver:
name: docker
platforms:
- name: prom-test
# master01 — единственная нода нужна, шаблоны деплоятся с первого мастера
- name: master01
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true