Part 11 · Docker · Execution Labs

Docker: execution labs

Kiểm chứng bằng evidence thay vì chỉ đọc lý thuyết: image layers và build cache, supply chain, process signals, resource limits, read-only filesystem và service networking.

Cách làm lab: ghi lại Docker/BuildKit version, command đã chạy, log/metrics trước–trong–sau failure injection và kết luận. Mục tiêu là giải thích được vì sao hệ thống có hành vi đó và recovery có đúng kỳ vọng hay không.

Lab A · Multi-stage và reproducible image

Objective

Chứng minh multi-stage build tách build dependencies khỏi runtime image, hiểu cache invalidation, kiểm tra supply-chain artifacts và nhận diện nguồn làm image digest không reproducible.

Prerequisites

FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /src
COPY pom.xml .
RUN mvn -B dependency:go-offline
COPY src src
RUN mvn -B -DskipTests package

FROM eclipse-temurin:21-jre
RUN useradd --system --uid 10001 app
COPY --from=build /src/target/app.jar /app/app.jar
USER 10001
ENTRYPOINT ["java","-jar","/app/app.jar"]

Steps

  1. Build lần đầu từ trạng thái cold, sau đó build lại khi không đổi input. Lưu duration và các layer được reuse.
  2. Chỉ đổi source code rồi build lại; sau đó đổi pom.xml hoặc dependency input và build lại. So sánh layer nào bị invalidated.
  3. Đo image size và đọc docker image history. Generate SBOM và scan CVE cho final image.
  4. Inspect final image/layers để xác nhận source tree, Maven cache và secrets không bị copy vào runtime stage.
  5. Build hai lần với pinned inputs; so image digest. Nếu khác nhau, truy nguồn nondeterminism như mutable base image, build timestamp, archive ordering, generated metadata hoặc dependency thay đổi.

Failure injection & verification

Expected evidence

EvidenceĐiều cần chứng minh
Cold/cache/no-cache timings + build logCache reuse và invalidation boundary đúng như Dockerfile ordering.
Image size + historyRuntime image không kéo theo build tool/layer không cần thiết.
SBOM + CVE reportBiết runtime artifact chứa gì và scanner phát hiện gì ở thời điểm kiểm tra.
Digest comparisonPinned inputs chưa đủ nếu build output còn timestamp/metadata nondeterministic.
Production note: image tag không phải immutable guarantee. Với deployment cần traceability, lưu source revision, Dockerfile, build provenance/SBOM và immutable image digest; rollback phải trỏ lại digest đã được verify.

Lab B · PID 1 và graceful shutdown

Objective

Quan sát trực tiếp signal path khi container dừng, khác biệt giữa exec-form và shell wrapper, và chứng minh application có ngừng nhận việc mới trước khi hoàn tất in-flight work.

Prerequisites

Steps

  1. Chạy một request/consumer dài, rồi gửi docker stop --time 20 <container>.
  2. So exec-form ENTRYPOINT ["java","-jar","/app/app.jar"] với shell wrapper không dùng exec.
  3. Quan sát timeline: SIGTERM đến process nào, lúc nào readiness/intake dừng, in-flight work có hoàn tất không và exit code là gì.
  4. Inject process con; kiểm tra child termination và zombie/reaping nếu application spawn child.

Failure injection & verification

Expected evidence

Operational signal: theo dõi termination duration, forced-kill count, unfinished work và restart rate. Nếu thường xuyên chạm stop timeout, tăng timeout chỉ là chữa triệu chứng; phải kiểm tra drain logic, downstream timeout và job granularity.

Lab C · Resource limits

Objective

Hiểu memory/CPU limits ở cấp container, cách JVM và thread/connection pools phản ứng dưới pressure, và thu thập evidence để phân biệt OOM, throttling, GC pressure và saturation.

Prerequisites

docker run --memory=512m --cpus=0.5 app:test

Steps

  1. Đặt JVM heap không chiếm toàn bộ 512 MiB để còn headroom cho metaspace, thread stacks, direct/native memory và runtime overhead.
  2. Inject allocation đến khi OOM/failure; lưu container state, exit code và JVM/container logs.
  3. Tạo CPU load để quan sát throttling, latency và GC khi container chỉ có 0.5 CPU.
  4. So unbounded threads/connections với bounded pools dưới cùng resource limit.

Failure injection & verification

Expected evidence

SignalCâu hỏi cần trả lời
Memory usage / JVM heap / native headroomFailure do heap đầy, native memory, hay container limit?
Container exit state + logsProcess tự fail hay bị OOM killer kết thúc?
CPU throttling + latencyQuota thấp làm latency/GC/queue tăng đến mức nào?
Threads/connections/queue depthBounded pool có ngăn saturation lan sang downstream không?
Capacity & recovery: limit phải đi cùng load test và headroom. Sau OOM/restart, kiểm tra startup surge, backlog catch-up và retry storm; nếu workload không idempotent, restart có thể biến một resource incident thành duplicate side effects.

Lab D · Read-only và networking

Objective

Chứng minh application chạy được với immutable root filesystem, chỉ mở đúng write paths cần thiết, dùng service DNS thay vì hard-code localhost, và recover đúng khi database connection bị stale sau restart.

Prerequisites

Steps

  1. Chạy container với read-only root filesystem; cấp tmpfs hoặc volume chỉ cho các thư mục thực sự cần write.
  2. Test bind mount ownership/UID với non-root user và kiểm tra secret file permissions.
  3. Chạy app + DB bằng Compose service DNS; application kết nối bằng service name, không hard-code localhost.
  4. Restart DB để tạo stale connection; kiểm tra connect/read timeout, retry/backoff và connection-pool recovery.
# Ví dụ minh họa; điều chỉnh path theo ứng dụng thật
docker run --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m app:test

Failure injection & verification

Expected evidence

Security boundary: read-only root filesystem và non-root UID giảm attack surface nhưng không thay thế least privilege, secret management, network policy/firewall hay application authorization. tmpfs dùng memory của container, vì vậy write-heavy temp data cũng phải nằm trong capacity model.

Deliverables

Tài liệu tham khảo chính thức: Docker multi-stage builds · BuildKit · SBOM attestations · Dockerfile ENTRYPOINT · Resource constraints · tmpfs mounts · Compose networking.