Update tech_docs/docker_compose_guide.md

This commit is contained in:
2025-08-05 23:33:05 -05:00
parent b7fa21be0e
commit 8a5f28e3bb

View File

@@ -1,3 +1,339 @@
Here is a **drop-in Jinja2 template skeleton** that turns the entire service stanza into variables you can override at render-time (`docker compose -f <(jinja2 compose.yml.j2 env.yml)` or `docker compose config`).
You only need to set the variables you care about; everything else falls back to safe blanks or defaults.
```jinja2
# compose.yml.j2
services:
{{ service_name | default("app") }}:
# — Image or Build (mutually exclusive; image wins if provided) —
{% if image is defined %}
image: {{ image }}
{% elif build is defined %}
build:
context: {{ build.context | default(".") }}
dockerfile: {{ build.dockerfile | default("Dockerfile") }}
{% endif %}
# — Ports —
{% if ports is defined %}
ports:
{% for p in ports %}
- "{{ p }}"
{% endfor %}
{% endif %}
# — Environment —
{% if environment is defined %}
environment:
{% for k,v in environment.items() %}
{{ k }}: {{ v }}
{% endfor %}
{% endif %}
# — Volumes / Mounts —
{% if volumes is defined %}
volumes:
{% for v in volumes %}
- "{{ v }}"
{% endfor %}
{% endif %}
# — Dependencies —
{% if depends_on is defined %}
depends_on:
{% for d in depends_on %}
- "{{ d }}"
{% endfor %}
{% endif %}
# — Health Check —
{% if healthcheck is defined %}
healthcheck:
{% for k,v in healthcheck.items() %}
{{ k }}: {{ v }}
{% endfor %}
{% endif %}
# — User / Privileges —
user: {{ user | default("") }}
{% if cap_add is defined %}
cap_add:
{% for c in cap_add %}
- {{ c }}
{% endfor %}
{% endif %}
{% if cap_drop is defined %}
cap_drop:
{% for c in cap_drop %}
- {{ c }}
{% endfor %}
{% endif %}
{% if security_opt is defined %}
security_opt:
{% for s in security_opt %}
- {{ s }}
{% endfor %}
{% endif %}
# — Restart Policy —
restart: {{ restart | default("unless-stopped") }}
# — Networks —
{% if networks is defined %}
networks:
{% for n in networks %}
- {{ n }}
{% endfor %}
{% endif %}
# — Resource Limits / Placement —
{% if deploy is defined %}
deploy:
{% for k,v in deploy.items() %}
{{ k }}: {{ v }}
{% endfor %}
{% endif %}
# — Logging —
{% if logging is defined %}
logging:
driver: {{ logging.driver | default("json-file") }}
{% if logging.options is defined %}
options:
{% for k,v in logging.options.items() %}
{{ k }}: {{ v }}
{% endfor %}
{% endif %}
{% endif %}
# — Misc (blank if unset) —
command: {{ command | default("") | tojson if command else "" }}
entrypoint: {{ entrypoint | default("") | tojson if entrypoint else "" }}
container_name: {{ container_name | default("") }}
hostname: {{ hostname | default("") }}
working_dir: {{ working_dir | default("") }}
tty: {{ tty | default(false) | lower }}
stdin_open: {{ stdin_open | default(false) | lower }}
privileged: {{ privileged | default(false) | lower }}
init: {{ init | default(false) | lower }}
stop_grace_period: {{ stop_grace_period | default("10s") }}
stop_signal: {{ stop_signal | default("SIGTERM") }}
{% if tmpfs is defined %}
tmpfs:
{% for t in tmpfs %}
- {{ t }}
{% endfor %}
{% endif %}
{% if labels is defined %}
labels:
{% for k,v in labels.items() %}
{{ k }}: {{ v }}
{% endfor %}
{% endif %}
```
Usage example with a tiny `env.yml`:
```yaml
# env.yml
service_name: api
image: mycorp/api:1.4.2
ports:
- "8080:8080"
environment:
LOG_LEVEL: info
DB_URL: postgresql://db:5432/app
volumes:
- ./data:/app/data
depends_on:
- db
restart: always
```
Render and validate:
```bash
jinja2 compose.yml.j2 env.yml | docker compose -f - config # dry-run
```
All other knobs (secrets, configs, devices, ulimits, etc.) can be added the same way—only when you need them.
---
Below is the minimal, copy-paste-ready workflow that gives you:
- **instant feedback** while you type (IDE)
- **CI gates** that reject bad templates (GitHub Actions / pre-commit)
- **no false positives** from YAML or Docker linters that dont understand Jinja
--------------------------------------------------------
1. Install
--------------------------------------------------------
```bash
# 1.1 CLI linter (fast, zero-config)
pip install git+https://github.com/aristanetworks/j2lint.git #
# 1.2 Optional VS Code inline squiggles
code --install-extension samuelcolvin.jinjahtml #
```
--------------------------------------------------------
2. Local lint (CLI)
--------------------------------------------------------
```bash
# single file
j2lint compose.yml.j2
# whole directory, custom extensions, exit non-zero on error
j2lint templates/ --extensions j2,yaml --log --json
```
--------------------------------------------------------
3. Pre-commit hook (repo-level gate)
--------------------------------------------------------
Create `.pre-commit-config.yaml`:
```yaml
repos:
- repo: https://github.com/aristanetworks/j2lint
rev: v1.1.0 # pick latest tag
hooks:
- id: j2lint
args: ["--"] # keep pre-commit from treating other files as args
```
Then:
```bash
pip install pre-commit
pre-commit install # runs on every `git commit`
pre-commit run --all-files # one-shot check
```
--------------------------------------------------------
4. CI example (GitHub Action)
--------------------------------------------------------
`.github/workflows/lint-jinja.yml`
```yaml
name: Lint Jinja2
on: [push, pull_request]
jobs:
j2lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- run: pip install j2lint
- run: j2lint templates/ --json
```
--------------------------------------------------------
5. Render-time *syntax* check (fail fast)
--------------------------------------------------------
If you just want to know “is the Jinja valid?” without any style rules:
```bash
python - <<'PY'
import sys, os
from jinja2 import Environment, FileSystemLoader, TemplateSyntaxError
env = Environment()
for root, _, files in os.walk("templates"):
for f in files:
if f.endswith(".j2"):
try:
env.parse(open(os.path.join(root, f)).read())
except TemplateSyntaxError as e:
print(f"{f}: {e}")
sys.exit(1)
PY
```
This doubles as a unit test you can add to pytest.
--------------------------------------------------------
6. VS Code one-shot tasks.json (optional)
--------------------------------------------------------
`.vscode/tasks.json`
```json
{
"label": "j2lint",
"type": "shell",
"command": "j2lint",
"args": ["${file}"],
"group": "test",
"presentation": { "reveal": "always" }
}
```
Now ⇧⌘B → *j2lint* lints the file youre editing.
--------------------------------------------------------
Summary
--------------------------------------------------------
- **CLI**: `j2lint`
- **Editor**: VS Code extension
- **Gate**: pre-commit or GitHub Action using the same linter
- **Zero extra dependencies** beyond Python.
---
Below is the canonical “complete-line” set for a **Compose v3+ service block**, minus anything you already listed.
Each item is one line you can drop into a YAML service stanza.
```
image: …
build: …
ports: …
environment: …
volumes: …
depends_on: …
healthcheck: …
user: …
deploy: …
# --- the missing ones ---
command: # override CMD
entrypoint: # override ENTRYPOINT
container_name: # fixed name (beware singleton)
restart: # no | always | on-failure | unless-stopped
networks: # list of custom networks
dns: # override DNS servers
dns_search: # override DNS search domains
extra_hosts: # static /etc/hosts entries
hostname: # container hostname
domainname: # container NIS domain name
ipc: # ipc mode (shareable, private, host, container:…)
mac_address: # static MAC
privileged: # true/false (danger)
cap_add: # add Linux capabilities
cap_drop: # drop Linux capabilities
device_cgroup_rules: # e.g., ["c 116:* rmw"]
devices: # expose host devices
security_opt: # ["no-new-privileges:true"]
sysctls: # kernel tunables
ulimits: # {"nproc": 65535, "nofile": {"soft": 1024, "hard": 2048}}
working_dir: # override WORKDIR
labels: # arbitrary key/val metadata
logging: # driver + options
stop_grace_period: # SIGKILL timeout
stop_signal: # override STOPSIGNAL
stdin_open: # -i (keep STDIN open)
tty: # -t (allocate pseudo-TTY)
init: # run tini (true or path)
tmpfs: # tmpfs mounts list
secrets: # named secrets
configs: # named configs
isolation: # Windows isolation mode
platform: # linux/amd64, linux/arm64, etc.
```
If you can rattle off what every one of those does, the Compose surface is covered.
---
Learning-GOAL: “I can read, reason about and harden any Dockerfile or `docker run` incantation I meet—without drowning in trivia.”
Below is a **minimal, language-agnostic curriculum** expressed as executable **pseudocode**.