Files
K3S/playbooks/add-etcd-node.yml
Sergey Antropoff a94039e0f1 feat: аддоны через addons.yml, внешний etcd, управление etcd нодами
## Аддоны (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
2026-04-25 06:34:48 +03:00

215 lines
9.5 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
# ─────────────────────────────────────────────────────────────────────────────
# Добавить ноду в внешний etcd кластер
#
# Использование:
# make add-etcd-node NODE=etcd04 — добавить ноду etcd04
# make add-etcd-node NODE=master04 — добавить мастер-ноду как etcd-участника
#
# Перед запуском:
# 1. Добавь ноду в [etcd_nodes] в inventory/hosts.ini
# 2. Убедись что etcd PKI существует (etcd-pki/ca.key, etcd-pki/ca.crt)
# 3. k3s_etcd_type: external в group_vars/all/main.yml
#
# PKI:
# Серверные/peer сертификаты для новой ноды генерируются на Ansible-контроллере
# и подписываются существующим CA из etcd-pki/. CA ключ должен быть доступен.
# ─────────────────────────────────────────────────────────────────────────────
# ── Валидация ─────────────────────────────────────────────────────────────────
- name: Validate prerequisites
hosts: localhost
gather_facts: false
become: false
tags: [always]
tasks:
- name: Check node_to_add is specified
ansible.builtin.assert:
that: node_to_add is defined and node_to_add | length > 0
fail_msg: "Укажи ноду: make add-etcd-node NODE=<nodename>"
- name: Check node is in etcd_nodes group
ansible.builtin.assert:
that: node_to_add in groups['etcd_nodes']
fail_msg: >
Нода '{{ node_to_add }}' не найдена в группе [etcd_nodes].
Добавь её в inventory/hosts.ini и повтори запуск.
- name: Check etcd PKI CA exists
ansible.builtin.stat:
path: "{{ etcd_pki_local_dir }}/ca.key"
register: _ca_key_stat
- name: Fail if CA key missing
ansible.builtin.assert:
that: _ca_key_stat.stat.exists
fail_msg: >
CA ключ не найден: {{ etcd_pki_local_dir }}/ca.key
Запусти первоначальную установку etcd: make install-etcd
- name: Check node is not already in cluster
ansible.builtin.shell: |
EXISTING_NODES=$(ls "{{ etcd_pki_local_dir }}"/server-*.crt 2>/dev/null \
| xargs -I{} basename {} .crt \
| sed 's/server-//')
echo "${EXISTING_NODES}" | grep -qx "{{ node_to_add }}" && echo "exists" || echo "new"
register: _node_cert_check
changed_when: false
- name: Warn if certs already exist (will be regenerated)
ansible.builtin.debug:
msg: >
Сертификаты для ноды {{ node_to_add }} уже существуют в PKI.
Они будут пересозданы.
when: _node_cert_check.stdout == "exists"
# ── Генерируем сертификаты для новой ноды ────────────────────────────────────
- name: Generate PKI certificates for new etcd node
hosts: localhost
gather_facts: false
become: false
tags: [pki]
vars:
_new_node_ip: "{{ hostvars[node_to_add]['ansible_host'] }}"
tasks:
- name: Generate server/peer certs for new node
ansible.builtin.shell: |
set -e
NODE="{{ node_to_add }}"
NODE_IP="{{ _new_node_ip }}"
PKI="{{ etcd_pki_local_dir }}"
SAN="subjectAltName=IP:${NODE_IP},IP:127.0.0.1,DNS:${NODE},DNS:localhost"
# Принудительно пересоздаём (нода могла быть заменена с тем же именем)
rm -f "${PKI}/server-${NODE}."{crt,key,csr} "${PKI}/peer-${NODE}."{crt,key,csr}
openssl genrsa -out "${PKI}/server-${NODE}.key" 2048
openssl req -new -key "${PKI}/server-${NODE}.key" -out "${PKI}/server-${NODE}.csr" \
-subj "/CN=${NODE}/O=etcd"
openssl x509 -req -days 3650 \
-in "${PKI}/server-${NODE}.csr" -CA "${PKI}/ca.crt" -CAkey "${PKI}/ca.key" \
-CAcreateserial -out "${PKI}/server-${NODE}.crt" \
-extfile <(printf "${SAN}")
openssl genrsa -out "${PKI}/peer-${NODE}.key" 2048
openssl req -new -key "${PKI}/peer-${NODE}.key" -out "${PKI}/peer-${NODE}.csr" \
-subj "/CN=${NODE}/O=etcd-peer"
openssl x509 -req -days 3650 \
-in "${PKI}/peer-${NODE}.csr" -CA "${PKI}/ca.crt" -CAkey "${PKI}/ca.key" \
-CAcreateserial -out "${PKI}/peer-${NODE}.crt" \
-extfile <(printf "${SAN}")
args:
executable: /bin/bash
changed_when: true
# ── Устанавливаем etcd на новую ноду ─────────────────────────────────────────
- name: Install etcd binary on new node
hosts: "{{ node_to_add }}"
gather_facts: true
become: true
tags: [install]
tasks:
- name: Install etcd binary
ansible.builtin.include_role:
name: etcd
tasks_from: install
- name: Copy CA cert
ansible.builtin.copy:
src: "{{ etcd_pki_local_dir }}/ca.crt"
dest: "{{ etcd_pki_dir }}/ca.crt"
owner: etcd
group: etcd
mode: '0644'
- name: Copy server/peer certs for new node
ansible.builtin.copy:
src: "{{ etcd_pki_local_dir }}/{{ item.src }}"
dest: "{{ etcd_pki_dir }}/{{ item.dest }}"
owner: etcd
group: etcd
mode: "{{ item.mode }}"
loop:
- { src: "server-{{ inventory_hostname }}.crt", dest: "server.crt", mode: "0644" }
- { src: "server-{{ inventory_hostname }}.key", dest: "server.key", mode: "0600" }
- { src: "peer-{{ inventory_hostname }}.crt", dest: "peer.crt", mode: "0644" }
- { src: "peer-{{ inventory_hostname }}.key", dest: "peer.key", mode: "0600" }
# ── Регистрируем новый member в кластере ─────────────────────────────────────
- name: Register new etcd member
hosts: "{{ (groups['etcd_nodes'] | reject('equalto', node_to_add) | list)[0] }}"
gather_facts: false
become: true
tags: [register]
vars:
_new_node_ip: "{{ hostvars[node_to_add]['ansible_host'] }}"
tasks:
- name: Add new member via etcdctl
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 add "{{ node_to_add }}" \
--peer-urls="https://{{ _new_node_ip }}:{{ etcd_peer_port }}"
register: _member_add
changed_when: true
- name: Show member add output
ansible.builtin.debug:
msg: "{{ _member_add.stdout_lines }}"
# ── Запускаем etcd на новой ноде ─────────────────────────────────────────────
- name: Start etcd on new node
hosts: "{{ node_to_add }}"
gather_facts: true
become: true
tags: [start]
vars:
# Для новой ноды которая присоединяется к существующему кластеру
etcd_initial_cluster_state: "existing"
tasks:
- name: Build initial cluster members string
ansible.builtin.set_fact:
etcd_initial_cluster_members: >-
{%- set members = [] -%}
{%- for h in groups['etcd_nodes'] -%}
{%- set _ = members.append(h ~ '=https://' ~ hostvars[h]['ansible_host'] ~ ':' ~ etcd_peer_port) -%}
{%- endfor -%}
{{ members | join(',') }}
- name: Configure and start etcd service
ansible.builtin.include_role:
name: etcd
tasks_from: service
# ── Верификация кластера ──────────────────────────────────────────────────────
- name: Verify etcd cluster
hosts: "{{ node_to_add }}"
gather_facts: false
become: true
tags: [verify]
tasks:
- name: Show etcd cluster members
ansible.builtin.shell: |
ETCDCTL_API=3 etcdctl \
--endpoints="{% for h in groups['etcd_nodes'] %}https://{{ hostvars[h]['ansible_host'] }}:{{ etcd_client_port }}{% if not loop.last %},{% endif %}{% endfor %}" \
--cacert="{{ etcd_pki_dir }}/ca.crt" \
--cert="{{ etcd_pki_dir }}/server.crt" \
--key="{{ etcd_pki_dir }}/server.key" \
member list -w table
register: _members
changed_when: false
- name: Display cluster members
ansible.builtin.debug:
msg: "{{ _members.stdout_lines }}"
- name: Summary
ansible.builtin.debug:
msg: >
Нода {{ node_to_add }} ({{ hostvars[node_to_add]['ansible_host'] }})
успешно добавлена в etcd кластер.
Не забудь обновить k3s конфиг если нода новая: make install-k3s.