docs(reference-ansible): add docs/ tree and document repo, playbooks, Makefile
Addresses the WKS PoC review (Notion 2026-05-26). All docs in English. - README: purpose, docs table of contents, annotated repo tree - docs/getting_started.md: prerequisites (WKS account, OIDC, SSH, VPN) + first deploy - docs/ansible.md: playbook table, "Running Ansible", service parameters, cheatsheet - docs/secrets.md: canonical Bao login (moved out of README) + demo defaults - docs/operations.md: full Makefile reference - docs/inventories.md: repo layout, topology, standard folder structure, walkthrough - docs/testing.md: static checks, inventory resolution, smoke test / dry run - remove ARCHITECTURE.md (architecture docs live externally) Also includes the gymburgdorf inventory build-out (bookstack, homarr, opnform, send) and scripts/bao-seed.sh. site.yml keeps a third traefik play (traefik_servers minus the vagrant _dmz/_backend split) so the demo inventories still configure their reverse proxy after the rebase onto main.
This commit is contained in:
parent
c67e9aac43
commit
2ba0c07cd3
24 changed files with 1541 additions and 525 deletions
179
docs/inventories.md
Normal file
179
docs/inventories.md
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<!-- markdownlint-disable MD013 MD060 MD051 -->
|
||||
# Repo layout & inventories
|
||||
|
||||
[← Documentation index](README.md)
|
||||
|
||||
## Repo layout and role origin
|
||||
|
||||
```text
|
||||
reference-ansible/
|
||||
├── Makefile # deploy targets, OIDC login, OBJC fork workaround
|
||||
├── ansible.cfg # collections_path, remote_user=root, hashi_vault auth_method=token
|
||||
├── requirements.yml # community.hashi_vault + digitalboard.core (Git)
|
||||
├── playbooks/site.yml # play sequence (see ansible.md)
|
||||
├── scripts/bao-seed.sh # seed/merge OpenBao secrets per inventory
|
||||
├── docs/ # this documentation
|
||||
├── collections/ # ← installed by `make install`, gitignored
|
||||
│ └── ansible_collections/
|
||||
│ └── digitalboard/core/
|
||||
│ └── roles/ # 🔑 THE ROLES LIVE HERE, NOT in the repo root
|
||||
└── inventories/
|
||||
├── demo-gymburgdorf/ # reference inventory of this documentation
|
||||
├── demo-mbazürich/
|
||||
├── demo-phbern/
|
||||
└── vagrant/ # local test inventory with its own topology
|
||||
```
|
||||
|
||||
> **Important:** There is **no** `roles/` directory in the repo root. All
|
||||
> roles come from the `digitalboard.core` collection (see
|
||||
> [requirements.yml](../requirements.yml)), installed via
|
||||
> `make install` into `./collections/`. Plays reference them by
|
||||
> FQCN `digitalboard.core.<role>`.
|
||||
|
||||
## Available inventories
|
||||
|
||||
| Inventory | Purpose |
|
||||
| --- | --- |
|
||||
| [`demo-gymburgdorf/`](../inventories/demo-gymburgdorf/) | Demo tenant — **recommended as the template for new tenants** |
|
||||
| [`demo-mbazürich/`](../inventories/demo-mbazürich/) | Demo tenant |
|
||||
| [`demo-phbern/`](../inventories/demo-phbern/) | Demo tenant |
|
||||
| [`vagrant/`](../inventories/vagrant/) | local test VMs; **incompatible group topology** with the demo inventories |
|
||||
|
||||
## Inventory topology (`demo-gymburgdorf`)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
classDef dmz fill:#fee2e2,stroke:#991b1b,color:#000
|
||||
classDef app fill:#dcfce7,stroke:#166534,color:#000
|
||||
classDef stor fill:#dbeafe,stroke:#1e40af,color:#000
|
||||
classDef turn fill:#fef9c3,stroke:#854d0e,color:#000
|
||||
|
||||
subgraph ALL["group: all_servers"]
|
||||
direction LR
|
||||
subgraph DMZ["DMZ 172.16.9.0/24"]
|
||||
RP["<b>reverseproxy</b><br/>172.16.9.111<br/>traefik_mode: dmz"]:::dmz
|
||||
TURN["<b>turn</b><br/>172.16.9.112<br/>(no role in site.yml yet)"]:::turn
|
||||
end
|
||||
subgraph BE["Backend 172.16.19.0/24<br/>group: backend_servers"]
|
||||
APP["<b>application</b><br/>172.16.19.101<br/>traefik_mode: backend<br/>+ authentik, authentik_outpost_ldap,<br/> nextcloud, collabora, drawio, …"]:::app
|
||||
ST["<b>storage</b><br/>172.16.19.102<br/>traefik_mode: backend<br/>+ garage (S3)"]:::stor
|
||||
end
|
||||
end
|
||||
|
||||
RP -.HTTPS in, HTTP out.-> APP
|
||||
RP -.HTTPS in, HTTP out.-> ST
|
||||
```
|
||||
|
||||
**Group memberships (from [hosts.yml](../inventories/demo-gymburgdorf/hosts.yml)):**
|
||||
|
||||
| Group | Members | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `all_servers` | `reverseproxy`, `application`, `storage`, `turn` | base role for all hosts |
|
||||
| `traefik_servers` | `children: all_servers` (all 4 hosts) | Traefik everywhere; DMZ/backend via `traefik_mode` |
|
||||
| `backend_servers` | `application`, `storage` | sets `traefik_mode: backend` via group_var |
|
||||
| `garage_servers` | `storage` | single-host wrapper for the Garage role |
|
||||
| `nextcloud_servers`, `collabora_servers`, `drawio_servers`, `authentik_servers`, `authentik_outpost_ldap_servers`, `send_servers`, `opnform_servers`, `homarr_servers`, `bookstack_servers` | only `application` each | single-host wrappers |
|
||||
|
||||
> **Difference from the `vagrant` inventory:** `vagrant` structures
|
||||
> Traefik differently — via the children groups `traefik_servers_dmz` and
|
||||
> `traefik_servers_backend` instead of via `backend_servers` +
|
||||
> `host_vars` override. The two topologies are **structurally
|
||||
> incompatible**; a 1:1 mapping is not possible. For new tenants, therefore,
|
||||
> take `demo-gymburgdorf` as the template.
|
||||
|
||||
## Standard folder structure of an inventory entry
|
||||
|
||||
A fully built-out inventory follows this layout (example
|
||||
`demo-gymburgdorf`). Currently only this inventory is built out;
|
||||
`demo-mbazürich` and `demo-phbern` so far contain only `hosts.yml`.
|
||||
|
||||
```text
|
||||
inventories/demo-<kunde>/
|
||||
├── hosts.yml # REQUIRED — hosts, IPs, group topology
|
||||
├── group_vars/
|
||||
│ ├── all/
|
||||
│ │ ├── vault.yml # REQUIRED — vault_addr, vault_mount (Bao)
|
||||
│ │ ├── ansible.yml # ansible_python_interpreter etc.
|
||||
│ │ └── docker.yml # docker_registry_mirrors
|
||||
│ ├── traefik_servers/
|
||||
│ │ └── traefik.yml # ACME/TSIG, TLS — applies to ALL Traefik instances
|
||||
│ └── backend_servers/
|
||||
│ └── traefik.yml # traefik_mode: backend (default for app + storage)
|
||||
└── host_vars/
|
||||
├── reverseproxy/
|
||||
│ └── traefik.yml # traefik_mode: dmz + DMZ-specific ACME overrides
|
||||
├── application/
|
||||
│ ├── main.yml # comment only: which services run here
|
||||
│ ├── traefik.yml # traefik_dmz_exposed_services (what the DMZ routes)
|
||||
│ └── <service>.yml # one file per service (nextcloud, authentik, …)
|
||||
└── storage/
|
||||
├── main.yml # same as above
|
||||
├── traefik.yml # traefik_extra_hosts + traefik_dmz_exposed_services
|
||||
└── garage.yml # service vars for garage
|
||||
```
|
||||
|
||||
**Conventions:**
|
||||
|
||||
- **`hosts.yml` is the only hard required file.** Vars are
|
||||
optional — if one is missing, the role defaults from
|
||||
`digitalboard.core` take effect. A new inventory therefore starts minimally with
|
||||
only `hosts.yml` (just like `demo-mbazürich`/`demo-phbern`).
|
||||
- **`group_vars/all/vault.yml`** is effectively required as soon as
|
||||
Bao lookups are supposed to work — without `vault_mount`/`vault_addr` the
|
||||
secret lookups fail.
|
||||
- **One file per service** under `host_vars/<host>/<service>.yml`. The
|
||||
file name is free (Ansible loads all YAMLs in the directory); by
|
||||
convention it is named like the role. Which variables belong where:
|
||||
[ansible.md § Where parameters belong](ansible.md#where-parameters-belong).
|
||||
- **`main.yml` per host** is pure documentation — a comment indicating which
|
||||
services run on the host. Carries no productive vars.
|
||||
- **`host_vars/<host>/traefik.yml`** declares via
|
||||
`traefik_dmz_exposed_services` which local services the
|
||||
DMZ Traefik should make reachable from outside. The DMZ reads this
|
||||
list via `hostvars[<backend>]` and renders its routers from it. A new
|
||||
service exposed externally = a new entry here. Mechanics:
|
||||
[ansible.md § traefik](ansible.md#traefik).
|
||||
|
||||
## Walkthrough: Creating a new demo tenant
|
||||
|
||||
Recommended template: **`demo-gymburgdorf`** (not `vagrant`, because its
|
||||
group topology is incompatible).
|
||||
|
||||
1. **Copy the inventory:**
|
||||
|
||||
```bash
|
||||
cp -r inventories/demo-gymburgdorf inventories/demo-<kunde>
|
||||
```
|
||||
|
||||
2. **Adjust `hosts.yml`:** IPs, hostnames per host.
|
||||
|
||||
3. **`group_vars/all/vault.yml`** — set `vault_mount` to the new
|
||||
tenant mount (`demo-<kunde>`).
|
||||
|
||||
4. **`group_vars/traefik_servers/traefik.yml`** —
|
||||
point `traefik_acme_dns_zone` and the `acme-tsig` lookup paths to the
|
||||
new zone / the new Bao path.
|
||||
|
||||
5. Go through **`host_vars/application/*.yml`** and **`host_vars/storage/*.yml`**:
|
||||
FQDNs to the new domain pattern (e.g.
|
||||
`*.<kunde>.souveredu.ch`), Bao lookup paths to `demo-<kunde>/data/…`.
|
||||
|
||||
6. **Prepare OpenBao** (out-of-band, not via Ansible):
|
||||
- Create a new KV-v2 mount `demo-<kunde>`.
|
||||
- Write secrets: `acme-tsig`, `authentik`, `nextcloud`,
|
||||
`garage`, … — conveniently via `make seed_bao_<kunde>` (see
|
||||
[scripts/bao-seed.sh](../scripts/bao-seed.sh) and
|
||||
[secrets.md § Demo-Only-Defaults](secrets.md#demo-only-defaults--must-be-overridden)).
|
||||
- Policy for the deploy token: read on `demo-<kunde>/data/*`.
|
||||
|
||||
7. **DNS:** Create the TSIG update zone (`demo-<kunde>._acme.digitalboard.ch`) at
|
||||
`ns1.digitalboard.ch`, CNAMEs
|
||||
`_acme-challenge.*.<kunde>.<tld>` pointing there.
|
||||
|
||||
8. **Makefile** — add a new target modeled on
|
||||
`deploy_site_demo_gymburgdorf` and add it to `deploy_site_demo`;
|
||||
likewise a `seed_bao_<kunde>` target.
|
||||
|
||||
9. **Smoke test:** `ansible all -i inventories/demo-<kunde>/hosts.yml -m ping`.
|
||||
|
||||
10. **Deploy:** Bao login + `make deploy_site_demo_<kunde>`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue