--- # Подъём 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, flux-k8s-init и vault(+init) ansible.builtin.command: cmd: "{{ compose_cmd }} up -d {{ aero_platform_services_wave2 | join(' ') }}" chdir: "{{ deploy_dir }}" changed_when: true # vault-init инициализирует и распечатывает Vault в цикле с шагом 10 секунд, # поэтому сразу после `up -d` root-токена ещё нет. Он нужен следующему шагу, # который настраивает auth/kubernetes. - name: Дождаться инициализации Vault (появления root-токена) ansible.builtin.command: cmd: "{{ compose_cmd }} exec -T vault-init test -s /vault/init/root.token" chdir: "{{ deploy_dir }}" register: aero_vault_ready until: aero_vault_ready.rc == 0 retries: 30 delay: 5 changed_when: false # Метод auth/kubernetes: Vault учится валидировать токены подов k3s, а поды # получают секреты файлами через vault-agent-injector (ставится Flux'ом). - name: Настроить интеграцию Vault с k3s ansible.builtin.include_tasks: vault-k8s.yml tags: [vault] # Наполнение gitea — ДО bootstrap: Flux начинает реконсиляцию сразу после # привязки, и репозиторий к этому моменту должен содержать clusters/aero # вместе со всем, на что тот ссылается. - name: Синхронизировать репозиторий контура в gitea ansible.builtin.include_tasks: gitea-sync.yml tags: [sync] - name: Волна 3 — bootstrap Flux ansible.builtin.command: cmd: "{{ compose_cmd }} up -d {{ aero_platform_services_wave3 | 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 }}" - "Слои: infra-controllers (istio, cert-manager, vault-injector, local-path, rabbitmq, minio) → infra-configs → apps" - "Состояние Flux: docker compose exec k3s-server kubectl -n flux-system get kustomizations,helmreleases -A" - "Dashboard: https://dashboard.{{ platform_domain }}" - "RabbitMQ: https://rabbitmq.{{ platform_domain }} (пользователь {{ sarex_rabbitmq_k8s_user }}, пароль в aero/.secrets/{{ inventory_hostname }}/SAREX_RABBITMQ_PASSWORD)" - "MinIO UI: https://minio.{{ platform_domain }}/console/ (пользователь {{ sarex_minio_k8s_user }}, пароль в aero/.secrets/{{ inventory_hostname }}/SAREX_MINIO_ROOT_PASSWORD)"