Files
Lucas WintherandClaude Opus 5 7b52e9af9e Add the compose_stack role, inventory and playbooks
One role that syncs a payload and brings the stack up, configured entirely
through stack_* variables, plus six playbooks that each demonstrate one part
of that contract. Every playbook runs standalone and is tagged with its
stack name, so site.yml --tags <stack> works.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-16 03:58:01 +02:00

69 lines
2.9 KiB
YAML

---
# One stack, both hosts, one payload.
#
# Demonstrates three things:
# - stack_src pointing outside the per-host convention, so two hosts share a
# single payload instead of keeping duplicate copies in step by hand;
# - a per-host variable map, which is how a play covers hosts that differ
# without growing a second playbook or a `when:` on every task;
# - stack_dirs entries that set ownership, and ones that only set a mode.
- name: Deploy metrics
hosts:
- edge
- app
gather_facts: false
tags: [metrics]
vars:
# Only the app host runs anything worth proxying a dashboard to, so only it
# joins the private backend network. Keyed by inventory_hostname and looked
# up below — adding a host means adding a line here, not a branch.
metrics_networks:
edge:
- proxy-net
app:
- proxy-net
- backend
metrics_dirs:
# The Grafana image runs as uid 472 and this is a bind mount, so the
# directory has to be owned by that uid on the host. `recurse: true` with
# no `mode`: recursing a mode here would rewrite the permissions of
# Grafana's SQLite database, which holds hashed credentials and datasource
# secrets.
#
# This belongs in stack_dirs rather than a pre_tasks chown. pre_tasks run
# before the role creates the stack directory, and an escalated
# ansible.builtin.file on a child stamps its owner onto every parent it
# creates on the way down — which would leave the payload sync, running as
# the deploy user, unable to write into the stack's own directory on a host
# where metrics has never been deployed.
- path: "{{ stack_root }}/metrics/grafana-data"
owner: "472"
group: "472"
recurse: true
# Written by the prometheus container, which runs as nobody (65534). A mode
# rather than an owner because the uid is not stable across image versions.
# Narrow this to an owner once you have confirmed the uid on your hosts.
- path: "{{ stack_root }}/metrics/prometheus-data"
mode: "0777"
roles:
- role: compose_stack
vars:
stack_name: metrics
# Both hosts run the same thing, so the payload lives under server/shared/
# rather than being copied into server/edge/ and server/app/.
stack_src: "{{ repo_root }}/server/shared/metrics"
stack_networks: "{{ metrics_networks[inventory_hostname] }}"
stack_dirs: "{{ metrics_dirs }}"
post_tasks:
- name: Reload prometheus so a changed scrape configuration takes effect
# prometheus.yml is bind-mounted, so Compose never recreates the container
# for a change to it. Prometheus re-reads its configuration on SIGHUP.
ansible.builtin.command:
cmd: docker kill --signal=SIGHUP prometheus
when: compose_stack_synced | default(false)
changed_when: true