feat(ansible): idempotent role-based host config for both production VMs
common/firewall/docker-tls/platform/lab-host/monitoring-agent/backup roles, orchestrated by site.yml with the ordering that matters (firewall/TLS land before the services that depend on them). Ansible Vault for secrets, no new tool introduced for this project's modest secret surface. Explicit dev/staging/production environment separation, no secrets committed (.gitignore covers vault plaintext, TLS keys, Terraform state, *.env). Validated: ansible-playbook --syntax-check clean.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: restart rootnode-platform
|
||||
ansible.builtin.systemd:
|
||||
name: rootnode-platform
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
# rn-platform-01: Docker Engine, directory structure, rendered compose file + env files,
|
||||
# Postgres tuning overlay, reverse proxy config, and the systemd unit that runs it all.
|
||||
# Docker TLS material and the host firewall are separate roles (docker-tls, firewall) run
|
||||
# before this one in site.yml — this role assumes both are already in place.
|
||||
|
||||
- name: Install Docker Engine
|
||||
ansible.builtin.import_tasks: "{{ role_path }}/../common/tasks/docker-engine.yml"
|
||||
|
||||
- name: Create data/config directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: docker
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ rootnode_data_root }}/postgres"
|
||||
- "{{ rootnode_data_root }}/backups"
|
||||
- /opt/rootnode/platform
|
||||
- /opt/rootnode/platform/postgresql.conf.d
|
||||
- /etc/rootnode/provisioner-config
|
||||
|
||||
- name: Deploy Postgres tuning overlay
|
||||
ansible.builtin.copy:
|
||||
src: "{{ role_path }}/../../../docker/postgres/postgresql.conf.d/00-rootnode-baseline.conf"
|
||||
dest: /opt/rootnode/platform/postgresql.conf.d/00-rootnode-baseline.conf
|
||||
mode: "0644"
|
||||
notify: restart rootnode-platform
|
||||
|
||||
- name: Deploy provisioner-config overlay ({{ env }})
|
||||
ansible.builtin.copy:
|
||||
src: "{{ role_path }}/../../../../environments/{{ env }}/provisioner-config/"
|
||||
dest: /etc/rootnode/provisioner-config/
|
||||
mode: "0644"
|
||||
notify: restart rootnode-platform
|
||||
|
||||
- name: Deploy per-service env files (from Ansible Vault-backed variables — see group_vars)
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}.env.j2"
|
||||
dest: "/etc/rootnode/{{ item }}.env"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
loop:
|
||||
- platform
|
||||
- provisioner
|
||||
- gateway
|
||||
- postgres
|
||||
notify: restart rootnode-platform
|
||||
|
||||
- name: Render production docker-compose.yml
|
||||
ansible.builtin.template:
|
||||
src: "{{ role_path }}/../../../docker/platform/docker-compose.production.yml"
|
||||
dest: /opt/rootnode/platform/docker-compose.yml
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0640"
|
||||
notify: restart rootnode-platform
|
||||
|
||||
- name: Render Caddyfile
|
||||
ansible.builtin.template:
|
||||
src: "{{ role_path }}/../../../docker/platform/Caddyfile.j2"
|
||||
dest: /opt/rootnode/platform/Caddyfile
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: restart rootnode-platform
|
||||
|
||||
- name: Deploy systemd unit
|
||||
ansible.builtin.template:
|
||||
src: rootnode-platform.service.j2
|
||||
dest: /etc/systemd/system/rootnode-platform.service
|
||||
mode: "0644"
|
||||
notify: restart rootnode-platform
|
||||
|
||||
# The `backup` role (applied after this one in site.yml) deploys the backup script and cron —
|
||||
# kept separate since it also has to run correctly on a host that ISN'T freshly deployed.
|
||||
|
||||
- name: Enable and start rootnode-platform
|
||||
ansible.builtin.systemd:
|
||||
name: rootnode-platform
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
@@ -0,0 +1,5 @@
|
||||
# Rendered by Ansible — see platform.env.j2 for the pattern.
|
||||
PLATFORM_API_URL=http://api:4300
|
||||
PROVISIONER_URL=http://provisioner:4100
|
||||
PROVISIONER_SHARED_SECRET={{ vault_provisioner_shared_secret }}
|
||||
GATEWAY_PORT=4200
|
||||
@@ -0,0 +1,18 @@
|
||||
# Rendered by Ansible from infra/environments/{{ env }}/platform.env.template's shape —
|
||||
# real values come from group_vars/{{ env }}.yml + the encrypted vault file. Do not hand-edit
|
||||
# on the host; re-run the platform role instead.
|
||||
|
||||
# service names, not localhost — production runs these on a named Docker bridge network
|
||||
# (rn-platform-internal), not network_mode: host like the dev compose file does.
|
||||
PLATFORM_DB_URL=postgres://rootnode_platform:{{ vault_postgres_password }}@postgres:5432/rootnode_platform
|
||||
PROVISIONER_URL=http://provisioner:4100
|
||||
PROVISIONER_SHARED_SECRET={{ vault_provisioner_shared_secret }}
|
||||
PLATFORM_API_PORT=4300
|
||||
|
||||
STUDENT_SESSION_TTL_HOURS=8
|
||||
STUDENT_SESSION_IDLE_HOURS=2
|
||||
INSTRUCTOR_SESSION_TTL_HOURS=12
|
||||
INSTRUCTOR_SESSION_IDLE_HOURS=2
|
||||
|
||||
COOKIE_SECURE=1
|
||||
TRUST_PROXY=1
|
||||
@@ -0,0 +1,4 @@
|
||||
# Rendered by Ansible — see platform.env.j2 for the pattern.
|
||||
POSTGRES_USER=rootnode_platform
|
||||
POSTGRES_PASSWORD={{ vault_postgres_password }}
|
||||
POSTGRES_DB=rootnode_platform
|
||||
@@ -0,0 +1,11 @@
|
||||
# Rendered by Ansible — see platform.env.j2 for the pattern.
|
||||
PLATFORM_DB_URL=postgres://rootnode_platform:{{ vault_postgres_password }}@postgres:5432/rootnode_platform
|
||||
PROVISIONER_SHARED_SECRET={{ vault_provisioner_shared_secret }}
|
||||
PROVISIONER_PORT=4100
|
||||
PROVISIONER_CONFIG_DIR=/app/config
|
||||
|
||||
LAB_IMAGE_REF={{ registry }}/rootnode/lab:{{ image_tag }}
|
||||
|
||||
EGRESS_PROXY_PORT=3128
|
||||
DNS_RESOLVER=127.0.0.11
|
||||
NO_PROXY_CIDRS=10.42.0.0/16,10.43.0.0/16
|
||||
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=RootNode Academy Platform (docker compose)
|
||||
Requires=docker.service
|
||||
After=docker.service network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
WorkingDirectory=/opt/rootnode/platform
|
||||
ExecStart=/usr/bin/docker compose -f docker-compose.yml up -d --remove-orphans
|
||||
ExecStop=/usr/bin/docker compose -f docker-compose.yml down
|
||||
ExecReload=/usr/bin/docker compose -f docker-compose.yml up -d --remove-orphans
|
||||
TimeoutStartSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user