## Аддоны (group_vars/all/addons.yml)
- Создан group_vars/all/addons.yml — единое место для включения/отключения
аддонов (addon_ingress_nginx: true/false и т.д.) и их основных настроек
- Из group_vars/all/main.yml убраны все секции аддонов (NFS, CSI, ingress,
cert-manager, etcd backup, Istio, Prometheus) — остался только core кластер
- Создан playbooks/addons.yml — комбинированный плейбук с 10 плеями,
каждый с `when: addon_X | default(false) | bool`; запускает только включённые
- make install-full: core (site.yml) + аддоны по addons.yml
- make install-addons: только аддоны без переустановки core
- Убраны все *_enabled флаги из аддонов (cert_manager_enabled, istio_enabled,
prometheus_stack_enabled и др.) — аддон ставится явным вызовом
- kube-vip: убран skip guard и kube_vip_enabled флаг (core, всегда ставится)
- TLS defaults в argocd/longhorn/kubernetes-dashboard: убрана зависимость
от cert_manager_enabled, теперь просто false (задаётся явно)
- Kiali: убрана зависимость от prometheus_stack_enabled, добавлены переменные
kiali_prometheus_enabled/url и kiali_grafana_enabled/url
## Внешний etcd кластер
- Новая переменная k3s_etcd_type: embedded|external в main.yml
- inventory/hosts.ini: добавлена группа [etcd_nodes] — любые серверы,
не обязательно мастера
- roles/etcd/: полная роль для установки внешнего etcd кластера:
- install.yml — скачивает бинарник, создаёт пользователя и директории
- pki.yml — генерирует CA + server/peer/client сертификаты через openssl
на Ansible-контроллере; раскладывает на etcd ноды и k3s мастера
- service.yml — разворачивает etcd.env и systemd сервис, проверяет здоровье
- etcd.env.j2 и etcd.service.j2 — шаблоны конфигурации
- etcd_pki_local_dir: persistent путь (<project>/etcd-pki/) вместо /tmp,
etcd-pki/ добавлен в .gitignore
- roles/k3s/templates/k3s-server-config.yaml.j2: при external режиме
подставляет datastore-endpoint со всеми etcd нодами + пути к клиентским
сертификатам; при embedded — прежняя логика cluster-init
- playbooks/site.yml: условный плей для etcd перед k3s (тег etcd)
- make install-etcd: отдельная команда для развёртывания etcd кластера
## Управление etcd нодами
- playbooks/add-etcd-node.yml: добавить ноду в работающий etcd кластер
(PKI генерация → install → etcdctl member add → start с state=existing → verify)
- playbooks/remove-etcd-node.yml: безопасно удалить ноду из etcd кластера
(проверка кворума → member remove → stop → clean up PKI)
- playbooks/add-node.yml: при k3s_etcd_type=external и наличии ноды в
[etcd_nodes] автоматически добавляет её в etcd кластер после k3s
- playbooks/remove-node.yml: при k3s_etcd_type=external сначала удаляет
ноду из etcd (member remove + stop), затем из k3s
- make add-etcd-node NODE=etcd04 / make remove-etcd-node NODE=etcd04
- Команды add-etcd-node / remove-etcd-node в docker/entrypoint.sh
200 lines
7.7 KiB
YAML
200 lines
7.7 KiB
YAML
---
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# Безопасное удаление ноды из кластера
|
||
#
|
||
# Использование:
|
||
# 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=...
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
# ── Удаляем из etcd кластера ПЕРЕД удалением из k3s ─────────────────────────
|
||
- name: Remove node from external etcd cluster (if applicable)
|
||
hosts: "{{ (groups['etcd_nodes'] | default([]) | reject('equalto', node_to_remove | default('')) | list)[0] | default('') }}"
|
||
gather_facts: false
|
||
become: true
|
||
when:
|
||
- k3s_etcd_type | default('embedded') == 'external'
|
||
- groups['etcd_nodes'] is defined
|
||
- node_to_remove is defined
|
||
- node_to_remove in groups['etcd_nodes']
|
||
- (groups['etcd_nodes'] | reject('equalto', node_to_remove) | list) | length > 0
|
||
tags: [etcd]
|
||
tasks:
|
||
- name: Warn about etcd membership removal
|
||
ansible.builtin.debug:
|
||
msg: >
|
||
Нода {{ node_to_remove }} является участником внешнего etcd кластера.
|
||
Удаляю из etcd перед удалением из k3s.
|
||
|
||
- name: Get member ID
|
||
ansible.builtin.shell: |
|
||
ETCDCTL_API=3 etcdctl \
|
||
--endpoints="https://{{ ansible_host }}:{{ etcd_client_port }}" \
|
||
--cacert="{{ etcd_pki_dir }}/ca.crt" \
|
||
--cert="{{ etcd_pki_dir }}/server.crt" \
|
||
--key="{{ etcd_pki_dir }}/server.key" \
|
||
member list -w json \
|
||
| python3 -c "
|
||
import json, sys
|
||
data = json.load(sys.stdin)
|
||
for m in data['members']:
|
||
if m.get('name') == '{{ node_to_remove }}':
|
||
print(hex(m['ID']))
|
||
sys.exit(0)
|
||
sys.exit(1)
|
||
"
|
||
register: _etcd_member_id
|
||
changed_when: false
|
||
failed_when: _etcd_member_id.rc != 0
|
||
|
||
- name: Remove member from etcd
|
||
ansible.builtin.shell: |
|
||
ETCDCTL_API=3 etcdctl \
|
||
--endpoints="https://{{ ansible_host }}:{{ etcd_client_port }}" \
|
||
--cacert="{{ etcd_pki_dir }}/ca.crt" \
|
||
--cert="{{ etcd_pki_dir }}/server.crt" \
|
||
--key="{{ etcd_pki_dir }}/server.key" \
|
||
member remove {{ _etcd_member_id.stdout }}
|
||
changed_when: true
|
||
|
||
- 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: Stop and disable etcd service (if running)
|
||
ansible.builtin.systemd:
|
||
name: etcd
|
||
state: stopped
|
||
enabled: false
|
||
failed_when: false
|
||
when:
|
||
- k3s_etcd_type | default('embedded') == 'external'
|
||
- groups['etcd_nodes'] is defined
|
||
- node_to_remove in groups['etcd_nodes']
|
||
|
||
- name: Remove etcd data and config (if applicable)
|
||
ansible.builtin.file:
|
||
path: "{{ item }}"
|
||
state: absent
|
||
loop:
|
||
- "{{ etcd_data_dir }}"
|
||
- "{{ etcd_config_dir }}"
|
||
failed_when: false
|
||
when:
|
||
- k3s_etcd_type | default('embedded') == 'external'
|
||
- groups['etcd_nodes'] is defined
|
||
- node_to_remove in groups['etcd_nodes']
|
||
|
||
- 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 %}
|