The fleet on emily is rootless Podman with Quadlet units under ~/.config/containers/systemd and state in ~/podman-data, all behind one shared Caddy unit. This brings the menus in line instead of leaving them the last Docker deployment. The build moves with them: podman reads a Containerfile as readily as a Dockerfile, so the name now says which toolchain is real. The app is one .container and one .network file. Two details carry it. UserNS=keep-id maps the image's own node account onto the user that owns the units, so data/ and site.env stay owned by tanshu: plain tar backups, no subuids. provision-site-env.sh writes site.env with the same mapping rather than --user, which under rootless Podman would have produced a subuid-owned file the app could not read. And the Site registers its own network with Caddy by writing a drop-in beside caddy.container, so Caddy never lists its upstreams and never changes when a Site is added. The unit keeps the Docker-era container name for the scripts and muscle memory that look it up, while the Caddyfile proxies to a dotless NetworkAlias, because a container name with dots in it is a liability in DNS. The unit keeps podman's Pull=missing, so a restart or a reboot never depends on the registry being reachable, while the play pulls explicitly at deploy time. That is also where a missing `podman login` for the private registry now fails, with the instruction, rather than as a unit that will not start. copy-site-data.sh moves a Site's data across, and is not a plain rsync: /var/lib/menu.* on monoco belongs to uid 1000 while the ssh user there is 1001, so the read needs sudo; monoco cannot ssh to emily, so the workstation relays the tar; and the extraction runs as tanshu on emily, which is what makes the files land owned by the user keep-id maps the app onto. It proves the copy by comparing a SHA-256 of the whole tree against the source and checking every file's owner. Not yet done: the copies themselves and the DNS move, which need a human.
60 lines
2.6 KiB
YAML
60 lines
2.6 KiB
YAML
---
|
|
# Defaults shared by every Site. Each Site's own file is named by the `var_file`
|
|
# in ansible/hosts, and its slug is its inventory name.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Rootless Podman / Quadlet
|
|
# ---------------------------------------------------------------------------
|
|
# podman_user is derived from the connection: this play carries no become, so the
|
|
# Quadlet units belong to whoever Ansible connects as. Requires gather_facts
|
|
# (set in playbook.yml).
|
|
#
|
|
# These three duplicate caddy/vars/default.yml in the ansible-playbooks repo,
|
|
# which owns the Caddy unit these Sites sit behind. Keep them in sync.
|
|
podman_user: "{{ ansible_facts['user_id'] }}"
|
|
podman_data_dir: "{{ ansible_facts['user_dir'] }}/podman-data"
|
|
quadlet_dir: "{{ ansible_facts['user_dir'] }}/.config/containers/systemd"
|
|
|
|
# Which Site config inside the image to run, and which web address it serves.
|
|
site: '{{ inventory_hostname }}'
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The app
|
|
# ---------------------------------------------------------------------------
|
|
image_name: registry.tanshu.com/menu-admin
|
|
image_tag: "{{ tag | default('latest') }}"
|
|
|
|
# The unit file is menu-<site>.container and the Podman network is
|
|
# menu-<site>_net. The container keeps the Docker-era name, dots and all, because
|
|
# scripts/provision-site-env.sh and anything an operator types look it up that
|
|
# way — but a name with dots is a DNS liability, so the Caddyfile proxies to the
|
|
# dotless NetworkAlias the unit sets beside it.
|
|
network_name: 'menu-{{ site }}'
|
|
unit_name: '{{ network_name }}.service'
|
|
container_name: 'menu-app.{{ host }}'
|
|
|
|
# The image's own account (`node`), which keep-id maps onto podman_user.
|
|
app_uid: 1000
|
|
app_gid: 1000
|
|
|
|
container_port: 3000
|
|
|
|
# podman builds an OCI image by default and drops the Containerfile's
|
|
# HEALTHCHECK instruction, so the unit declares the same check and the play gates
|
|
# on it. node is in the image and the sh -c podman wraps it in handles the quotes.
|
|
health_cmd: >-
|
|
node -e "fetch('http://127.0.0.1:{{ container_port }}/healthz').then(r =>
|
|
process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The shared Caddy unit and its Caddyfile, both owned by the caddy playbook in
|
|
# ansible-playbooks (caddy/playbook.yml).
|
|
# ---------------------------------------------------------------------------
|
|
caddy_name: caddy
|
|
caddyfile_path: '{{ podman_data_dir }}/{{ caddy_name }}/conf/Caddyfile'
|
|
|
|
caddy_snippet: |
|
|
{{ host }} {
|
|
reverse_proxy {{ network_name }}:{{ container_port }}
|
|
}
|