iac/aero/roles/sarex_stack/tasks/gitea-sync.yml
emelinda 565faa0e17 feat(aero): базовый слой контура — Vault↔k3s, GitOps-синхронизация, выход наружу
Топология 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 сливает с дефолтами, возвращая их целиком.
2026-08-08 09:36:08 +03:00

141 lines
6.0 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.

---
# Синхронизация репозитория контура в 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 }})