--- # --- Предпосылки на хосте ------------------------------------------------- # Загружаем ДО подъёма 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" # nginx монтирует ./nginx/templates и ./nginx/certs — без шаблонов nginx не # стартует, поэтому копируем конфигурацию рядом с compose. # Изменение шаблона → handler пересоздаёт nginx (envsubst рендерит на старте). - name: Скопировать шаблоны nginx ansible.builtin.copy: src: "{{ deploy_src_root }}/nginx/templates/" dest: "{{ deploy_dir }}/nginx/templates/" mode: "0644" notify: Recreate nginx # backend монтирует ./backend/uwsgi.ini (http-socket + 4 воркера) и # ./backend/production.py (классическая JWT-аутентификация). # Изменение конфигов → handler пересоздаёт backend и celery. - name: Скопировать конфиги backend (uwsgi.ini, production.py) ansible.builtin.copy: src: "{{ deploy_src_root }}/backend/" dest: "{{ deploy_dir }}/backend/" mode: "0644" notify: Recreate backend and celery # k3s-манифесты DNS-моста (namespace processing + Service/Endpoints postgres/minio). # processing-k8s-init монтирует ./k3s/manifests/processing и применяет их kubectl'ом. - name: Скопировать k3s-манифесты (DNS-мост processing) ansible.builtin.copy: src: "{{ deploy_src_root }}/k3s/manifests/" dest: "{{ deploy_dir }}/k3s/manifests/" mode: "0644" # engine монтирует ./engine/yc-s3-service-account.json (доступ Job-подов к S3). - name: Скопировать конфиги engine (S3 service-account) ansible.builtin.copy: src: "{{ deploy_src_root }}/engine/" dest: "{{ deploy_dir }}/engine/" 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] # --- Самоподписанный TLS-сертификат для трёх доменов ---------------------- - name: Создать каталог для сертификатов ansible.builtin.file: path: "{{ cert_dir }}" state: directory mode: "0750" - name: Сгенерировать самоподписанный сертификат (SAN на все домены) ansible.builtin.shell: # umask 077 → приватный ключ создаётся сразу с правами 0600 cmd: >- umask 077 && openssl req -x509 -nodes -newkey rsa:2048 -days {{ cert_days }} -keyout {{ cert_dir }}/selfsigned.key -out {{ cert_dir }}/selfsigned.crt -subj "/CN={{ platform_domain }}" -addext "subjectAltName=DNS:{{ platform_domain }},DNS:{{ admin_domain }},DNS:{{ minio_domain }}" creates: "{{ cert_dir }}/selfsigned.crt" # --- Доступ к приватному реестру (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] # --- Поднять сервисы приложения ------------------------------------------ - name: Поднять сервисы приложения (docker compose up -d) ansible.builtin.command: cmd: "{{ compose_cmd }} up -d {{ sarex_services | join(' ') }}" chdir: "{{ deploy_dir }}" when: sarex_compose_up | bool changed_when: true tags: [up] # --- 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]