30 câu hỏi Docker
Ôn theo mental model: CLI/Engine/runtime → image và lifecycle → build/storage/network → process, resource và security boundary. Mỗi câu trả lời ưu tiên cơ chế, failure evidence và cách vận hành production.
Architecture và lifecycle
1. CLI, dockerd, containerd và runc?
Call flow Docker CLI gọi Docker Engine API. dockerd quản lý object và policy ở mức Engine; nó phối hợp với containerd cho image/container lifecycle; runtime OCI như runc tạo process container theo OCI bundle/spec.
Khi debug, xác định lỗi nằm ở client/API, daemon, image/snapshot, runtime hay process bên trong thay vì coi “Docker” là một khối duy nhất.
2. Container khác VM?
Container cô lập process bằng kernel primitives và dùng chung host kernel. VM chạy guest OS/kernel riêng trên virtual hardware qua hypervisor.
Vì vậy boundary, startup time, mật độ và operational model khác nhau. Container isolation không nên được hiểu là cùng loại boundary với VM; workload untrusted cần threat model rõ và có thể cần sandbox/VM mạnh hơn.
English: A container is an isolated process with a layered filesystem and resource controls, sharing the host kernel. A virtual machine includes a guest operating system and kernel, so its isolation and operational model are different.
3. Image, container và layer?
Image là template immutable gồm config và các filesystem layers. Container là runtime instance của image: process, namespaces/cgroups, network state và một writable layer riêng.
Writable layer chỉ phù hợp cho state tạm thời. State cần tồn tại qua recreate/upgrade nên đặt ở volume hoặc external storage có lifecycle/backup riêng.
4. docker run thực hiện gì?
docker run về mặt mental model là create + start: resolve/pull image nếu cần, tạo container config/rootfs, áp resource/security/network/mount configuration rồi start process chính (PID 1 trong PID namespace của container).
Failure evidence nên lấy từ docker inspect, container logs, daemon events/logs và host/kernel logs tùy failure window.
5. Tag khác digest?
Tag là tên tham chiếu có thể được cập nhật để trỏ sang image khác; digest là content-addressable identity của manifest/content.
Production promotion nên record và verify digest để biết chính xác artifact nào được deploy. Tag vẫn hữu ích cho human-friendly release channels nhưng không đủ làm immutable identity.
6. Multi-platform image?
Một image index/manifest list có thể trỏ tới nhiều manifest theo OS/architecture; client chọn variant phù hợp platform.
Cross-platform build/run có thể dùng native builders hoặc emulation. Emulation giúp portability nhưng có thể chậm và làm benchmark/build behavior khác native, nên CI cần xác nhận platform thực tế.
7. Writable layer phù hợp DB?
Không nên dùng writable layer như durable database storage: container recreate sẽ tách lifecycle state khỏi instance, copy-on-write có overhead và backup/restore khó kiểm soát.
Dùng volume hoặc storage service phù hợp, định nghĩa rõ ownership, backup, restore test, capacity alert, fsync/durability assumptions và recovery khi host/container mất.
8. Exit 137 nghĩa gì?
137 thường là 128 + 9, tức process kết thúc do SIGKILL. Nó không tự chứng minh OOM.
Phân biệt bằng docker inspect (ví dụ OOMKilled), cgroup/host memory pressure, kernel OOM log, orchestrator events và shutdown timeline. Một stop timeout cũng có thể dẫn tới SIGKILL sau khi graceful period hết.
9. Docker socket nguy hiểm?
Ai điều khiển Docker daemon qua socket thường có thể tạo container với host mounts hoặc quyền mạnh, từ đó đạt mức ảnh hưởng tương đương host root trong nhiều cấu hình.
Không mount /var/run/docker.sock tùy tiện vào app/CI agent; nếu bắt buộc, thu hẹp quyền, đặt proxy/policy, audit API usage và tách trust boundary.
10. Rootless trade-off?
Rootless chạy daemon và container trong user namespace mà không cần daemon root, giảm impact của một số daemon/runtime compromise.
Trade-off phụ thuộc kernel/distro: networking, privileged ports, cgroup delegation, storage drivers và feature compatibility có thể khác rootful. Cần test workload thật, quan sát throughput/latency và document unsupported features trước production.
Build, storage và network
11. COPY . . sớm gây gì?
Build cache của layer phụ thuộc input. Nếu copy toàn source quá sớm, thay đổi nhỏ trong source có thể invalidate layer cài dependencies và mọi layer phía sau.
Thường copy dependency manifests/lockfiles trước, install dependencies, rồi mới copy source. Kết hợp .dockerignore để giảm build context, tránh cache busting và tránh gửi file nhạy cảm không cần thiết vào builder.
12. Dockerfile có multi-stage build không? Tại sao?
Có. Builder stage chứa toolchain như JDK/Maven và tạo artifact; runtime stage chỉ copy artifact cần chạy sang runtime image. Điều này thường giảm image size, build/runtime coupling và attack surface.
Multi-stage không tự làm image “secure”: vẫn cần pin/rebuild base, quản dependency, chạy non-root khi phù hợp, không bake secret vào layer, scan/SBOM/sign theo policy và giữ process/signal behavior đúng.
pom.xml hoặc lockfile trước source tận dụng cache thế nào? Vì sao xóa secret ở layer sau vẫn không an toàn?13. Xóa secret ở layer sau có đủ?
Không. Nếu secret đã được copy hoặc ghi vào một layer, layer cũ vẫn có thể tồn tại trong image history/cache dù layer sau đã rm.
Dùng BuildKit secret mount hoặc secret mechanism phù hợp để secret chỉ tồn tại trong phạm vi build step cần thiết; đồng thời tránh log secret và kiểm soát remote cache/export.
14. Build reproducible thế nào?
Pin base bằng version/digest theo policy, pin dependencies/lockfiles, kiểm soát repository/network input, loại timestamp/randomness không cần thiết và tạo artifact deterministic khi toolchain hỗ trợ.
Record provenance/SBOM và builder version. Trade-off: pin digest tăng reproducibility nhưng không tự nhận security fixes; cần process định kỳ resolve, test và cập nhật digest.
15. Volume khác bind mount?
Volume do Docker quản lý lifecycle/path và thường portable hơn giữa hosts/configurations. Bind mount ánh xạ trực tiếp host path nên coupling với filesystem layout, ownership, SELinux/AppArmor context và permission của host.
Production cần biết ai backup/restore data, UID/GID mapping, capacity và behavior khi mount không tồn tại hoặc host thay đổi.
16. Mount che image data?
Có. Khi mount volume/bind mount vào một path trong container, nội dung image layer ở path đó bị mount che khuất trong runtime view.
Đây là nguyên nhân phổ biến khi “file có trong image nhưng chạy container lại mất”. Kiểm tra Mounts trong inspect và init/copy strategy thay vì rebuild image một cách mù quáng.
17. localhost giữa containers?
localhost/127.0.0.1 trỏ tới network namespace của chính container, không phải container khác hay host.
Giữa services trên user-defined network, gọi bằng service/container DNS name và container port. Cần phân biệt container-to-container traffic với host-published ports để debug đúng path.
18. EXPOSE có publish port?
Không. EXPOSE là metadata/documentation về port mà image dự kiến dùng; nó không tự tạo host forwarding.
Publish ra host bằng runtime option như -p/--publish hoặc Compose ports. Chỉ publish interface/port cần thiết; binding 0.0.0.0 có thể mở service rộng hơn mong muốn.
19. App listen 127.0.0.1?
Nếu app chỉ bind 127.0.0.1 trong container, nó chỉ nhận traffic từ loopback của container; traffic từ bridge/network namespace khác sẽ không tới listener đó.
Server container thường bind 0.0.0.0 hoặc interface phù hợp, rồi dùng network/firewall/publish policy để giới hạn exposure.
20. Compose depends_on có đảm bảo ready?
Không nên hiểu startup order là application readiness. Compose có thể dùng healthcheck + condition để chờ dependency healthy trong startup flow, nhưng dependency vẫn có thể chết/restart sau đó.
Application cần timeout, bounded retry/backoff và idempotent reconnect. Healthcheck cũng phải phản ánh dependency thật thay vì chỉ kiểm tra process còn sống.
Security, process và Nginx
21. Vì sao non-root?
Chạy process bằng user không phải root giảm blast radius khi application bị compromise và giảm khả năng sửa filesystem/host resources khi kết hợp đúng isolation.
Non-root là một lớp: tiếp tục drop capabilities, dùng read-only rootfs khi phù hợp, no-new-privileges, seccomp/AppArmor/SELinux, least-privilege mounts và secrets. Verify app vẫn ghi được đúng temp/data paths.
22. Image nhỏ có chắc secure?
Không. Image nhỏ giảm số package/file và thường giảm attack surface, nhưng security còn phụ thuộc provenance, patch state, dependency vulnerabilities, runtime config, credentials và privilege.
“Không có shell” cũng không thay thế vulnerability management hoặc runtime hardening. Security review phải nhìn cả supply chain và deployment configuration.
23. SBOM/signing giải quyết gì?
SBOM cung cấp inventory thành phần để truy vết dependency/vulnerability/compliance. Signing/attestation giúp policy xác minh identity, provenance hoặc build claims của artifact.
Chúng không chứng minh artifact không có bug hay không thể bị khai thác. Cần trust root/key management, verification ở deployment và revocation/rotation process.
24. Shell-form ENTRYPOINT lỗi gì?
Shell form thường chạy qua /bin/sh -c, khiến shell trở thành PID 1 và có thể làm signal forwarding/argument handling khác mong muốn. Exec form chạy executable trực tiếp và ghép CMD arguments rõ ràng hơn.
Với Java/service cần graceful shutdown, verify SIGTERM thực sự tới application và wrapper script dùng exec khi cần.
25. PID 1 có trách nhiệm gì?
Main process của container nhận signals liên quan lifecycle và cần xử lý child processes mà nó tạo. Một PID 1 không reap zombie đúng có thể tích lũy process table entries.
Nếu app không làm init behavior tốt, dùng wrapper đúng cách hoặc runtime --init/init process phù hợp; không thêm process manager chỉ để che lỗi lifecycle mà không hiểu signal path.
26. Graceful shutdown flow?
Flow mong muốn: nhận SIGTERM → đánh dấu not-ready/stop nhận traffic mới → drain request/job đang chạy trong thời gian bounded → flush/checkpoint cần thiết → đóng dependency → exit trước khi stop grace period hết.
Test failure window với request dài, deploy rolling và dependency chậm. Nếu vượt timeout, runtime/orchestrator có thể SIGKILL, nên shutdown deadline phải nhỏ hơn platform grace period và có observability cho forced termination.
27. Memory limit với JVM?
Cgroup memory limit áp lên tổng memory accounting của container chứ không chỉ Java heap: heap, metaspace, code cache, thread stacks, direct/native buffers và các phần page cache/accounting liên quan đều cần headroom.
Đừng đặt -Xmx sát limit. Theo dõi RSS/cgroup usage, GC, native memory, thread count, OOM events và restart rate; capacity test với burst/load thật.
28. Nginx 502 debug?
502 thường nghĩa proxy không nhận được response hợp lệ từ upstream. Kiểm tra theo path: upstream DNS/resolution → network reachability → app listener/bind → port → HTTP/TLS protocol → timeout/reset → Nginx error log và upstream timing.
Phân biệt 502 với 504 và app-level 5xx. Sau incident, giữ evidence về connect error, reset, latency và upstream health thay vì chỉ restart.
29. SPA try_files caveat?
Fallback route về index.html giúp client-side router xử lý deep links, nhưng không nên biến mọi asset/API 404 thành HTML 200.
Tách location cho API/static assets; cache index.html ngắn/no-cache theo deployment strategy và cache hashed assets dài/immutable để tránh client chạy HTML mới với JS cũ hoặc ngược lại.
30. Container works root nhưng fail non-root?
Kiểm tra ownership/mode của files và directories, UID/GID của bind mount/volume, writable temp/cache/log dirs, executable bit, socket/device access, port dưới 1024 và capabilities.
Fix permission tại image/build/deploy boundary thay vì quay lại root. Với shared storage, document numeric UID/GID contract và test recreate trên host/node khác.