feat(semaphore): rewrite role with rootful Podman Quadlet + PostgreSQL
Complete rewrite of the semaphore role. Supersedes three prior
iterations whose admin-user-creation logic was unreliable across
Semaphore CLI versions.
Architecture:
- Rootful Podman Quadlet under /etc/containers/systemd/
- Separate PostgreSQL 16-alpine container on a user-defined
podman network (semaphore-net)
- Named volumes for both data stores (semaphore_data,
semaphore_postgres_data) so container recreation is
non-destructive
- Pinned image tags: semaphoreui/semaphore:v2.18.5-ansible2.16.5
and postgres:16-alpine
- Post-deploy HTTP health check fails the playbook if Semaphore
doesn't respond on /api/ping within ~60s
Admin user creation remains intentionally manual after first deploy;
the role README documents the exact podman exec command.
Removes the duplicate deploy_semaphore.yml and the now-unneeded
cleanup_semaphore.yml; day1_deploy_semaphore.yml is the canonical
entry point.
This commit is contained in:
117
ansible/roles/semaphore/README.md
Normal file
117
ansible/roles/semaphore/README.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# semaphore
|
||||
|
||||
Deploys [SemaphoreUI](https://semaphoreui.com) with a PostgreSQL backend on
|
||||
the mk-labs `imagineering` VM (`figment`) via rootful Podman Quadlets.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
user ─── HTTPS ─── Traefik ──▶│ figment (10.1.71.37) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ semaphore │ │
|
||||
│ │ :3000 │──┼──┐
|
||||
│ └─────────────────┘ │ │ semaphore-net
|
||||
│ │ │ (podman)
|
||||
│ ┌─────────────────┐ │ │
|
||||
│ │ semaphore- │◀─┼──┘
|
||||
│ │ postgres :5432 │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ /etc/containers/ │
|
||||
│ systemd/*.container│
|
||||
└───────────────────────┘
|
||||
```
|
||||
|
||||
- Both containers run as **rootful** Podman services, managed by
|
||||
systemd-generated units from Quadlet files in `/etc/containers/systemd/`.
|
||||
- The two containers share a user-defined Podman network
|
||||
(`semaphore-net`) so Semaphore can address PostgreSQL by container name.
|
||||
- Data persists on two named Podman volumes (`semaphore_data`,
|
||||
`semaphore_postgres_data`) so container recreation is non-destructive.
|
||||
- Image tags are **pinned** — no floating `:latest`.
|
||||
|
||||
## Required vault variables
|
||||
|
||||
| Variable | Purpose |
|
||||
|-----------------------------------------|---------------------------------------------------------|
|
||||
| `vault_semaphore_database_password` | PostgreSQL role password for the `semaphore` DB user. |
|
||||
| `vault_semaphore_admin_password` | Initial admin password (used during manual user-add). |
|
||||
| `vault_semaphore_access_key_encryption` | 32-byte key for Semaphore-stored access keys. |
|
||||
|
||||
Generate the access-key encryption value with:
|
||||
|
||||
```bash
|
||||
head -c 32 /dev/urandom | base64
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
- name: Deploy SemaphoreUI
|
||||
hosts: semaphore_server
|
||||
become: true
|
||||
roles:
|
||||
- semaphore
|
||||
```
|
||||
|
||||
Or via the dedicated playbook:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory.yml playbooks/day1_deploy_semaphore.yml
|
||||
```
|
||||
|
||||
## First-run admin user creation (manual)
|
||||
|
||||
Semaphore's CLI `user add` is intentionally invoked once, by hand, after
|
||||
the first deployment succeeds:
|
||||
|
||||
```bash
|
||||
sudo podman exec -it semaphore semaphore user add --admin \
|
||||
--login admin \
|
||||
--name "Administrator" \
|
||||
--email admin@local.mk-labs.cloud \
|
||||
--password '<vault_semaphore_admin_password>'
|
||||
```
|
||||
|
||||
Subsequent password rotations should also be done via the CLI, not by
|
||||
re-running this role.
|
||||
|
||||
## Pinned versions
|
||||
|
||||
- **Semaphore**: `docker.io/semaphoreui/semaphore:v2.18.5-ansible2.16.5`
|
||||
(bundles Ansible 2.16.5 inside the container, matching our controller)
|
||||
- **PostgreSQL**: `docker.io/library/postgres:16-alpine`
|
||||
|
||||
To bump versions, update `semaphore_image` / `semaphore_postgres_image`
|
||||
in `defaults/main.yml`. Test against figment, snapshot first.
|
||||
|
||||
## Verification
|
||||
|
||||
The role completes with an HTTP health check against
|
||||
`http://127.0.0.1:3000/api/ping`. If Semaphore doesn't respond within
|
||||
~60 seconds, the role fails loudly rather than reporting green-but-broken.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# Container status
|
||||
sudo systemctl status semaphore semaphore-postgres
|
||||
sudo podman ps -a
|
||||
|
||||
# Logs
|
||||
sudo journalctl -u semaphore -f
|
||||
sudo journalctl -u semaphore-postgres -f
|
||||
sudo podman logs semaphore --tail 100
|
||||
|
||||
# Network
|
||||
sudo podman network inspect semaphore-net
|
||||
|
||||
# Wipe and redeploy (destructive — destroys all Semaphore data)
|
||||
sudo systemctl stop semaphore semaphore-postgres
|
||||
sudo podman volume rm semaphore_data semaphore_postgres_data
|
||||
sudo rm /etc/containers/systemd/semaphore.container /etc/containers/systemd/semaphore-postgres.container
|
||||
sudo systemctl daemon-reload
|
||||
# then re-run the playbook
|
||||
```
|
||||
Reference in New Issue
Block a user