--- # --- Предпосылки на хосте ------------------------------------------------- # Загружаем ДО подъёма k3s: istio-init внутри подов правит iptables через ядро # хоста, и без этих модулей поды в namespace с istio-injection не стартуют. # persistent: present закрепляет загрузку в /etc/modules-load.d, иначе после # перезагрузки хоста контур снова развалился бы. - name: Загрузить модули ядра, нужные istio-init community.general.modprobe: name: "{{ item }}" state: present persistent: present loop: "{{ aero_kernel_modules }}" tags: [kernel] # --- Каталоги и файлы ---------------------------------------------------- - name: Создать каталог развёртывания ansible.builtin.file: path: "{{ deploy_dir }}" state: directory owner: root group: root mode: "0750" tags: [env] - name: Скопировать docker-compose.yaml ansible.builtin.copy: src: "{{ deploy_src_root }}/docker-compose.yaml" dest: "{{ deploy_dir }}/docker-compose.yaml" mode: "0640" # k3s-манифесты DNS-мостов: flux-system → gitea (монтирует flux-k8s-init) и # vault → Vault в compose (применяет vault-k8s.yml). Оба нужны потому, что у # кластера своя DNS и compose-имена в ней не резолвятся. - name: Скопировать k3s-манифесты (DNS-мосты gitea и vault) ansible.builtin.copy: src: "{{ deploy_src_root }}/k3s/manifests/" dest: "{{ deploy_dir }}/k3s/manifests/" mode: "0644" # vault монтирует ./vault/config как :ro — без конфига сервер не стартует. - name: Скопировать конфигурацию Vault ansible.builtin.copy: src: "{{ deploy_src_root }}/vault/config/" dest: "{{ deploy_dir }}/vault/config/" mode: "0644" # Каталоги PVC-данных k3s — по одному на ноду. Создаём заранее: иначе docker # создаст их от root с umask демона, а local-path пишет туда от имени подов. - name: Создать каталоги PVC-хранилища k3s (по ноде) ansible.builtin.file: path: "{{ deploy_dir }}/k3s-storage/{{ item }}" state: directory mode: "0755" loop: "{{ aero_k3s_nodes }}" # --- Секреты: генерация с персистом на control-node (идемпотентно) -------- - name: Сгенерировать/загрузить пароли (persist в secrets_store, gitignored) ansible.builtin.set_fact: sarex_secrets: >- {{ sarex_secrets | default({}) | combine({ item: lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/' ~ item ~ ' length=' ~ secret_length ~ ' chars=ascii_letters,digits') }) }} loop: "{{ sarex_secret_vars }}" no_log: true tags: [env] # Учётка администратора платформы. Живёт отдельно от общего цикла выше по двум # причинам: у логина свой алфавит (Django не примет username со спецсимволами) # и оба значения не должны попасть в .env — compose-стек их не использует, # потребитель один, и это Vault. # # Генерируется всегда, а не по флагу sarex_create_superuser: значения уезжают # в secrets/apps/django/superuser (см. vault_app_secrets), откуда их забирает # стартовый скрипт пода backend. Без них vault-agent не отрендерит шаблон и # под встанет в Init:Error. lookup('password') идемпотентен — на повторных # прогонах возвращает уже сохранённое значение, а не генерирует новое. - name: Сгенерировать/загрузить логин администратора платформы ansible.builtin.set_fact: superuser_name: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_NAME chars=ascii_lowercase,digits length=10') }}" no_log: true tags: [env, superuser] - name: Сгенерировать/загрузить пароль администратора платформы ansible.builtin.set_fact: superuser_password: "{{ lookup('password', secrets_store ~ '/' ~ inventory_hostname ~ '/SAREX_SUPERUSER_PASSWORD chars=ascii_letters,digits length=' ~ secret_length) }}" no_log: true tags: [env, superuser] # RSA-ключи JWT (RS512): генерируем на control-node, persist в secrets_store. - name: Сгенерировать приватный RSA-ключ JWT ansible.builtin.command: "openssl genrsa -out {{ jwt_private_key_path }} 4096" args: creates: "{{ jwt_private_key_path }}" delegate_to: localhost become: false tags: [env] - name: Извлечь публичный RSA-ключ JWT ansible.builtin.command: "openssl rsa -in {{ jwt_private_key_path }} -pubout -out {{ jwt_public_key_path }}" args: creates: "{{ jwt_public_key_path }}" delegate_to: localhost become: false tags: [env] - name: Собрать словарь переопределений для .env (пароли + домены + JWT) ansible.builtin.set_fact: env_overrides: >- {{ sarex_secrets | combine({ 'PLATFORM_DOMAIN': platform_domain, 'ADMIN_DOMAIN': admin_domain, 'MINIO_DOMAIN': minio_domain, 'SAREX_MINIO_CONSOLE_URL': 'https://' ~ minio_domain, 'GITEA_ADMIN_USER': gitea_admin_user, 'GITEA_ADMIN_EMAIL': gitea_admin_email, 'GITEA_ORG': gitea_org, 'GITEA_REPO': gitea_repo, 'AERO_GIT_BRANCH': aero_git_branch, 'AERO_FLUX_PATH': aero_flux_path, 'SAREX_JWT_PRIVATE_KEY': "'" ~ (lookup('file', jwt_private_key_path) | replace('\n', '\\n')) ~ "'", 'SAREX_JWT_PUBLIC_KEY': "'" ~ (lookup('file', jwt_public_key_path) | replace('\n', '\\n')) ~ "'", }) }} no_log: true tags: [env] - name: Отрендерить .env из .env.example с переопределениями ansible.builtin.template: src: env.j2 dest: "{{ deploy_dir }}/.env" owner: root group: root mode: "0600" no_log: true tags: [env] # --- Доступ к приватному реестру (docker на хосте + registries.yaml нод) --- # Обязательно ДО подъёма k3s: registries.yaml монтируется в ноды, и k3s читает # его только при старте. - name: Настроить доступ к приватному реестру ansible.builtin.include_tasks: registry.yml tags: [login, registry] # --- GitOps-подложка: k3s + gitea + vault + Flux -------------------------- # Отдельный тег и отдельный флаг: подложка поднимается публичными образами и # не зависит от docker login, поэтому её можно накатывать независимо от стека. - name: Поднять GitOps-подложку контура ansible.builtin.include_tasks: platform.yml when: aero_platform_up | bool tags: [platform] # --- Досоздание баз в работающей СУБД ------------------------------------- # Только по тегу dbsync. Обоснование — в шапке databases.yml. - name: Досоздать недостающие базы ansible.builtin.include_tasks: databases.yml when: sarex_dbsync | bool tags: [dbsync] # --- Перекат нагрузок с изменённой конфигурацией -------------------------- # Только по тегу reload: правка ConfigMap сама под не перезапускает, а делать # это на каждом прогоне нельзя. Обоснование — в шапке reload.yml. - name: Перекатить нагрузки с изменённой конфигурацией ansible.builtin.include_tasks: reload.yml when: sarex_reload | bool tags: [reload] # --- Django-суперпользователь ---------------------------------------------- # Самого создания здесь больше нет — его делает под backend при каждом старте # (apps/django/aero/kustomization.yaml, args стартового скрипта). Так учётка # появляется вместе с приложением, а не отдельной командой оператора, и # переживает пересоздание пода без ручного вмешательства. # # Ansible остаётся владельцем самих значений: генерирует их выше (тег env) и # кладёт в Vault. Здесь — только показать логин по явному запросу # (uv run poe superuser передаёт sarex_create_superuser=true). - name: Показать учётные данные администратора платформы ansible.builtin.debug: msg: "Django superuser → login: {{ superuser_name }} password: {{ superuser_password }}" when: sarex_create_superuser | default(false) | bool tags: [superuser]