Топология k3s: 1 мастер + 2 воркера (убран k3s-worker-3 с томом).
Vault ↔ k3s. Vault живёт в compose, вне кластера, поэтому получает
статический IP 172.28.0.13, DNS-мост в namespace vault и token reviewer
(SA vault-auth + system:auth-delegator). Ansible включает auth/kubernetes,
передавая адрес apiserver, CA и JWT ревьюера явно, и заводит политику с
ролями. Секрет token reviewer'а читается целиком в JSON: вариант
-o jsonpath={.data.ca\.crt} НЕ работает — модуль command разбирает строку
через shlex, съедает обратный слэш, kubectl возвращает пустую строку с
кодом 0, и отказ остаётся незамеченным до падения vault write.
Синхронизация репозитория в gitea. Нужное подмножество путей (замыкание
ссылок clusters/aero) едет архивом на хост и коммитится там: gitea не
публикуется дальше самого хоста, контур остаётся замкнутым. flux-bootstrap
вынесен в отдельную волну — до неё Vault получает auth/kubernetes, а
репозиторий наполняется, иначе первая реконсиляция падает на пустом репо.
Инфраструктура: vault-agent-injector (чарт vault-contour в режиме внешнего
Vault), local-path-provisioner взамен встроенного в k3s (--disable=
local-storage; путь данных прежний), rabbitmq и minio с кредами из Vault.
Выход наружу: порты 80/443 k3s-server опубликованы (istio ingressgateway
занимает эти hostPort), общий contour-gateway на wildcard-хост, сертификат
Let's Encrypt через http01. Имена в сертификате перечислены явно —
http01 не выдаёт wildcard, для них нужен dns01.
Разорваны два дедлока Flux: infra-configs больше не зависит от
infra-controllers (издатели не должны зависеть от здоровья чартов, которые
их используют), istio-config получил disableWait.
Модули ядра iptable_nat и смежные грузятся на хосте: ноды k3s —
контейнеры, istio-init правит iptables через ядро хоста, а на RED OS 8
(nf_tables) legacy-модули не загружены, из-за чего любой под с
istio-injection навсегда вставал в Init:Error.
Собственные Gateway/VirtualService/Certificate чартов rabbitmq и dashboard
отключены — маршрутизация описана централизованно. У rabbitmq это сделано
postRenderers, а не values: kustomize нормализует null в {}, а пустую карту
Helm сливает с дефолтами, возвращая их целиком.
141 lines
6.0 KiB
YAML
141 lines
6.0 KiB
YAML
---
|
||
# Синхронизация репозитория контура в gitea.
|
||
#
|
||
# Flux читает манифесты ИЗ gitea, а не из этого репозитория, поэтому содержимое
|
||
# нужно туда доставить. Схема: control-node пакует нужное подмножество путей в
|
||
# архив → ansible кладёт его на хост → на хосте архив распаковывается в рабочую
|
||
# копию репозитория gitea и коммитится.
|
||
#
|
||
# Почему коммит делается на хосте, а не push'ем с control-node: gitea публикует
|
||
# порт только на самом хосте, и контур остаётся замкнутым — наружу за git никто
|
||
# не ходит.
|
||
#
|
||
# Синхронизируется ЗАМЫКАНИЕ ссылок clusters/aero (gitea_sync_paths), а не весь
|
||
# репозиторий: ansible-роль, snapshots и docs кластеру не нужны.
|
||
#
|
||
# Идемпотентно: если содержимое не изменилось, коммита не будет.
|
||
|
||
- name: Установить git на хосте (нужен для коммита в gitea)
|
||
ansible.builtin.package:
|
||
name: git
|
||
state: present
|
||
|
||
- name: Проверить, что синхронизируемые пути существуют
|
||
ansible.builtin.stat:
|
||
path: "{{ deploy_src_root }}/{{ item }}"
|
||
loop: "{{ gitea_sync_paths }}"
|
||
loop_control:
|
||
label: "{{ item }}"
|
||
delegate_to: localhost
|
||
become: false
|
||
register: gitea_sync_stat
|
||
|
||
- name: Прервать деплой, если путь из gitea_sync_paths отсутствует
|
||
ansible.builtin.fail:
|
||
msg: >-
|
||
Путь {{ item.item }} указан в gitea_sync_paths, но отсутствует в
|
||
репозитории. Список должен совпадать с тем, на что ссылается
|
||
clusters/aero — иначе kustomize в кластере упадёт.
|
||
loop: "{{ gitea_sync_stat.results }}"
|
||
loop_control:
|
||
label: "{{ item.item }}"
|
||
when: not item.stat.exists
|
||
|
||
# Архив собирается из РАБОЧЕГО ДЕРЕВА, а не из HEAD: синхронизировать нужно то,
|
||
# что лежит на диске, включая ещё не закоммиченные локально правки.
|
||
- name: Собрать архив синхронизируемых путей на control-node
|
||
ansible.builtin.command:
|
||
cmd: "tar -czf {{ gitea_sync_archive_local }} {{ gitea_sync_paths | join(' ') }}"
|
||
chdir: "{{ deploy_src_root }}"
|
||
delegate_to: localhost
|
||
become: false
|
||
changed_when: true
|
||
|
||
- name: Запомнить исходную ревизию (для сообщения коммита)
|
||
ansible.builtin.command:
|
||
cmd: git rev-parse --short HEAD
|
||
chdir: "{{ deploy_src_root }}"
|
||
delegate_to: localhost
|
||
become: false
|
||
register: gitea_sync_rev
|
||
changed_when: false
|
||
failed_when: false
|
||
|
||
- name: Скопировать архив на хост
|
||
ansible.builtin.copy:
|
||
src: "{{ gitea_sync_archive_local }}"
|
||
dest: "{{ gitea_sync_archive_remote }}"
|
||
mode: "0600"
|
||
|
||
- name: Синхронизировать и закоммитить содержимое в gitea
|
||
ansible.builtin.shell:
|
||
cmd: |
|
||
set -eu
|
||
WORKDIR={{ gitea_sync_workdir | quote }}
|
||
BRANCH={{ aero_git_branch | quote }}
|
||
URL={{ gitea_sync_url | quote }}
|
||
|
||
mkdir -p "$WORKDIR"
|
||
cd "$WORKDIR"
|
||
|
||
# Клон может не существовать, а репозиторий в gitea — быть пустым (до
|
||
# первого коммита у него нет ни одной ветки). init+fetch переживает оба
|
||
# случая, в отличие от `git clone`, который на пустом репозитории
|
||
# оставляет рабочую копию без ветки.
|
||
[ -d .git ] || git init -q
|
||
git config user.name {{ gitea_commit_name | quote }}
|
||
git config user.email {{ gitea_commit_email | quote }}
|
||
git remote remove origin 2>/dev/null || true
|
||
git remote add origin "$URL"
|
||
|
||
if git fetch -q origin "$BRANCH" 2>/dev/null; then
|
||
git checkout -q -B "$BRANCH" FETCH_HEAD
|
||
else
|
||
git checkout -q -B "$BRANCH"
|
||
fi
|
||
|
||
# Управляемые пути удаляются перед распаковкой, иначе файлы, удалённые в
|
||
# исходном репозитории, остались бы в gitea навсегда: `git add -A` увидит
|
||
# удаление только если файла действительно нет на диске.
|
||
for p in {{ gitea_sync_paths | join(' ') }}; do
|
||
rm -rf "$p"
|
||
done
|
||
|
||
tar -xzf {{ gitea_sync_archive_remote | quote }}
|
||
|
||
git add -A
|
||
if git diff --cached --quiet; then
|
||
echo "SYNC_RESULT=nochange"
|
||
else
|
||
git commit -q -m "sync from control-node ({{ gitea_sync_rev.stdout | default('unknown') }})"
|
||
echo "SYNC_RESULT=committed"
|
||
fi
|
||
|
||
git push -q origin "$BRANCH"
|
||
echo "SYNC_DONE"
|
||
chdir: "{{ deploy_dir }}"
|
||
register: gitea_sync_result
|
||
changed_when: "'SYNC_RESULT=committed' in gitea_sync_result.stdout"
|
||
no_log: true
|
||
|
||
- name: Удалить архив с хоста
|
||
ansible.builtin.file:
|
||
path: "{{ gitea_sync_archive_remote }}"
|
||
state: absent
|
||
|
||
- name: Удалить архив с control-node
|
||
ansible.builtin.file:
|
||
path: "{{ gitea_sync_archive_local }}"
|
||
state: absent
|
||
delegate_to: localhost
|
||
become: false
|
||
|
||
- name: Итог синхронизации
|
||
ansible.builtin.debug:
|
||
msg: >-
|
||
gitea {{ gitea_org }}/{{ gitea_repo }}@{{ aero_git_branch }}:
|
||
{{ 'закоммичены изменения' if 'SYNC_RESULT=committed' in gitea_sync_result.stdout
|
||
else 'изменений нет' }}
|
||
(источник {{ gitea_sync_rev.stdout | default('unknown') }},
|
||
путей: {{ gitea_sync_paths | length }})
|