Add the example stack payloads
The files copied to /srv/stacks/<stack>/ verbatim: compose files, vhosts, Prometheus configuration, the webapp image source. Payloads are data, never templated and never linted as Ansible content. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7b52e9af9e
commit
b76c76e3b0
@@ -0,0 +1,11 @@
|
||||
# Copy to .env ON THE HOST, not here. The compose_stack role excludes .env from
|
||||
# every sync (stack_exclude), so a .env committed to this repository would be
|
||||
# ignored, and a host's .env is never overwritten by a deploy.
|
||||
#
|
||||
# ssh [email protected]
|
||||
# cd /srv/stacks/webapp && cp .env.example .env && $EDITOR .env
|
||||
#
|
||||
# webapp.yml refuses to deploy if .env is absent on the host, because every
|
||||
# variable below is required with no default.
|
||||
POSTGRES_PASSWORD=
|
||||
SECRET_KEY=
|
||||
@@ -0,0 +1,11 @@
|
||||
# Stands in for a real application image. The point of building from the payload
|
||||
# rather than pulling is that stack_build then controls when a rebuild happens —
|
||||
# see "Deploy versus update" in the README.
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY serve.py migrate.sh ./
|
||||
RUN chmod +x migrate.sh
|
||||
|
||||
EXPOSE 8080
|
||||
CMD ["python", "serve.py"]
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# Stands in for a real migration runner. The property that matters is the one
|
||||
# asserted here: running it twice must be a no-op the second time, because an
|
||||
# update run can be repeated and the playbook does not remember whether the last
|
||||
# one finished.
|
||||
set -eu
|
||||
|
||||
echo "migrate: applying any pending migrations against ${DATABASE_URL%%:*}..."
|
||||
echo "migrate: already up to date"
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Minimal stand-in for an application server."""
|
||||
|
||||
import http.server
|
||||
import os
|
||||
|
||||
PORT = int(os.environ.get("PORT", "8080"))
|
||||
|
||||
|
||||
class Handler(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self): # noqa: N802 - name fixed by the stdlib
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"webapp is running\n")
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
print(fmt % args, flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
http.server.HTTPServer(("", PORT), Handler).serve_forever()
|
||||
@@ -0,0 +1,39 @@
|
||||
# Maintenance services, layered on top of docker-compose.yml with a second -f.
|
||||
#
|
||||
# These are one-shot `run --rm` targets, never part of the running stack, which
|
||||
# is why docker-compose.yml alone is what the playbook passes to `up` via
|
||||
# stack_files. Bringing the stack up with both files would start them as
|
||||
# long-running services.
|
||||
services:
|
||||
backup:
|
||||
image: postgres:17-alpine
|
||||
profiles: ["maintenance"]
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
PGPASSWORD: ${POSTGRES_PASSWORD:?set it in .env on the host}
|
||||
volumes:
|
||||
- ./backups:/backups
|
||||
entrypoint:
|
||||
- sh
|
||||
- -c
|
||||
- 'pg_dump -h db -U webapp webapp > /backups/webapp-$(date +%Y%m%dT%H%M%S).sql'
|
||||
networks:
|
||||
- backend
|
||||
|
||||
migrate:
|
||||
build:
|
||||
context: ./app
|
||||
image: example/webapp:local
|
||||
profiles: ["maintenance"]
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgres://webapp:${POSTGRES_PASSWORD:?set it in .env on the host}@db:5432/webapp
|
||||
# Migrations must be safe to run twice: an update run can be repeated, and
|
||||
# the playbook does not track whether the last one finished.
|
||||
command: ["/app/migrate.sh"]
|
||||
networks:
|
||||
- backend
|
||||
@@ -0,0 +1,59 @@
|
||||
# A database-backed application: the stack that needs more than "sync and up".
|
||||
#
|
||||
# Every credential comes from .env, with no defaults. `${VAR:?message}` makes
|
||||
# Compose refuse to start and say which variable is missing, which is strictly
|
||||
# better than booting with an empty password. .env lives on the host and is never
|
||||
# synced from this repository, so ansible/playbooks/webapp.yml asserts it is
|
||||
# there before doing anything.
|
||||
services:
|
||||
db:
|
||||
container_name: webapp-db
|
||||
image: postgres:17-alpine
|
||||
restart: unless-stopped
|
||||
# ./pgdata is deliberately NOT pre-created by the playbook: Postgres refuses
|
||||
# to start unless its data directory is 0700 or 0750, and the image gets that
|
||||
# right on first start. See "Common mistakes" in skills/adding-a-stack.
|
||||
volumes:
|
||||
- ./pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: webapp
|
||||
POSTGRES_USER: webapp
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set it in .env on the host}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U webapp"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- backend
|
||||
|
||||
web:
|
||||
container_name: webapp
|
||||
# Built from the payload rather than pulled. stack_build in the playbook
|
||||
# decides when a rebuild happens: on an update run, not on every deploy.
|
||||
build:
|
||||
context: ./app
|
||||
image: example/webapp:local
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgres://webapp:${POSTGRES_PASSWORD:?set it in .env on the host}@db:5432/webapp
|
||||
SECRET_KEY: ${SECRET_KEY:?set it in .env on the host}
|
||||
# Set by the role to stack_dest, so a path handed to a sibling container is
|
||||
# the path on the host and not wherever the SSH session happened to land.
|
||||
UPLOAD_HOST_DIR: ${PWD}/uploads
|
||||
VIRTUAL_HOST: app.example.com
|
||||
VIRTUAL_PORT: "8080"
|
||||
LETSENCRYPT_HOST: app.example.com
|
||||
volumes:
|
||||
- ./uploads:/var/lib/webapp/uploads
|
||||
networks:
|
||||
- backend
|
||||
- proxy-net
|
||||
|
||||
networks:
|
||||
backend:
|
||||
proxy-net:
|
||||
external: true
|
||||
Reference in New Issue
Block a user