c8601c0115
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m5s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m39s
Two prod-deploy hardening measures (owner-requested): - Swap file (Ansible common role, swap_size=1G, vm.swappiness=10). The per-container memory caps enforce that one service can't eat all RAM, but they overcommit the 1.9 GiB main host (~2.8 GiB of caps), so a simultaneous spike could hit the kernel OOM-killer (and it might pick postgres). A small swap absorbs the overshoot. Idempotent, builtin-only (no ansible.posix). - Edge maintenance page. prod-deploy.sh raises a flag around the rolling swap / migration window that the caddy edge serves a static 503 "технические работы" page from (deploy/caddy/maintenance.html), for the user-facing routes only — /_gm (Grafana) stays reachable. Cleared on any exit (success, health failure + rollback, or error) by a shell trap so it can never stick on. The 503 carries Retry-After + an X-Scrabble-Maintenance marker so a follow-up SPA overlay can tell a planned window apart from a transient error (the static page only catches a fresh load; an in-session user needs the app-side overlay). Not zero-downtime — the single stateful backend still blips — but the window is graceful instead of raw 502s. Verified: caddy validate; the gate 503s every non-/_gm path incl. the Connect/gRPC edge and serves the page + markers; /_gm bypasses; toggling needs no reload (per-request stat). Ansible --syntax-check + compose config (base+prod) pass.
30 lines
1.6 KiB
YAML
30 lines
1.6 KiB
YAML
---
|
|
# Service account the CI prod-deploy workflow uses to drive docker on the hosts.
|
|
# Membership in the docker group is root-equivalent (docker socket access), which
|
|
# is all the deploy workflow needs; the account is deliberately not given sudo.
|
|
deploy_user: deploy
|
|
|
|
# Public half of the dedicated CI deploy SSH key, read from the controller at run
|
|
# time. The private half is generated on the controller during provisioning and
|
|
# stored ONLY in the Gitea PROD_SSH_KEY secret; it is never committed. Override the
|
|
# path with -e deploy_ci_pubkey_path=/path/to/key.pub if the key lives elsewhere.
|
|
deploy_ci_pubkey_path: "{{ lookup('env', 'HOME') }}/.ssh/scrabble_ci_deploy_ed25519.pub"
|
|
deploy_ci_pubkey: "{{ lookup('file', deploy_ci_pubkey_path) }}"
|
|
|
|
# Base directory the deploy workflow rsyncs compose files, config, certs and dumps
|
|
# into. Owned by deploy_user so the workflow needs no elevation.
|
|
scrabble_base_dir: /opt/scrabble
|
|
|
|
# Docker daemon json-file log rotation, mirroring the compose x-logging anchor so
|
|
# the host's own containers (and any ad-hoc runs) rotate identically.
|
|
docker_log_max_size: "10m"
|
|
docker_log_max_file: "3"
|
|
|
|
# Swap file as an OOM cushion. The per-container memory caps (docker-compose.prod.yml)
|
|
# sum to more than the tight main host's RAM (2 vCPU / 1.9 GiB), so a simultaneous
|
|
# spike could otherwise hit the kernel OOM-killer and it might pick postgres. A small
|
|
# swap absorbs the overshoot; swappiness stays low so swap is a cushion, not a hot
|
|
# path (a slow service beats a killed one). Set swap_size to "0" to skip provisioning.
|
|
swap_size: "1G"
|
|
swap_swappiness: 10
|