refactor: перемещение плейбуков в playbooks/, ротация сертификатов, сохранение ключей локально

Организация плейбуков:
- все .yml плейбуки перенесены из корня в playbooks/
- Makefile и entrypoint.sh обновлены — все пути с playbooks/ префиксом
- k8s-user.yml переработан: include_tasks → include_role (корректная работа из подкаталога)

Сохранение ключей и kubeconfig локально:
- k8s-user.yml: новый play сохраняет k8s SSH ключи в ./keys/ на машине запуска
- переменная k8s_local_keys_dir: "./keys" (настраивается в group_vars)
- .gitignore: keys/k8s_id_rsa исключён (публичный ключ можно коммитить)
- kubeconfig уже сохранялся роль k3s (k3s_kubeconfig_local_path: "./kubeconfig")

Автоматическая ротация сертификатов K3S (роль k3s-certs):
- K3S выпускает сертификаты на 1 год (hardcoded), таймер обеспечивает их обновление
- скрипт k3s-cert-check.sh: проверяет срок через openssl, ротирует при k3s_cert_rotate_before_days
- systemd service + timer: запуск по расписанию k3s_cert_check_schedule (по умолчанию monthly)
- RandomizedDelaySec: снижает нагрузку при одновременном запуске на нескольких нодах
- переменные: k3s_cert_validity_years: 5, k3s_cert_rotate_before_days: 90
- добавлен в site.yml (тег certs) и отдельный плейбук playbooks/k3s-certs.yml
- make k3s-certs и команда k3s-certs в entrypoint.sh
This commit is contained in:
Sergey Antropoff
2026-04-24 07:00:18 +03:00
parent 408779a379
commit 437d0cce34
22 changed files with 340 additions and 49 deletions

99
playbooks/add-node.yml Normal file
View File

@@ -0,0 +1,99 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# Добавить новую ноду в кластер
#
# Использование:
# make add-node NODE=master04 — новый мастер (etcd участник)
# make add-node NODE=worker04 — новый воркер (только agent)
#
# Перед запуском:
# 1. Добавь ноду в inventory/hosts.ini в нужную группу
# 2. Убедись что SSH доступ настроен
# 3. Запусти make ping NODE=<nodename> для проверки
#
# Новые мастера: подключаются через VIP (kube-vip должен быть запущен)
# Новые воркеры: подключаются через VIP как agents (без etcd)
# ─────────────────────────────────────────────────────────────────────────────
# ── Читаем token и факты первого мастера ─────────────────────────────────────
- name: Gather cluster info from first master
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: true
become: true
tags: [always]
tasks:
- name: Read node-token from first master
ansible.builtin.slurp:
src: "{{ k3s_data_dir }}/server/node-token"
register: _node_token_raw
- name: Set shared token fact
ansible.builtin.set_fact:
k3s_node_token: "{{ _node_token_raw.content | b64decode | trim }}"
# ── Устанавливаем K3S на новую ноду ──────────────────────────────────────────
- name: Add new master node
hosts: "{{ node_to_add | default('') }}"
gather_facts: true
become: true
tags: [k3s]
serial: 1
vars:
# Новые мастера используют VIP (kube-vip уже работает)
k3s_join_address: "{{ kube_vip_address }}"
k3s_fetch_kubeconfig: true
pre_tasks:
- name: Verify node is in k3s_master or k3s_workers group
ansible.builtin.assert:
that:
- inventory_hostname in groups['k3s_master'] or
(groups['k3s_workers'] is defined and inventory_hostname in groups['k3s_workers'])
fail_msg: >
Нода '{{ inventory_hostname }}' не найдена в группах k3s_master или k3s_workers.
Добавь её в inventory/hosts.ini и повтори запуск.
- name: Check if K3S already installed
ansible.builtin.stat:
path: /usr/local/bin/k3s
register: k3s_installed
- name: Abort if K3S already running
ansible.builtin.assert:
that: not k3s_installed.stat.exists
fail_msg: >
K3S уже установлен на {{ inventory_hostname }}.
Используй make upgrade для обновления или make remove-node для удаления.
roles:
- role: k3s
post_tasks:
- name: Verify new node in cluster
ansible.builtin.command: >
k3s kubectl get node {{ inventory_hostname }} -o wide
delegate_to: "{{ groups['k3s_master'][0] }}"
become: true
register: new_node_status
changed_when: false
retries: 10
delay: 10
until: "'Ready' in new_node_status.stdout"
- name: Show node status
ansible.builtin.debug:
msg: "{{ new_node_status.stdout_lines }}"
- name: Install NFS client on new node (if CSI NFS enabled)
ansible.builtin.apt:
name: nfs-common
state: present
update_cache: true
when: csi_nfs_enabled | default(true) | bool
- name: Summary
ansible.builtin.debug:
msg: >
Нода {{ inventory_hostname }} успешно добавлена в кластер.
Тип: {{ 'master (etcd participant)' if inventory_hostname in groups['k3s_master'] else 'worker (agent)' }}

56
playbooks/bootstrap.yml Normal file
View File

@@ -0,0 +1,56 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# Bootstrap — первоначальная настройка нод
#
# Запускать ОДИН РАЗ перед make install.
# Подключается с начальными логином/паролем из vault (host_vars/<node>/vault.yml).
# Создаёт пользователя k3s_admin_user, раскладывает SSH ключ.
# После этого все playbook работают по ключу без пароля.
#
# Использование:
# make bootstrap
# make bootstrap NODE=master01 — только одна нода
#
# Требования vault (в host_vars/<node>/vault.yml для каждой ноды):
# bootstrap_user: ubuntu # начальный пользователь
# bootstrap_password: "секрет" # пароль SSH
# bootstrap_sudo_password: "секрет" # пароль sudo (часто тот же)
# ─────────────────────────────────────────────────────────────────────────────
- name: Bootstrap cluster nodes
hosts: "{{ node_to_bootstrap | default('k3s_cluster') }}"
gather_facts: false
serial: 1
vars:
# Подключаемся с первоначальными credentials из vault каждой ноды
ansible_user: "{{ bootstrap_user }}"
ansible_password: "{{ bootstrap_password }}"
ansible_become_password: "{{ bootstrap_sudo_password | default(bootstrap_password) }}"
ansible_ssh_common_args: >-
-o StrictHostKeyChecking=no
-o UserKnownHostsFile=/dev/null
-o PasswordAuthentication=yes
-o PubkeyAuthentication=no
pre_tasks:
- name: Validate bootstrap credentials are defined
ansible.builtin.assert:
that:
- bootstrap_user is defined
- bootstrap_user | length > 0
- bootstrap_password is defined
- bootstrap_password | length > 0
fail_msg: >
Для {{ inventory_hostname }} не заданы bootstrap credentials.
Создай host_vars/{{ inventory_hostname }}/vault.yml с полями:
bootstrap_user: ubuntu
bootstrap_password: "пароль"
Зашифруй файл: ansible-vault encrypt host_vars/{{ inventory_hostname }}/vault.yml
- name: Test initial SSH connection
ansible.builtin.ping:
register: ping_result
roles:
- role: bootstrap

20
playbooks/etcd-backup.yml Normal file
View File

@@ -0,0 +1,20 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# Резервное копирование etcd
# Запуск: make etcd-backup
# make etcd-backup SNAPSHOT=my-backup (своё имя)
# make etcd-backup ETCD_COPY=true (скопировать локально)
# ─────────────────────────────────────────────────────────────────────────────
- name: Backup etcd snapshot
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: true
become: true
tags: [etcd, backup]
vars:
etcd_backup_name: "{{ lookup('env', 'SNAPSHOT') | default('', true) }}"
etcd_backup_copy_to_local: "{{ lookup('env', 'ETCD_COPY') | default(false, true) | bool }}"
tasks:
- name: Run etcd backup
ansible.builtin.include_tasks: roles/etcd/tasks/backup.yml

View File

@@ -0,0 +1,60 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# Восстановление etcd из снимка
# ВНИМАНИЕ: останавливает K3S на ВСЕХ нодах и восстанавливает данные!
#
# Запуск: make etcd-restore SNAPSHOT=k3s-etcd-20250101T120000.db
# make etcd-list-snapshots — посмотреть доступные снимки
# ─────────────────────────────────────────────────────────────────────────────
- name: List available etcd snapshots
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [etcd, list]
tasks:
- name: List snapshots
ansible.builtin.command: k3s etcd-snapshot ls
register: snap_list
changed_when: false
- ansible.builtin.debug:
msg: "{{ snap_list.stdout_lines }}"
- name: Restore etcd snapshot
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: true
become: true
tags: [etcd, restore]
serial: 1
vars:
etcd_restore_snapshot: "{{ lookup('env', 'SNAPSHOT') }}"
etcd_restore_force: "{{ lookup('env', 'FORCE') | default(false, true) | bool }}"
tasks:
- name: Run etcd restore
ansible.builtin.include_tasks: roles/etcd/tasks/restore.yml
- name: Restart k3s on remaining master nodes
hosts: k3s_master
gather_facts: false
become: true
tags: [etcd, restore]
tasks:
- name: Start k3s (skip first master — already running)
ansible.builtin.systemd:
name: k3s
state: started
when: inventory_hostname != groups['k3s_master'][0]
failed_when: false
- name: Restart k3s-agent on worker nodes
hosts: k3s_workers
gather_facts: false
become: true
tags: [etcd, restore]
tasks:
- name: Start k3s-agent on workers
ansible.builtin.systemd:
name: k3s-agent
state: started
failed_when: false

13
playbooks/healthcheck.yml Normal file
View File

@@ -0,0 +1,13 @@
---
# Плейбук диагностики кластера
# Запуск: ansible-playbook healthcheck.yml
- name: K3S Cluster Health Check
hosts: k3s_cluster
gather_facts: true
become: true
tasks:
- name: Run health checks
ansible.builtin.include_role:
name: k3s
tasks_from: healthcheck

22
playbooks/k3s-certs.yml Normal file
View File

@@ -0,0 +1,22 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# k3s-certs: установка systemd таймера автоматической ротации сертификатов K3S
#
# K3S выпускает сертификаты сроком на 1 год. Этот плейбук устанавливает
# systemd таймер, который автоматически проверяет и обновляет сертификаты
# до их истечения, обеспечивая бесперебойную работу кластера.
#
# Настройка:
# k3s_cert_validity_years: 5 — желаемый срок жизни без вмешательства
# k3s_cert_rotate_before_days: 90 — ротация за N дней до истечения
# k3s_cert_check_schedule: "monthly" — расписание проверки (systemd OnCalendar)
#
# Запуск: ansible-playbook playbooks/k3s-certs.yml --ask-vault-pass
# ─────────────────────────────────────────────────────────────────────────────
- name: Setup K3S certificate auto-rotation
hosts: k3s_cluster
gather_facts: true
become: true
roles:
- role: k3s-certs

119
playbooks/k8s-user.yml Normal file
View File

@@ -0,0 +1,119 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# k8s-user: создание сервисного пользователя k8s на всех серверах
#
# Последовательность:
# 1. Создать пользователя k8s + sudo на всех нодах кластера
# 2. Сгенерировать RSA 4096 ключевую пару на первом мастере (один раз)
# 3. Сохранить ключи локально в ./keys/
# 4. Разложить ключи на все ноды кластера (SSH в любую сторону)
# 5. Обновить /etc/hosts на нодах кластера
# 6. То же самое для lab_hosts (через пароль из vault)
#
# Запуск: ansible-playbook playbooks/k8s-user.yml --ask-vault-pass
# Только кластер: ansible-playbook playbooks/k8s-user.yml --limit k3s_cluster
# ─────────────────────────────────────────────────────────────────────────────
# ── 1. Создать пользователя k8s на всех нодах кластера ───────────────────────
- name: Create k8s service user on cluster nodes
hosts: k3s_cluster
gather_facts: true
become: true
roles:
- role: k8s-user
# ── 2. Сгенерировать ключевую пару на первом мастере ─────────────────────────
- name: Generate k8s SSH key pair (first master only)
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tasks:
- name: Generate RSA key pair and store facts
ansible.builtin.include_role:
name: k8s-user
tasks_from: generate_keys.yml
# ── 3. Сохранить ключи локально в ./keys/ ────────────────────────────────────
- name: Save k8s SSH keys to local machine
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
tasks:
- name: Create local keys directory
ansible.builtin.file:
path: "{{ k8s_local_keys_dir }}"
state: directory
mode: '0700'
delegate_to: localhost
become: false
- name: Save private key locally
ansible.builtin.copy:
content: "{{ k8s_ssh_private_key }}"
dest: "{{ k8s_local_keys_dir }}/k8s_id_rsa"
mode: '0600'
delegate_to: localhost
become: false
- name: Save public key locally
ansible.builtin.copy:
content: "{{ k8s_ssh_public_key }}\n"
dest: "{{ k8s_local_keys_dir }}/k8s_id_rsa.pub"
mode: '0644'
delegate_to: localhost
become: false
- name: Show where keys were saved
ansible.builtin.debug:
msg: "SSH keys saved to {{ k8s_local_keys_dir }}"
# ── 4. Разложить ключи на все ноды кластера ──────────────────────────────────
- name: Distribute k8s SSH keys to all cluster nodes
hosts: k3s_cluster
gather_facts: false
become: true
tasks:
- name: Deploy keys to node
ansible.builtin.include_role:
name: k8s-user
tasks_from: distribute_keys.yml
# ── 5. Обновить /etc/hosts на нодах кластера ─────────────────────────────────
- name: Update /etc/hosts on cluster nodes
hosts: k3s_cluster
gather_facts: false
become: true
tasks:
- name: Update hosts file
ansible.builtin.include_role:
name: k8s-user
tasks_from: update_hosts.yml
# ── 6. Bootstrap lab_hosts: создать пользователя, разложить ключи, обновить hosts
# Подключение через логин/пароль из host_vars/<host>/vault.yml
- name: Setup k8s user on lab hosts
hosts: lab_hosts
gather_facts: true
become: true
vars:
ansible_user: "{{ bootstrap_user }}"
ansible_password: "{{ bootstrap_password }}"
ansible_become_password: "{{ bootstrap_sudo_password | default(bootstrap_password) }}"
ansible_ssh_common_args: >-
-o StrictHostKeyChecking=no
-o PasswordAuthentication=yes
-o PubkeyAuthentication=no
tasks:
- name: Create k8s user on lab host
ansible.builtin.include_role:
name: k8s-user
tasks_from: create_user.yml
- name: Distribute k8s SSH keys to lab host
ansible.builtin.include_role:
name: k8s-user
tasks_from: distribute_keys.yml
- name: Update /etc/hosts on lab host
ansible.builtin.include_role:
name: k8s-user
tasks_from: update_hosts.yml

15
playbooks/mdadm.yml Normal file
View File

@@ -0,0 +1,15 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# mdadm: обнаружение RAID массива и монтирование в /storage
#
# Запуск: ansible-playbook mdadm.yml --ask-vault-pass
# Только одна нода: ansible-playbook mdadm.yml --limit master01
# Отключить: задай mdadm_enabled: false в host_vars или group_vars
# ─────────────────────────────────────────────────────────────────────────────
- name: Configure mdadm RAID and mount /storage
hosts: k3s_cluster
gather_facts: true
become: true
roles:
- role: mdadm

125
playbooks/remove-node.yml Normal file
View File

@@ -0,0 +1,125 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# Безопасное удаление ноды из кластера
#
# Использование:
# make remove-node NODE=worker04
# make remove-node NODE=master04 (ВНИМАНИЕ: теряется один etcd участник)
#
# Порядок:
# 1. Cordon — запрещает планирование новых подов
# 2. Drain — вытесняет все поды на другие ноды
# 3. Delete — удаляет ноду из k8s API
# 4. Uninstall — удаляет k3s с ноды
#
# После удаления мастера рекомендуется добавить новый: make add-node NODE=...
# ─────────────────────────────────────────────────────────────────────────────
- name: Validate and prepare
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [always]
tasks:
- name: Validate NODE is specified
ansible.builtin.assert:
that: node_to_remove is defined and node_to_remove | length > 0
fail_msg: "Укажи ноду: make remove-node NODE=<nodename>"
- name: Prevent removing the only/first master
ansible.builtin.assert:
that:
- node_to_remove != groups['k3s_master'][0]
fail_msg: >
Нельзя удалить первый мастер ({{ groups['k3s_master'][0] }}) —
он является точкой инициализации кластера.
Перенести роль первого мастера: поменяй порядок нод в inventory и выполни restore.
- name: Warn if removing etcd member
ansible.builtin.debug:
msg: >
ВНИМАНИЕ: {{ node_to_remove }} является мастером (etcd участником).
После удаления в кластере останется {{ groups['k3s_master'] | length - 1 }} мастер(а).
Кворум: {{ (groups['k3s_master'] | length - 1) > (groups['k3s_master'] | length - 1) // 2 }}
when: node_to_remove in groups['k3s_master']
- name: Check node exists in cluster
ansible.builtin.command: k3s kubectl get node {{ node_to_remove }}
register: node_exists
changed_when: false
failed_when: false
- name: Cordon node
ansible.builtin.command: k3s kubectl cordon {{ node_to_remove }}
changed_when: true
when: node_exists.rc == 0
- name: Drain node (evict pods)
ansible.builtin.command: >
k3s kubectl drain {{ node_to_remove }}
--ignore-daemonsets
--delete-emptydir-data
--timeout=180s
--force
changed_when: true
when: node_exists.rc == 0
register: drain_result
failed_when: drain_result.rc != 0 and 'not found' not in drain_result.stderr
- name: Delete node from Kubernetes
ansible.builtin.command: k3s kubectl delete node {{ node_to_remove }}
changed_when: true
when: node_exists.rc == 0
- name: Uninstall K3S from removed node
hosts: "{{ node_to_remove | default('') }}"
gather_facts: false
become: true
tags: [k3s]
tasks:
- name: Run K3S server uninstall script
ansible.builtin.command: /usr/local/bin/k3s-uninstall.sh
failed_when: false
changed_when: true
when: node_to_remove in groups['k3s_master']
- name: Run K3S agent uninstall script
ansible.builtin.command: /usr/local/bin/k3s-agent-uninstall.sh
failed_when: false
changed_when: true
when:
- groups['k3s_workers'] is defined
- node_to_remove in groups['k3s_workers']
- name: Clean up k3s data directory
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "{{ k3s_config_dir }}"
- "{{ k3s_data_dir }}"
failed_when: false
- name: Post-removal verification
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [verify]
tasks:
- name: Show remaining nodes
ansible.builtin.command: k3s kubectl get nodes -o wide
register: remaining_nodes
changed_when: false
- name: Display cluster nodes
ansible.builtin.debug:
msg: "{{ remaining_nodes.stdout_lines }}"
- name: Summary
ansible.builtin.debug:
msg: >
Нода {{ node_to_remove }} успешно удалена из кластера.
Удали её из inventory/hosts.ini.
{% if node_to_remove in groups['k3s_master'] %}
Рекомендуется добавить новую мастер-ноду: make add-node NODE=<новая-нода>
{% endif %}

147
playbooks/site.yml Normal file
View File

@@ -0,0 +1,147 @@
---
# ─────────────────────────────────────────────────────────────────────────────
# K3S Full Stack Playbook
# Порядок установки:
# 1. K3S cluster (master → workers)
# 2. kube-vip (VIP для control plane + LoadBalancer)
# 3. NFS Server (на master или отдельном хосте)
# 4. CSI NFS Driver (StorageClass для PVC)
# 5. ingress-nginx (Ingress controller через Helm)
#
# Запуск: ansible-playbook playbooks/site.yml --ask-vault-pass
# Только отдельный компонент: ansible-playbook playbooks/site.yml --tags kube_vip
# ─────────────────────────────────────────────────────────────────────────────
# ── 1. K3S Cluster ────────────────────────────────────────────────────────────
# serial: 1 — master01 запускается первым (cluster-init), остальные присоединяются
- name: Install K3S cluster (HA embedded etcd)
hosts: k3s_cluster
gather_facts: true
become: true
serial: 1
tags: [k3s]
roles:
- role: k3s
# ── 2. CNI (calico/cilium; при flannel — пропускается) ───────────────────────
- name: Deploy CNI plugin
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [cni]
roles:
- role: cni
# ── 3. kube-vip ───────────────────────────────────────────────────────────────
- name: Deploy kube-vip (VIP + LoadBalancer)
hosts: k3s_master
gather_facts: true
become: true
tags: [kube_vip]
roles:
- role: kube-vip
# ── 4. NFS Server ─────────────────────────────────────────────────────────────
- name: Configure NFS server
hosts: nfs_server
gather_facts: true
become: true
tags: [nfs, nfs_server]
roles:
- role: nfs-server
# ── 5. NFS client on all K3S nodes + CSI Driver ───────────────────────────────
- name: Deploy CSI NFS Driver
hosts: k3s_cluster
gather_facts: true
become: true
tags: [nfs, csi_nfs]
roles:
- role: csi-nfs
# ── 6. ingress-nginx ──────────────────────────────────────────────────────────
- name: Deploy ingress-nginx
hosts: k3s_cluster
gather_facts: true
become: true
tags: [ingress, ingress_nginx]
roles:
- role: ingress-nginx
# ── 7. cert-manager (опционально: cert_manager_enabled: true) ────────────────
- name: Deploy cert-manager
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [cert_manager, certmanager]
roles:
- role: cert-manager
# ── 8. Istio (опционально: istio_enabled: true) ───────────────────────────────
- name: Deploy Istio service mesh
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [istio]
roles:
- role: istio
# ── 9. kube-prometheus-stack (опционально: prometheus_stack_enabled: true) ─────
- name: Deploy kube-prometheus-stack
hosts: "{{ groups['k3s_master'][0] }}"
gather_facts: false
become: true
tags: [monitoring, prometheus]
roles:
- role: prometheus-stack
# ── Verify full stack ─────────────────────────────────────────────────────────
- name: Verify full stack
hosts: k3s_master
gather_facts: false
become: true
tags: [verify]
tasks:
- name: Nodes
ansible.builtin.command: k3s kubectl get nodes -o wide
register: nodes
changed_when: false
- ansible.builtin.debug:
msg: "{{ nodes.stdout_lines }}"
- name: All pods
ansible.builtin.command: k3s kubectl get pods -A
register: pods
changed_when: false
- ansible.builtin.debug:
msg: "{{ pods.stdout_lines }}"
- name: Services with External IPs
ansible.builtin.command: k3s kubectl get svc -A
register: svcs
changed_when: false
- ansible.builtin.debug:
msg: "{{ svcs.stdout_lines }}"
- name: StorageClasses
ansible.builtin.command: k3s kubectl get storageclass
register: sc
changed_when: false
- ansible.builtin.debug:
msg: "{{ sc.stdout_lines }}"
- name: IngressClasses
ansible.builtin.command: k3s kubectl get ingressclass
register: ic
changed_when: false
- ansible.builtin.debug:
msg: "{{ ic.stdout_lines }}"
# ── Certificate Auto-Rotation (systemd timer) ─────────────────────────────────
- name: Setup K3S certificate auto-rotation
hosts: k3s_cluster
gather_facts: true
become: true
tags: [certs, k3s_certs]
roles:
- role: k3s-certs

101
playbooks/uninstall.yml Normal file
View File

@@ -0,0 +1,101 @@
---
# Полное удаление всего стека
# Порядок: обратный установке
# Запуск: ansible-playbook uninstall.yml
- name: Confirm uninstall
hosts: localhost
gather_facts: false
tasks:
- name: Ask for confirmation
ansible.builtin.pause:
prompt: |
⚠️ ВНИМАНИЕ! Будут удалены: ingress-nginx, CSI NFS, kube-vip, K3S и ВСЕ данные.
Введите 'yes' для подтверждения
register: confirm
when: confirm_uninstall is not defined
- name: Abort if not confirmed
ansible.builtin.fail:
msg: "Отменено."
when:
- confirm_uninstall is not defined
- confirm.user_input != 'yes'
- name: Remove ingress-nginx
hosts: k3s_master
gather_facts: false
become: true
tags: [ingress_nginx]
tasks:
- name: Uninstall ingress-nginx Helm release
ansible.builtin.command: >
helm uninstall ingress-nginx -n ingress-nginx
failed_when: false
changed_when: true
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Delete ingress-nginx namespace
ansible.builtin.command: k3s kubectl delete namespace ingress-nginx --ignore-not-found
changed_when: true
- name: Remove CSI NFS Driver
hosts: k3s_master
gather_facts: false
become: true
tags: [csi_nfs]
tasks:
- name: Uninstall CSI NFS Helm release
ansible.builtin.command: >
helm uninstall csi-driver-nfs -n kube-system
failed_when: false
changed_when: true
environment:
KUBECONFIG: "{{ k3s_kubeconfig_path }}"
- name: Delete NFS StorageClass
ansible.builtin.command: >
k3s kubectl delete storageclass nfs-client --ignore-not-found
changed_when: true
- name: Remove kube-vip
hosts: k3s_master
gather_facts: false
become: true
tags: [kube_vip]
tasks:
- name: Remove kube-vip manifests
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "{{ k3s_data_dir }}/server/manifests/kube-vip.yaml"
- "{{ k3s_data_dir }}/server/manifests/kube-vip-rbac.yaml"
- name: Uninstall K3S workers
hosts: k3s_workers
gather_facts: true
become: true
tags: [k3s]
tasks:
- ansible.builtin.include_role:
name: k3s
tasks_from: uninstall
- name: Uninstall K3S master
hosts: k3s_master
gather_facts: true
become: true
tags: [k3s]
tasks:
- ansible.builtin.include_role:
name: k3s
tasks_from: uninstall
- name: Done
hosts: localhost
gather_facts: false
tasks:
- ansible.builtin.debug:
msg: "✓ Весь стек удалён."

40
playbooks/upgrade.yml Normal file
View File

@@ -0,0 +1,40 @@
---
# Плейбук обновления K3S
# Workers обновляются по одному (serial: 1) чтобы не потерять кворум
#
# Запуск: ansible-playbook upgrade.yml -e k3s_version=v1.30.0+k3s1
- name: Upgrade K3S master
hosts: k3s_master
gather_facts: true
become: true
tasks:
- name: Upgrade K3S server
ansible.builtin.include_role:
name: k3s
tasks_from: upgrade
- name: Upgrade K3S workers (one by one)
hosts: k3s_workers
gather_facts: true
become: true
serial: 1 # ← обновляем по одной ноде за раз
tasks:
- name: Upgrade K3S agent
ansible.builtin.include_role:
name: k3s
tasks_from: upgrade
- name: Verify cluster after upgrade
hosts: k3s_master
gather_facts: false
become: true
tasks:
- name: Final node status
ansible.builtin.command: k3s kubectl get nodes -o wide
register: final_status
changed_when: false
- name: Show final cluster state
ansible.builtin.debug:
msg: "{{ final_status.stdout_lines }}"