Update tech_docs/docker_compose_guide.md
This commit is contained in:
@@ -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 don’t 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 you’re 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**.
|
||||
|
||||
Reference in New Issue
Block a user