Sergey Antropoff dbc21150b2 feat: добавить Prometheus-метрики для nfs-server addon
- Устанавливает prometheus-node-exporter на NFS-хостах (включает NFSD-коллектор из /proc/net/rpc/nfsd)
- Открывает порт 9100 в UFW для cluster-сети
- При addon_prometheus_stack=true создаёт в k8s:
  - headless Service nfs-server-node-exporter
  - Endpoints со списком IP NFS-хостов из inventory
  - ServiceMonitor с label release: kube-prometheus-stack
2026-04-25 11:29:22 +03:00
2026-04-17 08:37:27 +03:00
2026-04-17 08:37:27 +03:00
2026-04-17 08:37:27 +03:00
2026-04-17 08:37:27 +03:00
2026-04-17 08:58:26 +03:00
2026-04-17 08:37:27 +03:00
2026-04-17 08:37:27 +03:00

K3S Ansible Stack

Полный Kubernetes стек на базе K3S с HA (High Availability), управляемый через Ansible внутри Docker-контейнера. Ansible устанавливать не нужно — всё работает через make.

Содержание


Архитектура

Кластер работает в HA-режиме с embedded etcd: все три ноды являются полноценными мастерами. Raspberry Pi участвует в etcd-кворуме, но не принимает рабочие нагрузки.

┌──────────────────────────────────────────────────────────────┐
│                     Локальная сеть 192.168.1.0/24            │
│                                                              │
│  VIP: 192.168.1.100 (kube-vip)                               │
│    ├── :6443  K3S API Server (HA)                            │
│    └── :80/:443  ingress-nginx → приложения                  │
│                                                              │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────┐   │
│  │   master01      │  │   worker01      │  │   rpi01     │   │
│  │   192.168.1.10  │  │   192.168.1.11  │  │  .1.12      │   │
│  │   x86_64        │  │   x86_64        │  │  aarch64    │   │
│  │                 │  │                 │  │             │   │
│  │  K3S server     │  │  K3S server     │  │  K3S server │   │
│  │  etcd #1        │  │  etcd #2        │  │  etcd #3    │   │
│  │  cluster-init   │  │  kube-vip       │  │  NoSchedule │   │
│  │  NFS server     │  │  workloads ✓    │  │  workloads ✗│   │
│  │  workloads ✓    │  │                 │  │             │   │
│  └─────────────────┘  └─────────────────┘  └─────────────┘   │
│                                                              │
│  StorageClass: nfs-master01 (default)                        │
│    └── /srv/nfs/k8s на master01                              │
└──────────────────────────────────────────────────────────────┘

Роли нод

Нода K3S роль etcd Workloads Описание
master01 server #1 (leader) Первый сервер, инициализирует кластер (cluster-init)
worker01 server #2 Присоединяется к master01, запускает поды
rpi01 server #3 Мастер-нода для quorum, taint NoSchedule

При отказе любой одной ноды кластер продолжает работать — etcd сохраняет кворум (2 из 3).

Компоненты

Компонент Версия Описание
K3S v1.29.3+k3s1 Лёгкий Kubernetes с embedded etcd (HA)
kube-vip v0.8.3 VIP для API + LoadBalancer сервисов
NFS Server Персистентное хранилище на master01
CSI NFS Driver v4.8.0 Динамические PVC через NFS
ingress-nginx chart 4.10.1 HTTP/S Ingress controller
cert-manager v1.15.3 TLS сертификаты (опционально)
Istio 1.22.2 Service mesh (опционально)
Kiali 1.86.0 UI для Istio, вход по токену (опционально)
kube-prometheus-stack 60.3.0 Prometheus + Grafana + Alertmanager (опционально)
Helm 3.14.4 Внутри Docker контейнера
kubectl v1.29.3 Внутри Docker контейнера

CNI плагин

По умолчанию используется встроенный в K3S Flannel. Переключение — одна переменная в group_vars/all/main.yml:

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).

Установить отдельно:

make install-cni K3S_CNI=calico   # или cilium

Требования

На твоей машине (откуда запускаешь)

Инструмент Версия Установка
Docker >= 24.0 https://docs.docker.com/get-docker/
make любая apt install make / brew install make
SSH ключ ssh-keygen -t ed25519

Ansible, Helm, kubectl, Molecule — устанавливать не нужно. Всё работает внутри Docker-контейнера.

На серверах кластера

Требование Описание
ОС Ubuntu 20.04/22.04/24.04, Debian 11/12, Raspberry Pi OS 64-bit
SSH Доступ с твоего публичного ключа
sudo Без пароля (NOPASSWD) — рекомендуется
Интернет Для скачивания K3S, образов, Helm чартов
RAM master01/worker01: 2+ ГБ; rpi01: 1+ ГБ

Настроить sudo без пароля на каждом сервере:

echo "$USER ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/$USER

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

k3s-ansible/
├── Makefile                        ← Все команды (единая точка входа)
├── Dockerfile                      ← Образ: Ansible + Helm + kubectl
├── docker-compose.yml
├── .env.example                    ← Шаблон переменных окружения
├── .yamllint.yml                   ← Правила линтинга YAML
│
├── requirements.yml                ← Ansible Galaxy коллекции
├── requirements-python.txt         ← Python пакеты (ansible + molecule)
│
├── ansible.cfg
├── site.yml                        ← Главный плейбук (serial: 1)
├── upgrade.yml
├── uninstall.yml
├── healthcheck.yml
├── add-node.yml                    ← Добавить ноду в кластер
├── remove-node.yml                 ← Удалить ноду из кластера
├── etcd-backup.yml                 ← Создать снимок etcd
├── etcd-restore.yml                ← Восстановить etcd из снимка
│
├── inventory/
│   └── hosts.ini                   ← IP и параметры серверов
│
├── group_vars/all/
│   ├── main.yml                    ← Все переменные кластера + опции
│   └── vault.yml                   ← Зашифрованные секреты (токены, пароли)
│
├── host_vars/
│   ├── master01/main.yml           ← Labels, server args
│   ├── worker01/main.yml           ← Labels, server args
│   └── rpi01/main.yml              ← Labels, taint NoSchedule, ARM args
│
└── roles/
    ├── k3s/                        ← K3S HA cluster (embedded etcd)
    │   ├── tasks/
    │   │   ├── main.yml            ← Точка входа
    │   │   ├── prereqs.yml         ← Пакеты, sysctl, swap, модули ядра
    │   │   ├── install_server.yml  ← Установка K3S server
    │   │   ├── node_config.yml     ← Labels и taints
    │   │   └── kubeconfig.yml      ← Скачать kubeconfig локально
    │   ├── templates/
    │   │   └── k3s-server-config.yaml.j2
    │   └── molecule/default/       ← Unit-тесты роли
    │       ├── molecule.yml
    │       ├── prepare.yml
    │       ├── converge.yml
    │       └── verify.yml
    │
    ├── kube-vip/                   ← Virtual IP + LoadBalancer
    ├── nfs-server/                 ← NFS сервер
    ├── csi-nfs/                    ← CSI Driver + StorageClass
    ├── ingress-nginx/              ← Ingress controller
    ├── cert-manager/               ← TLS сертификаты (опционально)
    │   ├── tasks/main.yml
    │   ├── defaults/main.yml
    │   └── templates/
    │       ├── clusterissuer-selfsigned.yaml.j2
    │       └── clusterissuer-letsencrypt.yaml.j2
    ├── etcd/                       ← Backup/restore задачи etcd
    │   ├── tasks/
    │   │   ├── backup.yml
    │   │   └── restore.yml
    │   └── defaults/main.yml
    │
    ├── istio/                      ← Service mesh + Kiali (опционально)
    │   ├── tasks/main.yml
    │   ├── templates/
    │   │   ├── istiod-values.yaml.j2
    │   │   ├── kiali-values.yaml.j2
    │   │   ├── kiali-token-secret.yaml.j2
    │   │   └── peer-authentication.yaml.j2
    │   └── molecule/default/
    │
    └── prometheus-stack/           ← Prometheus + Grafana + Alert (опционально)
        ├── tasks/main.yml
        ├── templates/
        │   └── prometheus-stack-values.yaml.j2
        └── molecule/default/

Полная установка с нуля

Это полный сценарий от чистой машины до работающего кластера. Скопируй и выполни блоками.

Шаг 1 — Подготовка серверов

Выполни на каждом сервере (замени user и IP на свои):

# Создать пользователя с sudo без пароля (если нужно)
sudo adduser ansible
echo "ansible ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ansible

# Или для текущего пользователя:
echo "$USER ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/$USER

Шаг 2 — SSH ключи

# Создать ключ (если нет)
ssh-keygen -t ed25519 -C "k3s-ansible" -f ~/.ssh/id_ed25519

# Скопировать на каждый сервер
ssh-copy-id ubuntu@192.168.1.10   # master01
ssh-copy-id ubuntu@192.168.1.11   # worker01
ssh-copy-id pi@192.168.1.12       # rpi01

# Проверить
ssh ubuntu@192.168.1.10 "hostname"

Шаг 3 — Клонировать и настроить проект

git clone <url> k3s-ansible && cd k3s-ansible

# Создать .env
make setup

Отредактируй .env — задай пароль от vault:

# .env
VAULT_PASSWORD=придумай-надёжный-пароль

Шаг 4 — Инвентарь

Отредактируй inventory/hosts.ini:

[k3s_master]
master01 ansible_host=192.168.1.10 ansible_user=ubuntu
worker01 ansible_host=192.168.1.11 ansible_user=ubuntu
rpi01    ansible_host=192.168.1.12 ansible_user=pi ansible_python_interpreter=/usr/bin/python3

[k3s_cluster:children]
k3s_master

[k3s_cluster:vars]
ansible_ssh_private_key_file=~/.ssh/id_ed25519

[nfs_server]
master01

Шаг 5 — Переменные кластера

Отредактируй group_vars/all/main.yml. Обязательные поля:

kube_vip_address: "192.168.1.100"   # свободный IP, не в DHCP пуле
# 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 с секретами

# Сгенерировать токен K3S
openssl rand -hex 32
# → a3f8c2d1e9b04756...

make vault-create

В открывшемся редакторе введи (замени значения на свои):

vault_k3s_token: "a3f8c2d1e9b04756..."   # токен из openssl выше
vault_grafana_user: "admin"               # если планируешь Prometheus
vault_grafana_password: "мой-пароль"
vault_kiali_token: ""                     # заполнишь после установки Istio

Сохрани: :wq (в vim) или Ctrl+X → Y (в nano).

Шаг 7 — Собрать Docker образ

make build
# Занимает ~3-5 минут при первом запуске

Шаг 8 — Прогнать тесты (рекомендуется)

Перед деплоем убедись что роли корректны. Molecule запускается внутри Docker — устанавливать ничего не нужно.

# Убедись что образ собран (если ещё не сделал на шаге 7):
# make build

# Запустить все тесты (~15-20 минут)
make molecule-all

Что поднимается: 3 контейнера (master01/worker01 на Ubuntu 22.04 + rpi01 на Debian 12), тест конфигурации K3S HA, шаблонов Prometheus и Istio.

Если всё зелёное — можно деплоить. Если есть ошибки — смотри раздел Тестирование через Molecule.

Шаг 9 — Проверить SSH и dry-run

# Проверить доступность всех нод
make ping
# Ожидаемый вывод: SUCCESS для каждой ноды

# Проверить плейбук без применения изменений
make check

Шаг 10 — Развернуть базовый стек

make install

Плейбук выполняет всё последовательно (serial: 1):

  1. master01: prereqs → K3S server (cluster-init) → ждёт готовности API
  2. worker01: prereqs → K3S server (join) → ждёт готовности ноды
  3. rpi01: prereqs + cgroups → K3S server (join) → применяет taint NoSchedule
  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. cert-manager (только если cert_manager_enabled: true)
  10. Финальная проверка: nodes / pods / svc / storageclass

Ожидаемое время: 15-25 минут в зависимости от скорости интернета.

Шаг 11 — Проверить результат

make verify

# Или с локальным kubectl:
export KUBECONFIG=$(pwd)/kubeconfig

kubectl get nodes -o wide
# NAME       STATUS   ROLES                       AGE   VERSION
# master01   Ready    control-plane,etcd,master   5m    v1.29.3+k3s1
# worker01   Ready    control-plane,etcd,master   3m    v1.29.3+k3s1
# rpi01      Ready    control-plane,etcd,master   1m    v1.29.3+k3s1

kubectl get svc -n ingress-nginx
# NAME                       TYPE           EXTERNAL-IP      PORT(S)
# ingress-nginx-controller   LoadBalancer   192.168.1.100    80:xxx/443:xxx

kubectl get storageclass
# NAME                    PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE
# nfs-master01 (default)  nfs.csi.k8s.io   Delete          Immediate

Шаг 12 (опционально) — Установить Istio + мониторинг

# Добавь в group_vars/all/main.yml:
# istio_enabled: true
# kiali_enabled: true
# prometheus_stack_enabled: true

make install-istio
make install-monitoring

# Или всё сразу с флагами:
# EXTRA_VARS="istio_enabled=true kiali_enabled=true prometheus_stack_enabled=true" make install

Рабочий процесс

Рекомендуемый порядок при любых изменениях в ролях:

Изменить роль → Тест Molecule → Линтинг → Dry-run → Deploy
# 1. Внёс изменение в roles/k3s/ или roles/prometheus-stack/
# 2. Запустить тест только нужной роли
make molecule-k3s          # или molecule-prometheus / molecule-istio

# 3. Линтинг всего проекта
make molecule-lint

# 4. Проверить что плейбук парсится корректно (не применяет изменения)
make check

# 5. Деплой на реальные серверы
make install

Тегированный деплой (обновить только один компонент)

make install ANSIBLE_TAGS=k3s          # только K3S
make install ANSIBLE_TAGS=kube_vip     # только kube-vip
make install ANSIBLE_TAGS=nfs          # NFS + CSI
make install ANSIBLE_TAGS=ingress      # ingress-nginx
make install ANSIBLE_TAGS=istio        # Istio + Kiali
make install ANSIBLE_TAGS=monitoring   # Prometheus stack

Настройка кластера

K3S

# group_vars/all/main.yml
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 (только для Flannel)
k3s_disable_traefik: true       # ОБЯЗАТЕЛЬНО true при ingress-nginx

# CNI выбирается здесь:
k3s_cni: "flannel"              # flannel | calico | cilium

# Пути K3S (изменены с /var/lib/rancher на /var/lib/kubernetes)
k3s_config_dir: /etc/kubernetes/k3s        # конфиги и kubeconfig
k3s_data_dir: /var/lib/kubernetes/k3s      # данные etcd, токены, манифесты
k3s_kubeconfig_path: "{{ k3s_config_dir }}/k3s.yaml"

CNI

Flannel (по умолчанию) — ничего дополнительно настраивать не нужно.

Calico:

k3s_cni: "calico"
calico_version: "v3.28.0"
calico_encapsulation: "VXLAN"   # VXLAN | IPIP | None

Cilium:

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

kube_vip_address: "192.168.1.100"   # Свободный IP — обязательно задать!
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

nfs_exports:
  - path: /srv/nfs/k8s
    options: "*(rw,sync,no_subtree_check,no_root_squash)"
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

ingress_nginx_service_type: "LoadBalancer"
ingress_nginx_load_balancer_ip: ""  # "" = авто от kube-vip
ingress_nginx_set_default_class: true

Индивидуальные настройки нод (host_vars/)

master01 — дополнительные labels и server args:

# host_vars/master01/main.yml
k3s_node_labels:
  - "node-role=master"
  - "disk-type=ssd"
k3s_extra_server_args: |
  kube-controller-manager-arg: "node-monitor-grace-period=20s"

rpi01 — taint запрещает планирование обычных подов:

# host_vars/rpi01/main.yml
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"

Чтобы разрешить поды на RPi — очисти список:

k3s_node_taints: []

Ansible Vault

make vault-create                                        # Создать
make vault-edit                                          # Редактировать
make vault-view                                          # Просмотреть
make vault-encrypt-string STR="токен" NAME="vault_k3s_token"

Полный шаблон group_vars/all/vault.yml:

# Обязательно:
vault_k3s_token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Если используешь Prometheus stack:
vault_grafana_user: "admin"
vault_grafana_password: "мой-безопасный-пароль"

# Если используешь Kiali (заполни после первой установки):
vault_kiali_token: ""

Опциональные компоненты

Включаются через переменные в group_vars/all/main.yml. По умолчанию все отключены — базовый стек работает без них.


cert-manager (TLS сертификаты)

Автоматически выдаёт и обновляет TLS сертификаты в кластере. Поддерживает самоподписанные сертификаты (для внутреннего использования) и Let's Encrypt (для публичных доменов).

Включить:

# group_vars/all/main.yml
cert_manager_enabled: true

Или разово:

make install-cert-manager

Типы ClusterIssuer:

cert_manager_issuer Описание
none Только установка cert-manager, без ClusterIssuer
selfsigned Самоподписанный CA (умолч.) — для внутреннего использования
letsencrypt Let's Encrypt ACME — требует публичный домен и ingress-nginx

Параметры:

Переменная Умолч. Описание
cert_manager_enabled false Включить установку
cert_manager_version v1.15.3 Версия Helm chart
cert_manager_namespace cert-manager Namespace
cert_manager_issuer selfsigned Тип ClusterIssuer
cert_manager_acme_email admin@example.com Email для Let's Encrypt
cert_manager_acme_server prod prod или staging

Использование selfsigned (внутренний CA):

После установки будут созданы:

  • selfsigned-issuer — базовый самоподписанный issuer
  • cluster-ca — Certificate (CA сертификат)
  • cluster-ca-issuer — ClusterIssuer для выдачи сертификатов приложениям

Пример использования в манифестах:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-app-tls
spec:
  secretName: my-app-tls-secret
  issuerRef:
    name: cluster-ca-issuer
    kind: ClusterIssuer
  dnsNames:
    - myapp.local

Использование Let's Encrypt:

cert_manager_issuer: "letsencrypt"
cert_manager_acme_email: "admin@example.com"
cert_manager_acme_server: "prod"   # или staging для тестирования

Добавить аннотацию на Ingress:

annotations:
  cert-manager.io/cluster-issuer: "letsencrypt-prod"

Istio (Service Mesh)

Устанавливает istio/base (CRDs) → istiod (control plane) → istio/gateway (LoadBalancer) → глобальную политику mTLS.

Включить:

# group_vars/all/main.yml
istio_enabled: true

Или разово без изменения файлов:

make install-istio

Параметры:

Переменная Умолч. Описание
istio_enabled false Включить установку
istio_version 1.22.2 Версия Helm chart
istio_mtls_mode STRICT Режим mTLS: STRICT / PERMISSIVE / DISABLE
istio_install_gateway true Устанавливать Ingress Gateway
istio_telemetry_enabled true Сбор метрик для Prometheus

Kiali (UI для Istio)

Веб-интерфейс для визуализации service mesh. Требует istio_enabled: true.

Включить:

kiali_enabled: true

Аутентификация по токену:

Kiali настроен со стратегией token — для входа в UI нужен Kubernetes ServiceAccount токен.

При первой установке Ansible:

  1. Создаёт ServiceAccount kiali-admin с правами cluster-admin
  2. Создаёт Secret типа kubernetes.io/service-account-token
  3. Читает сгенерированный k8s токен и выводит его в конце плейбука
══════════════════════════════════════════════════
 Kiali UI: kubectl -n istio-system port-forward svc/kiali 20001:20001
 Откройте: http://localhost:20001
 Стратегия: token
 Токен для входа:
 eyJhbGciOiJSUzI1NiIsImtpZCI6...
 Сохрани токен в vault.yml:  vault_kiali_token: <токен>
══════════════════════════════════════════════════

Скопируй выведенный токен в vault:

make vault-edit
# добавь: vault_kiali_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6..."

При последующих запусках Ansible использует сохранённый токен и не создаёт новый.

Параметры:

Переменная Умолч. Описание
kiali_enabled false Включить установку
kiali_version 1.86.0 Версия Helm chart
kiali_token vault_kiali_token Токен входа (из vault)
kiali_ingress_enabled false Создать Ingress
kiali_ingress_host kiali.local Hostname для Ingress

Включить Ingress:

kiali_ingress_enabled: true
kiali_ingress_host: "kiali.example.com"
kiali_ingress_class: "nginx"

kube-prometheus-stack (Prometheus + Grafana + Alertmanager)

Полный monitoring-стек: Prometheus, Grafana, Alertmanager, node-exporter, kube-state-metrics. Данные хранятся на PVC через StorageClass nfs-client.

Включить:

prometheus_stack_enabled: true

Или разово:

make install-monitoring

Хранилище (PVC):

Компонент Переменная Умолч. Назначение
Prometheus prometheus_storage_size 10Gi Метрики временных рядов
Grafana grafana_storage_size 5Gi Дашборды, плагины, настройки
Alertmanager prometheus_alertmanager_storage_size 2Gi Состояние алертов

Изменить размер:

# group_vars/all/main.yml
prometheus_storage_size: "20Gi"
grafana_storage_size: "10Gi"
prometheus_alertmanager_storage_size: "5Gi"

# Явно указать StorageClass (по умолчанию используется default = nfs-client):
# prometheus_storage_class: "nfs-client"
# grafana_storage_class: "nfs-client"

Grafana — доступ:

После установки Grafana доступна на NodePort 32000 (на любой ноде):

http://192.168.1.10:32000

Логин и пароль из vault:

vault_grafana_user: "admin"
vault_grafana_password: "мой-пароль"

Включить Ingress вместо NodePort:

prometheus_grafana_ingress_enabled: true
prometheus_grafana_ingress_host: "grafana.example.com"

Все параметры:

Переменная Умолч. Описание
prometheus_stack_enabled false Включить установку
prometheus_stack_version 60.3.0 Версия Helm chart
prometheus_retention_days 7 Срок хранения метрик (дней)
prometheus_storage_size 10Gi PVC Prometheus
grafana_storage_enabled true Включить PVC для Grafana
grafana_storage_size 5Gi PVC Grafana
grafana_admin_user vault_grafana_user Логин Grafana
prometheus_grafana_admin_password vault_grafana_password Пароль Grafana
prometheus_alertmanager_enabled true Включить Alertmanager
prometheus_alertmanager_storage_size 2Gi PVC Alertmanager
prometheus_node_exporter_enabled true Метрики хостов (DaemonSet)
prometheus_kube_state_metrics_enabled true Метрики объектов k8s

Интеграция Kiali ↔ Prometheus/Grafana:

При istio_enabled: true + prometheus_stack_enabled: true Kiali автоматически получает URL Prometheus и Grafana и настраивается на их использование без дополнительной конфигурации.


Управление нодами

Добавить ноду

Перед добавлением:

  1. Добавь ноду в inventory/hosts.ini в нужную группу (k3s_master или k3s_workers)
  2. Убедись что SSH доступ работает: make ping
# Добавить мастер-ноду (станет etcd участником)
make add-node NODE=master04

# Добавить рабочую ноду (только agent, без etcd)
make add-node NODE=worker04

Новые ноды подключаются через VIP (kube_vip_address) — kube-vip должен быть запущен. После добавления нода автоматически появляется в кластере.

Пример inventory/hosts.ini с рабочими нодами:

[k3s_master]
master01 ansible_host=192.168.1.10 ansible_user=ubuntu
worker01 ansible_host=192.168.1.11 ansible_user=ubuntu
rpi01    ansible_host=192.168.1.12 ansible_user=pi

[k3s_workers]
worker04 ansible_host=192.168.1.14 ansible_user=ubuntu
worker05 ansible_host=192.168.1.15 ansible_user=ubuntu

[k3s_cluster:children]
k3s_master
k3s_workers

Удалить ноду

make remove-node NODE=worker04

Порядок удаления:

  1. cordon — запретить планирование новых подов
  2. drain — вытеснить все поды на другие ноды (таймаут 180 сек)
  3. delete — удалить ноду из Kubernetes API
  4. Запустить uninstall-скрипт K3S на удалённой ноде
  5. Очистить директории K3S

После удаления убери ноду из inventory/hosts.ini.

Ограничения:

  • Нельзя удалить первый мастер (groups['k3s_master'][0]) — он является точкой инициализации кластера
  • При удалении мастера etcd-кворум уменьшается: 3 → 2 нод. Рекомендуется добавить новый мастер после удаления

Резервное копирование etcd

etcd хранит всё состояние кластера — поды, сервисы, конфигмапы, секреты, PVC. Регулярные снимки позволяют восстановить кластер после сбоя.

Снимки хранятся на сервере по пути {{ k3s_data_dir }}/server/db/snapshots (по умолчанию /var/lib/kubernetes/k3s/server/db/snapshots).

Создать снимок

make etcd-backup

Создаёт снимок с автоматическим именем k3s-etcd-<timestamp>.db, удаляет старые снимки сверх etcd_backup_retention (по умолчанию 5).

Опционально — скопировать снимок на локальную машину:

# group_vars/all/main.yml
etcd_backup_copy_to_local: true
etcd_backup_local_dir: "./etcd-backups"

Список снимков

make etcd-list-snapshots

Восстановить из снимка

# Посмотреть доступные снимки
make etcd-list-snapshots

# Восстановить (с подтверждением — введи 'yes')
make etcd-restore SNAPSHOT=k3s-etcd-20250101T120000.db

# Без интерактивного подтверждения
FORCE=true make etcd-restore SNAPSHOT=k3s-etcd-20250101T120000.db

Что происходит при восстановлении:

  1. K3S останавливается на всех мастерах и воркерах
  2. Запускается k3s server --cluster-reset --cluster-reset-restore-path=<snapshot> на первом мастере (процесс завершается после сброса)
  3. K3S запускается на первом мастере → ждёт готовности API
  4. K3S запускается на остальных мастерах
  5. K3S-agent запускается на воркерах

Параметры резервного копирования:

Переменная Умолч. Описание
etcd_backup_dir {{ k3s_data_dir }}/server/db/snapshots Директория снимков на сервере
etcd_backup_retention 5 Количество хранимых снимков
etcd_backup_copy_to_local false Копировать снимок на Ansible-хост
etcd_backup_local_dir ./etcd-backups Локальная директория для снимков

Все команды Make

Настройка и сборка

make setup          # Создать .env из шаблона
make build          # Собрать Docker образ (~5 мин)
make rebuild        # Пересобрать без кэша

Проверки

make ping           # SSH до всех нод
make check          # Dry-run без изменений
make lint           # Проверить синтаксис плейбуков

Установка

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
make install-cert-manager # cert-manager + ClusterIssuer
make install-istio        # Istio + Kiali (нужен istio_enabled: true в vars)
make install-monitoring   # Prometheus + Grafana (нужен prometheus_stack_enabled: true)

Управление нодами

make add-node NODE=worker04      # Добавить ноду в кластер
make remove-node NODE=worker04   # Безопасно удалить ноду

etcd — резервное копирование

make etcd-backup                              # Создать снимок etcd
make etcd-list-snapshots                      # Список доступных снимков
make etcd-restore SNAPSHOT=k3s-etcd-XXX.db   # Восстановить из снимка

Тестирование (Molecule — всё в Docker, pip не нужен)

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-20 мин)
make molecule-lint          # Линтинг YAML+ansible-lint в контейнере (~30 сек)

Обновление и диагностика

make upgrade VERSION=v1.30.0+k3s1   # Обновить K3S
make health                          # Полная диагностика
make verify                          # Сводка стека

Vault

make vault-create
make vault-edit
make vault-view
make vault-encrypt-string STR=... NAME=...

Прочее

make shell          # bash внутри ansible-контейнера
make uninstall      # Удалить весь стек (с подтверждением)
make clean          # Удалить Docker образ
make clean-all      # Образ + kubeconfig + кэш Docker

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

ANSIBLE_VERBOSITY=2 make install                                 # Подробный вывод
ANSIBLE_TAGS=k3s,kube_vip make install                           # Только теги
EXTRA_VARS="k3s_version=v1.30.0+k3s1" make install-k3s          # Доп. переменные

Тестирование через Molecule

Molecule — стандартный инструмент для тестирования Ansible ролей. Каждая роль запускается в Docker-контейнерах, проходит набор автоматических проверок и удаляется. Реальные серверы при этом не нужны.

Требования

Только Docker — Molecule, Python и зависимости уже внутри образа k3s-ansible. Устанавливать ничего дополнительно не нужно.

# Убедись что образ собран:
make build

# Запустить тесты (всё в Docker):
make molecule-k3s

Как это работает: make molecule-* запускает контейнер k3s-ansible с примонтированным Docker socket (/var/run/docker.sock). Внутри этого контейнера Molecule создаёт тестовые контейнеры для каждой роли — Docker внутри Docker без демона (только socket от хоста).

Жизненный цикл теста

Команда molecule test выполняет следующие фазы по порядку:

dependency   → скачать зависимые Ansible-роли (если есть)
lint         → yamllint + ansible-lint (статический анализ)
syntax       → ansible-playbook --syntax-check
create       → запустить Docker-контейнер (платформа)
prepare      → подготовить контейнер (установить Python, collections)
converge     → выполнить тестируемые задачи внутри контейнера
idempotency  → выполнить converge повторно (проверить что нет лишних изменений)
verify       → запустить assertions (проверить результаты)
cleanup      → очистить состояние (если задан cleanup.yml)
destroy      → удалить Docker-контейнер

При ошибке на любой фазе — тест падает, контейнер удаляется автоматически.

Что тестирует каждая роль

Роль k3s (3 контейнера, privileged Docker, ~8-12 мин)

Поднимает 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 — пакеты, swap, модули ядра, sysctl
  • Рендеринг k3s-server-config.yaml.j2/etc/kubernetes/k3s/config.yaml

Что проверяет verify.yml:

Проверка Нода Что именно
Директория /etc/kubernetes/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 мин)

Только рендеринг Jinja2-шаблона — не требует privileged режима.

Тестируемые задачи:

  • Рендеринг prometheus-stack-values.yaml.j2/tmp/prometheus-stack-values.yaml

Что проверяет verify.yml:

Проверка Что именно
grafana.adminUser Значение переменной grafana_admin_user
grafana.adminPassword Непустой пароль из переменной
grafana.persistence.enabled true — PVC включён
grafana.persistence.size 5Gi — размер PVC Grafana
prometheus.prometheusSpec.retention 7d
Prometheus PVC storage 10Gi
Alertmanager enabled true
Alertmanager PVC storage 2Gi
nodeExporter enabled true

Роль istio (lightweight Docker, ~2-3 мин)

Рендеринг всех четырёх шаблонов роли.

Тестируемые задачи:

  • istiod-values.yaml.j2/tmp/istiod-values.yaml
  • kiali-values.yaml.j2/tmp/kiali-values.yaml
  • peer-authentication.yaml.j2/tmp/peer-authentication.yaml
  • kiali-token-secret.yaml.j2/tmp/kiali-token-secret.yaml

Что проверяет verify.yml:

Файл Проверка
istiod-values.yaml Ресурсы pilot (cpu/memory), meshConfig существует, enablePrometheusMerge: true
kiali-values.yaml auth.strategy: token, Prometheus URL содержит prom-kube-prometheus-stack-prometheus, Grafana auth username
peer-authentication.yaml kind: PeerAuthentication, spec.mtls.mode: STRICT
kiali-token-secret.yaml type: kubernetes.io/service-account-token, аннотация kiali-admin

Запуск тестов: пошагово

Линтинг (в контейнере, ~30 сек)

make molecule-lint

Запускает yamllint . и ansible-lint на всём проекте внутри Docker-контейнера. Используй перед каждым коммитом.

Запуск линтинга...
✓ Линтинг прошёл

Тест одной роли

make molecule-k3s

Вывод (успешный, сокращён):

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
TASK [Wait for systemd to start] ***
ok: [master01]
ok: [worker01]
ok: [rpi01]

INFO     Running default > converge
TASK [Mock k3s binary] ***
changed: [master01]
changed: [worker01]
changed: [rpi01]

TASK [Test server config template rendering] ***
changed: [master01]
changed: [worker01]
changed: [rpi01]

INFO     Running default > idempotency
PLAY RECAP
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
TASK [Assert cluster-init is set (только master01)] ***
ok: [master01] => {"msg": "All assertions passed"}
skipping: [worker01]
skipping: [rpi01]

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: [master01]
ok: [worker01]
ok: [rpi01]

INFO     Running default > destroy
✓ k3s role: OK

Все тесты

make molecule-all
Тестирую роль k3s (3 ноды: master01, worker01, rpi01)...
...
✓ k3s role: OK

Тестирую роль prometheus-stack...
...
✓ prometheus-stack role: OK

Тестирую роль istio...
...
✓ istio role: OK

✓ Все тесты прошли успешно

Отладка упавших тестов

Если тест упал — контейнеры удаляются автоматически. Чтобы оставить контейнеры живыми для ручной отладки, войди в интерактивный shell внутри Molecule-контейнера и запускай отдельные фазы:

# Открыть 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

# Внутри контейнера — перейти к роли и запускать фазы вручную:
cd /ansible/roles/prometheus-stack

molecule converge          # создать контейнер и запустить задачи (без удаления)
molecule login             # войти в тестовый контейнер
# Внутри: cat /tmp/prometheus-stack-values.yaml
# Внутри: exit

molecule verify            # только assertions
molecule converge -- -vvv  # подробный вывод
molecule destroy           # удалить тестовый контейнер

Для роли k3s, которая поднимает 3 контейнера:

cd /ansible/roles/k3s

molecule converge          # поднимет master01, worker01, rpi01
molecule login --host master01   # войти в конкретный контейнер
molecule login --host rpi01
molecule verify
molecule destroy

Типичные ошибки и решения

Ошибка Причина Решение
Unable to pull image Нет интернета или Docker Hub Проверь подключение, попробуй docker pull geerlingguy/docker-ubuntu2204-ansible
FAILED: assert ... is defined Переменная не задана в converge.yml Добавь переменную в секцию vars: в converge.yml
Idempotency: CHANGED Таск меняет состояние при повторном запуске Добавь changed_when: false или исправь идемпотентность задачи
yamllint: wrong indentation Ошибка отступа в YAML Исправь файл, запусти make molecule-lint
ansible-lint: no-changed-when Таск shell/command без changed_when Добавь changed_when: <условие> к задаче
sysctl: Operation not permitted Контейнер не privileged Убедись что в molecule.yml стоит privileged: true

Фазы Molecule (внутри runner-контейнера)

# Войти в 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 prepare     # только prepare.yml
molecule converge    # только converge.yml (основной тест)
molecule idempotency # только проверка идемпотентности
molecule verify      # только verify.yml (assertions)
molecule destroy     # только удалить тестовые контейнеры

Написание новых тестов

Структура каждого scenario:

molecule.yml — конфигурация драйвера и платформы:

driver:
  name: docker
platforms:
  - name: my-test-instance
    image: geerlingguy/docker-ubuntu2204-ansible:latest
    pre_build_image: true
    privileged: true            # нужно для sysctl, модулей ядра
    groups:
      - k3s_master              # добавить в Ansible-группу для шаблонов
provisioner:
  name: ansible
verifier:
  name: ansible

converge.yml — что запускать (не весь role, а конкретные задачи):

- name: Converge
  hosts: all
  become: true
  vars:
    my_var: "test-value"        # все нужные переменные задаются здесь
  tasks:
    - name: Render template
      ansible.builtin.template:
        src: "{{ playbook_dir }}/../../templates/my-template.j2"
        dest: /tmp/result.yaml

verify.yml — что проверять:

- name: Verify
  hosts: all
  tasks:
    - name: Read result
      ansible.builtin.slurp:
        src: /tmp/result.yaml
      register: raw

    - name: Parse YAML
      ansible.builtin.set_fact:
        result: "{{ raw.content | b64decode | from_yaml }}"

    - name: Assert key exists
      ansible.builtin.assert:
        that: result.myKey == 'expected-value'
        fail_msg: "Ожидалось 'expected-value', получено: {{ result.myKey }}"

Компоненты стека

K3S HA (embedded etcd)

Три ноды в режиме server формируют etcd-кластер из 3 участников — кворум сохраняется при отказе любой одной ноды.

Порядок установки управляется serial: 1 в site.yml:

master01 (cluster-init: true)
    ↓  готов, API отвечает на :6443
worker01 (server: https://192.168.1.10:6443)
    ↓  присоединился, etcd = 2/3
rpi01    (server: https://192.168.1.10:6443, NoSchedule taint)
    ↓  присоединился, etcd = 3/3, кворум достигнут

Конфигурационный файл K3S генерируется из шаблона k3s-server-config.yaml.j2. Шаблон автоматически определяет роль ноды:

# master01 получает:
cluster-init: true

# worker01 и rpi01 получают:
server: "https://192.168.1.10:6443"

kube-vip

Создаёт виртуальный IP через ARP (L2). VIP анонсируется всем устройствам в локальной сети. При отказе мастера VIP автоматически мигрирует на другую ноду в течение нескольких секунд.

Требования к VIP:

  • В той же подсети что серверы
  • Не занят другим устройством
  • Не выдаётся 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. StorageClass автоматически именуется nfs-<hostname NFS сервера> — например, nfs-master01. Это позволяет сразу понять на какой сервер указывает StorageClass при kubectl get sc. Если NFS вынесен на отдельный хост с hostname storage01, StorageClass будет nfs-storage01.

Каждый PVC создаёт отдельную папку внутри /srv/nfs/k8s:

/srv/nfs/k8s/
├── default/                    ← namespace
│   └── my-pvc/                 ← имя PVC
│       └── pvc-xxx-yyy-zzz/   ← имя PV
│           └── данные...

NFS можно вынести на отдельный хост — задай в inventory/hosts.ini:

[nfs_server]
nfshost ansible_host=192.168.1.20 ansible_user=ubuntu

И в group_vars/all/main.yml:

csi_nfs_server: "192.168.1.20"

ingress-nginx

Устанавливается через Helm, отключает встроенный Traefik K3S, получает LoadBalancer IP от kube-vip. Настроен с JSON-логами, CORS, proxy timeouts и tolerations для всех типов нод (включая control-plane и RPi).


Raspberry Pi

Роль автоматически определяет ARM-архитектуру и:

  1. Включает memory cgroup в /boot/cmdline.txt (или /boot/firmware/cmdline.txt для Bookworm) и перезагружает RPi
  2. Применяет пониженные kubelet-резервы (cpu=50m, memory=128Mi)
  3. Настраивает пороги garbage collection образов (85%/80%)
  4. Применяет taint node-type=raspberry-pi:NoSchedule — поды не планируются если нет явного toleration

Рекомендуемая ОС: Raspberry Pi OS Lite 64-bit (Bookworm) для RPi 4/5.

Для деплоя на RPi добавь toleration в манифест:

tolerations:
  - key: "node-type"
    operator: "Equal"
    value: "raspberry-pi"
    effect: "NoSchedule"

Чтобы снять ограничение и использовать RPi как обычную ноду — очисти host_vars/rpi01/main.yml:

k3s_node_taints: []

Docker образ

Образ собирается один раз через 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:
    ├── community.general >= 8.0
    ├── ansible.posix >= 1.5
    └── 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

make upgrade VERSION=v1.30.0+k3s1

Происходит по очереди (serial: 1 в upgrade.yml):

  1. Drain ноды → вытеснить поды
  2. Обновить K3S бинарник
  3. Перезапустить сервис
  4. Дождаться Ready
  5. Uncordon → восстановить планирование
  6. Перейти к следующей ноде

HA-режим гарантирует доступность кластера во время обновления: пока одна нода обновляется, остальные две обслуживают трафик.


Диагностика

make health     # Статус сервисов, поды, диск, память
make verify     # Nodes, pods, services, storageclass, ingress

# Открыть shell в ansible-контейнере:
make shell

# Внутри контейнера:
ansible all -m shell -a "systemctl status k3s"
ansible k3s_master -m shell -a "k3s kubectl get nodes -o wide"
ansible k3s_master -m shell -a "k3s kubectl get pods -A | grep -v Running"
ansible k3s_master -m shell -a "k3s etcd-snapshot ls"

# Локально с kubectl (после make install):
export KUBECONFIG=$(pwd)/kubeconfig
kubectl top nodes
kubectl top pods -A
kubectl get events -A --sort-by='.lastTimestamp' | tail -20

Проверка etcd

# Статус участников etcd
kubectl -n kube-system exec -it \
  $(kubectl -n kube-system get pods -l component=etcd -o name | head -1) \
  -- etcdctl --endpoints=https://127.0.0.1:2379 \
     --cacert=/var/lib/kubernetes/k3s/server/tls/etcd/server-ca.crt \
     --cert=/var/lib/kubernetes/k3s/server/tls/etcd/server-client.crt \
     --key=/var/lib/kubernetes/k3s/server/tls/etcd/server-client.key \
     member list

Примеры манифестов

Приложение с Ingress и NFS-хранилищем

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes: [ReadWriteMany]
  storageClassName: nfs-master01   # или имя твоего NFS сервера
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels: {app: my-app}
  template:
    metadata:
      labels: {app: my-app}
    spec:
      containers:
        - name: app
          image: nginx:alpine
          volumeMounts:
            - mountPath: /data
              name: storage
      volumes:
        - name: storage
          persistentVolumeClaim:
            claimName: app-data
---
apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  selector: {app: my-app}
  ports: [{port: 80}]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
spec:
  ingressClassName: nginx
  rules:
    - host: myapp.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80

Приложение в Istio mesh

apiVersion: v1
kind: Namespace
metadata:
  name: my-app
  labels:
    istio-injection: enabled    # автоматически внедрять sidecar
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-app
  namespace: my-app
spec:
  hosts: ["myapp.local"]
  gateways: ["istio-system/istio-ingressgateway"]
  http:
    - route:
        - destination:
            host: my-app
            port:
              number: 80

Приложение только на x86 нодах (не на RPi)

spec:
  template:
    spec:
      nodeSelector:
        node-type: x86_64

Приложение с доступом к Grafana/Prometheus (ServiceMonitor)

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
  namespace: my-app
  labels:
    release: prom    # должен совпадать с prometheus_stack_release_name
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics
      interval: 30s

Решение проблем

Проблема Решение
Permission denied (publickey) ssh-copy-id user@server
Vault decryption failed Проверь VAULT_PASSWORD в .env
kube-vip VIP не пингуется Проверь kube_vip_interface — должен совпадать с ip a на master01
RPi K3S не стартует ssh pi@rpi "cat /proc/cgroups | grep memory" — должна быть 1
PVC в Pending kubectl get events -A — проверь монтирование NFS на нодах
ingress EXTERNAL-IP <pending> kubectl -n kube-system get pods | grep kube-vip — поды kube-vip должны быть Running
etcd нет кворума Нужно минимум 2 из 3 нод. kubectl get nodes — проверь статус
worker01/rpi01 не присоединились Убедись что master01 полностью стартовал. serial: 1 гарантирует порядок, но нода должна быть доступна
Kiali: "Could not get token" Проверь: kubectl -n istio-system get secret kiali-admin-token
Grafana PVC Pending kubectl get scnfs-client должен быть (default). Проверь NFS сервер: exportfs -v
Molecule: image pull failed docker pull geerlingguy/docker-ubuntu2204-ansible:latest вручную
Molecule: idempotency failed Таск выдаёт changed при повторном запуске — добавь changed_when: false
ansible-lint ошибки Запусти make molecule-lint и исправь указанные файлы
etcd restore: снимок не найден Запусти make etcd-list-snapshots — снимок должен быть на первом мастере
add-node: нода не присоединяется Убедись что kube-vip работает: kubectl -n kube-system get pods | grep kube-vip
remove-node: drain завис Поды с PodDisruptionBudget могут блокировать drain. Проверь PDB: kubectl get pdb -A
cert-manager: сертификат в Pending kubectl describe certificate <name> — проверь условия. При letsencrypt нужен публичный домен и ingress

Отладка с подробным выводом

ANSIBLE_VERBOSITY=2 make install                # основной вывод
ANSIBLE_VERBOSITY=4 make install                # максимальный вывод (SSH, модули)
ANSIBLE_VERBOSITY=2 ANSIBLE_TAGS=k3s make install  # подробно только для k3s

Сброс и повторная установка

# Удалить весь стек (с подтверждением)
make uninstall

# Затем установить заново
make install
Description
No description provided
Readme 1.9 MiB
Languages
Jinja 56.1%
Makefile 20%
Python 10.7%
Shell 7.7%
Smarty 3.8%
Other 1.7%