Модуль 18 / 18
Фінальний проєкт: production-ready контейнерна платформа
Зміст
1. Фінальний проєкт і Definition of Done
Спроєктуйте, зберіть, захистіть, запустіть і прийміть single-host platform із gateway та application. Результат відтворюється з файлів, не ручного стану host.
- non-root image
- назовні лише gateway
- internal backend network
- readiness керує startup
- read-only і cap_drop
- log limits
- crash recovery
- evidence, runbook і rollback
2. Архітектура та межі
Client звертається до loopback gateway. Gateway має frontend і backend networks; app — лише internal backend. DNS використовує ім’я app, не container IP.
Compose придатний для production на одному host, але не дає multi-host HA, scheduler чи rolling updates. Далі потрібні Swarm, Kubernetes або managed platform.
3. Build, configuration та release
Dockerfile створює UID 10001 та exec-form CMD. Pipeline фіксує base digest, створює SBOM/provenance, scan і signature release digest.
Deploy отримує перевірений immutable image. Secrets не входять до image, environment чи Git; їх доставляє secret manager.
4. Надійність та lifecycle
Healthcheck визначає readiness, depends_on усуває startup race, restart policy обробляє crash. Unhealthy сам не перезапускає standalone container.
App приймає SIGTERM. Під час restart можливий короткий 502; production HA потребує replicas та orchestrator.
5. Security та network policy
Сервіси non-root, read-only, no-new-privileges і cap_drop ALL. Writable paths — лише tmpfs. Loopback port не відкриває service зовнішній мережі.
Internal network не є повною firewall policy: перевіряйте routes, DNS, host rules і Docker socket.
6. Observability, backup та rollback
Evidence: resolved config, digest, health, restart count, resources, logs і networks. Alerts: SLO, saturation, restarts, disk bytes/inodes, certificates.
Rollback розгортає попередній digest. Stateful service потребує consistent backup, restore drill, RPO/RTO та сумісних migrations.
7. Методичні вказівки
- Відтворіть файли точно
- Не лікуйте privileged mode
- Перевірте resolved config
- Фіксуйте expected result
- Evidence до cleanup
- Owner/severity alerts
- Deploy і rollback як команди
- Обмеження single-host
8. Лабораторна робота: production-ready stack
Створіть app із health, controlled crash і SIGTERM, non-root image та gateway.
rm -rf /tmp/docker-m18
mkdir -p /tmp/docker-m18/app /tmp/docker-m18/nginx
cat > /tmp/docker-m18/app/server.py <<'PY'
import json, os, signal
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/health": body=b'{"status":"ok"}'
elif self.path == "/crash":
self.send_response(202); self.end_headers(); self.wfile.write(b"crashing\n"); self.wfile.flush(); os._exit(42)
else: body=json.dumps({"service":"docker-capstone","pid":os.getpid()}).encode()
if self.path != "/crash":
self.send_response(200); self.send_header("Content-Type","application/json")
self.send_header("Content-Length",str(len(body))); self.end_headers(); self.wfile.write(body)
def log_message(self, fmt, *args): print(fmt % args, flush=True)
server=HTTPServer(("0.0.0.0",8080),Handler); server.timeout=.5; stopping=False
def stop(signum, frame):
global stopping; stopping=True
signal.signal(signal.SIGTERM,stop); signal.signal(signal.SIGINT,stop)
while not stopping: server.handle_request()
server.server_close()
PY
cat > /tmp/docker-m18/app/Dockerfile <<'EOF'
# syntax=docker/dockerfile:1
FROM python:3.13-alpine
RUN addgroup -S app && adduser -S -G app -u 10001 app
WORKDIR /app
COPY --chown=10001:10001 server.py .
USER 10001:10001
EXPOSE 8080
CMD ["python","server.py"]
EOF
cat > /tmp/docker-m18/nginx/nginx.conf <<'EOF'
pid /tmp/nginx.pid;
events {}
http {
access_log /dev/stdout;
error_log /dev/stderr warn;
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
server {
listen 8080;
location / { proxy_pass http://app:8080; proxy_set_header Host $host; }
}
}
EOFОпишіть hardening, networks, readiness, logs та lifecycle.
cat > /tmp/docker-m18/compose.yaml <<'EOF'
name: docker-m18
services:
app:
build: ./app
image: docker-m18-app:1.0.0
init: true
read_only: true
tmpfs: [/tmp]
restart: on-failure:3
stop_grace_period: 10s
healthcheck:
test: ["CMD","python","-c","import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/health')"]
interval: 2s
timeout: 1s
retries: 5
security_opt: [no-new-privileges:true]
cap_drop: [ALL]
networks: [backend]
logging: &logs
driver: json-file
options: {max-size: "1m", max-file: "2"}
gateway:
image: nginx:1.29-alpine
user: "101:101"
entrypoint: ["nginx","-g","daemon off;"]
read_only: true
tmpfs: ["/tmp:mode=1777"]
depends_on:
app: {condition: service_healthy}
ports: ["127.0.0.1:18080:8080"]
volumes: ["./nginx/nginx.conf:/etc/nginx/nginx.conf:ro"]
security_opt: [no-new-privileges:true]
cap_drop: [ALL]
restart: unless-stopped
networks: [frontend, backend]
logging: *logs
networks:
frontend: {}
backend: {internal: true}
EOFValidate, build та wait readiness.
cd /tmp/docker-m18
docker compose config -q
docker compose build
docker compose up --detach --wait
curl --fail --silent http://127.0.0.1:18080/ | jq
docker compose ps
docker inspect docker-m18-app-1 --format 'user={{.Config.User}} readonly={{.HostConfig.ReadonlyRootfs}} caps={{json .HostConfig.CapDrop}} security={{json .HostConfig.SecurityOpt}}'Ініціюйте exit 42 та доведіть recovery.
before=$(docker inspect --format '{{.RestartCount}}' docker-m18-app-1)
curl --silent http://127.0.0.1:18080/crash || true
for attempt in 1 2 3 4 5 6 7 8 9 10; do
after=$(docker inspect --format '{{.RestartCount}}' docker-m18-app-1)
status=$(docker inspect --format '{{.State.Status}}' docker-m18-app-1)
[ "$after" -gt "$before" ] && [ "$status" = running ] && break
sleep 1
done
printf 'before=%s after=%s status=%s\n' "$before" "$after" "$status"
test "$after" -gt "$before"
for attempt in 1 2 3 4 5; do
curl --fail --silent http://127.0.0.1:18080/health && break
sleep 1
done9. Приймання та діагностика
Доведіть: який artifact запущено, UID, external surface, writable paths, health і recovery.
cd /tmp/docker-m18
docker compose config
docker compose images
docker compose ps
docker compose logs --since 10m
docker stats --no-stream
docker network inspect docker-m18_backend
docker inspect docker-m18-app-1 --format '{{json .State}}' | jq
curl -sS -D - -o /dev/null http://127.0.0.1:18080/health- config — resolved model
- build — BuildKit vertex
- unhealthy — health/app logs
- 502 — backend DNS/readiness/restart
- permission — path/UID/capability
- no space — logs/cache/layers/volumes/inodes
10. Захист проєкту та cleanup
- Architecture/data flow
- Обґрунтування controls
- Single-host failure domains
- Crash/recovery evidence
- Digest promotion
- SLI/SLO та alerts
- Backup/restore state
- Rollback rehearsal
cd /tmp/docker-m18
docker compose down --remove-orphans
docker image rm docker-m18-app:1.0.0
rm -rf /tmp/docker-m18
docker network ls --filter name=docker-m18
docker ps -a --filter name=docker-m18Курс завершено, якщо platform відтворюється з clean host, проходить acceptance, дає evidence та чесно описує limitations.