--- # Подъём GitOps-подложки контура: k3s + gitea + vault + bootstrap FluxCD. # Отделено от прикладного стека (sarex_services) намеренно: здесь только # публичные образы, docker login в приватный реестр не требуется. # # Порядок важен и поэтому разбит на две волны: flux-bootstrap монтирует # ./k3s/kubeconfig.yaml, который k3s-server создаёт только при первом старте. # Если поднять всё одной командой, docker может создать на месте отсутствующего # файла директорию, и bootstrap упадёт с невнятной ошибкой. - name: Волна 1 — поднять k3s-server и gitea ansible.builtin.command: cmd: "{{ compose_cmd }} up -d {{ aero_platform_services_wave1 | join(' ') }}" chdir: "{{ deploy_dir }}" changed_when: true - name: Дождаться kubeconfig от k3s-server ansible.builtin.wait_for: path: "{{ deploy_dir }}/k3s/kubeconfig.yaml" state: present timeout: "{{ aero_kubeconfig_timeout }}" # Пустой файл означает, что k3s-server ещё дописывает kubeconfig. Без этой # проверки flux-bootstrap может стартовать с нулевым конфигом. - name: Убедиться, что kubeconfig не пустой ansible.builtin.wait_for: path: "{{ deploy_dir }}/k3s/kubeconfig.yaml" search_regex: "clusters:" timeout: 60 # Секреты реестра — до bootstrap: без yc-cr-auth в flux-system source-controller # не сможет скачать ни один OCI-чарт, и вся первая волна инфраструктуры встанет. - name: Создать секреты доступа к реестру в кластере ansible.builtin.include_tasks: registry-secrets.yml - name: Волна 2 — gitea-init, vault(+init) и bootstrap Flux ansible.builtin.command: cmd: "{{ compose_cmd }} up -d {{ aero_platform_services_wave2 | join(' ') }}" chdir: "{{ deploy_dir }}" changed_when: true # flux-bootstrap одноразовый: успех = код 0. Забираем его явно, иначе ошибка # bootstrap'а останется незамеченной — `compose up -d` о ней не сообщает. # # `docker compose wait` здесь не годится: для уже завершившегося контейнера он # возвращает rc=1 и "no containers for project". Так как `up -d` не дожидается # одноразового сервиса, состояние гоночное — поэтому опрашиваем `ps -a`, # который корректно отвечает и для running, и для exited. # # Формат обёрнут в {% raw %}: это Go-шаблон docker'а, и без raw Jinja попыталась # бы раскрыть {{.State.Status}} как свою переменную. Аргументы передаём списком — # у `command` нет шелла, и строка со скобками разбилась бы по пробелу. - name: Дождаться завершения flux-bootstrap ansible.builtin.command: argv: - "{{ compose_cmd.split()[0] }}" - inspect - "-f" - "{% raw %}{{.State.Status}} {{.State.ExitCode}}{% endraw %}" - flux-bootstrap register: aero_flux_bootstrap changed_when: false retries: 30 delay: 10 until: aero_flux_bootstrap.stdout | trim is not match('^running') - name: Показать логи flux-bootstrap при ошибке ansible.builtin.command: cmd: "{{ compose_cmd }} logs --no-color --tail 50 flux-bootstrap" chdir: "{{ deploy_dir }}" register: aero_flux_logs changed_when: false when: aero_flux_bootstrap.stdout.split() | last != "0" - name: Прервать деплой, если bootstrap не удался ansible.builtin.fail: msg: | flux bootstrap завершился с кодом {{ aero_flux_bootstrap.stdout.split() | last }}. Логи контейнера: {{ aero_flux_logs.stdout | default('(недоступны)') }} when: aero_flux_bootstrap.stdout.split() | last != "0" - name: Итог ansible.builtin.debug: msg: - "gitea: http://{{ ansible_host }}:3000 (пользователь {{ gitea_admin_user }}, пароль в aero/.secrets/{{ inventory_hostname }}/GITEA_ADMIN_PASSWORD)" - "Flux реконсилирует {{ gitea_org }}/{{ gitea_repo }} → {{ aero_flux_path }}" - "Проверка: docker compose exec k3s-server kubectl -n flux-system get kustomizations"