Chương 45. Docker và container

Ở các chương trước, ta đã nói về production, reverse proxy, load balancer, CDN, firewall.

Chương này nói về một công cụ gần như ai làm backend hiện đại cũng gặp:

Docker.
Container.

Docker rất hữu ích.

Nó giúp đóng gói app và môi trường chạy vào một đơn vị dễ chạy hơn.

Nhưng cũng có hiểu nhầm rất phổ biến:

Dùng Docker là app tự production-ready.
Dùng container là app tự scale.
Dùng container là hết lỗi deploy.

Không đúng.

Thông điệp chính của chương:

> Container giúp đóng gói và chạy app nhất quán hơn. Nhưng container không tự giải quyết scale, dữ liệu bền vững, bảo mật, monitoring, migration, backup, hay deploy an toàn.

---

45.1. Ví dụ dễ hiểu: hộp cơm đóng gói

Bạn nấu một món ăn.

Nếu chỉ nói:

Món này chạy được trong bếp nhà tôi.

người khác có thể gặp vấn đề:

  • Thiếu nguyên liệu.
  • Bếp khác loại.
  • Nồi khác.
  • Gia vị khác.
  • Thứ tự nấu không rõ.

Nếu bạn đóng gói thành một hộp cơm chuẩn:

Có món ăn.
Có hướng dẫn hâm.
Có khẩu phần rõ.

người khác dễ dùng hơn.

Container cũng vậy.

Nó đóng gói:

  • App code.
  • Runtime.
  • Dependency.
  • System package cần thiết.
  • Command để chạy.

Nhờ vậy app chạy giống nhau hơn giữa máy dev, CI, staging, production.

---

45.2. Container giải quyết vấn đề gì?

Container giải quyết vấn đề rất quen:

Máy tôi chạy được, máy bạn không chạy.

Vì sao xảy ra?

  • Python/Node/Java version khác.
  • Thiếu system library.
  • Environment variable khác.
  • Package version khác.
  • OS khác.
  • Command chạy khác.
  • File path khác.

Container giúp định nghĩa môi trường chạy rõ hơn.

Ví dụ:

App này chạy bằng Python 3.12.
Cài dependency từ requirements.txt.
Expose port 8000.
Chạy command uvicorn app:app.

Không phải hoàn hảo tuyệt đối.

Nhưng tốt hơn rất nhiều so với hướng dẫn miệng.

---

45.3. Container là gì?

Container là một process chạy trong môi trường được cô lập tương đối.

Nó có:

  • Filesystem riêng.
  • Process namespace.
  • Network namespace.
  • Environment variables.
  • Resource limit nếu cấu hình.

Nói đơn giản:

Container là cách chạy app trong một hộp riêng, dựa trên image.

Container không phải máy ảo đầy đủ.

Nó nhẹ hơn VM vì chia sẻ kernel với host.

Điều này làm container:

  • Start nhanh.
  • Dễ đóng gói.
  • Dễ nhân bản.
  • Phù hợp deploy nhiều instance.

---

45.4. Image là gì?

Image là bản đóng gói bất biến để tạo container.

Ví dụ:

synvia-api:1.0.0

Image giống khuôn/bản mẫu.

Container là instance chạy từ image.

Ví dụ:

Image:
  synvia-api:1.0.0

Containers:
  synvia-api-container-1
  synvia-api-container-2
  synvia-api-container-3

Cùng một image có thể chạy nhiều container.

Trong deploy production, ta thường build image, push lên registry, rồi server/platform kéo image đó để chạy.

---

45.5. Dockerfile là gì?

Dockerfile là file mô tả cách build image.

Ví dụ tư duy:

Bắt đầu từ Python image.
Copy requirements.
Cài dependency.
Copy source code.
Chạy app bằng command này.

Dockerfile biến môi trường chạy thành tài liệu có thể thực thi.

Thay vì nói:

Cài mấy thứ này rồi chạy thử xem.

ta viết:

Đây là các bước build image.

Dockerfile tốt giúp onboarding và deploy dễ hơn.

Dockerfile xấu có thể làm image nặng, build chậm, lộ secret, hoặc khó debug.

---

45.6. Container không phải VM

VM có hệ điều hành riêng đầy đủ hơn.

Container chia sẻ kernel với host.

Nói đơn giản:

VM giống một căn hộ riêng đầy đủ.
Container giống một phòng riêng trong cùng tòa nhà, có tường ngăn nhưng dùng chung nền móng.

Container nhẹ hơn và start nhanh hơn.

Nhưng isolation không giống VM hoàn toàn.

Với workload không tin cậy cao, ví dụ chạy code user upload trong AI Judge, cần sandbox mạnh hơn và cấu hình container rất cẩn thận.

Không nên nghĩ:

Chạy trong Docker là tuyệt đối an toàn.

---

45.7. Container chạy một process chính

Một container thường nên chạy một process chính.

Ví dụ:

api container:
  chạy web server

worker container:
  chạy worker

scheduler container:
  chạy scheduler

Không nên nhét mọi thứ vào một container:

Nginx + API + worker + database + cron

trừ khi chỉ là demo/local.

Tách process giúp:

  • Scale riêng.
  • Restart riêng.
  • Log rõ.
  • Health check rõ.
  • Resource limit rõ.

Ví dụ AI Judge:

api image có thể dùng cùng codebase với worker,
nhưng api container chạy command web,
worker container chạy command worker.

---

45.8. Container là ephemeral

Ephemeral nghĩa là có thể bị xóa/thay bất cứ lúc nào.

Trong production, container có thể bị:

  • Restart.
  • Kill.
  • Replace khi deploy.
  • Move sang node khác.
  • Scale down.

Vì vậy không nên lưu dữ liệu quan trọng trong filesystem container.

Ví dụ xấu:

User upload file vào /app/uploads trong container.
Container bị replace.
File mất.

Đúng hơn:

File user upload -> object storage.
Database -> managed database/volume bền vững.
Job -> queue.
Session -> Redis/database nếu cần.

Container chết không nên làm mất dữ liệu nghiệp vụ.

---

45.9. Volume là gì?

Volume là cách gắn storage bên ngoài vào container.

Ví dụ:

Container
-> volume

Volume giúp dữ liệu sống lâu hơn container.

Hay dùng cho:

  • Database local dev.
  • File cần persist trong một host.
  • Development bind mount.

Nhưng production web app hiện đại thường tránh phụ thuộc vào local volume cho file user quan trọng nếu cần scale nhiều server.

Vì volume local gắn với một máy.

Nếu request đi sang máy khác, file không có.

Với file user upload:

Object storage thường tốt hơn.

---

45.10. Bind mount là gì?

Bind mount là gắn thư mục host vào container.

Rất hữu ích ở local dev.

Ví dụ:

Code trên máy bạn
-> mount vào container
-> sửa code là container thấy

Nhưng production nên cẩn thận.

Production image nên chứa code đã build.

Không nên phụ thuộc vào bind mount code từ host lung tung.

Local dev cần nhanh.

Production cần reproducible và kiểm soát.

Hai mục tiêu khác nhau.

---

45.11. Docker network là gì?

Docker network cho phép container nói chuyện với nhau.

Ví dụ Docker Compose:

api -> db
api -> redis
worker -> redis
worker -> db

Service có thể gọi nhau bằng tên:

postgres://db:5432
redis://redis:6379

Điều này rất tiện cho local/dev/staging nhỏ.

Nhưng production network vẫn cần:

  • Firewall/security group.
  • Private/public boundary.
  • Không publish port nội bộ bừa.
  • Service auth nếu cần.

Docker network không tự động làm hệ thống an toàn.

---

45.12. Port expose và publish

Trong Docker có khác biệt giữa:

App lắng nghe port trong container.

và:

Port đó được publish ra host/internet.

Ví dụ:

api container listen 8000.
Host publish 80/443 qua reverse proxy.

Không nên publish mọi port ra ngoài.

Ví dụ production không nên:

PostgreSQL 5432 publish public.
Redis 6379 publish public.
Worker admin port publish public.

Chỉ public đúng entrypoint cần thiết.

Các service nội bộ dùng network nội bộ.

---

45.13. Docker Compose dùng khi nào?

Docker Compose giúp chạy nhiều container cùng nhau.

Ví dụ local:

api
worker
postgres
redis
mailhog

Một file compose giúp developer chạy cả stack:

docker compose up

Compose rất tốt cho:

  • Local dev.
  • Demo.
  • Integration test.
  • Staging nhỏ.
  • Single-server deployment đơn giản nếu team hiểu giới hạn.

Compose không phải orchestrator production lớn.

Nó không tự giải quyết:

  • Multi-node scheduling.
  • Auto healing phức tạp.
  • Rolling deploy đầy đủ.
  • Autoscaling.
  • Service discovery phức tạp.

Nhưng với app nhỏ, Compose có thể rất thực dụng.

---

45.14. Container không làm app tự scale

Dùng container không có nghĩa app tự scale.

Nếu bạn chạy:

1 container API

thì vẫn là một instance.

Muốn scale, cần chạy nhiều container:

api-1
api-2
api-3

và có load balancer phía trước.

Muốn scale worker, cần chạy nhiều worker container hoặc tăng concurrency.

Nhưng nếu bottleneck là database hoặc AI API quota, thêm container chưa chắc giúp.

Container là đơn vị đóng gói/chạy.

Scale là bài toán orchestration và bottleneck.

---

45.15. Container không thay thế load balancer

Nếu có nhiều API container, user cần một cửa chung.

Ví dụ:

Browser
-> Load balancer/reverse proxy
-> api container 1/2/3

Container không tự chia traffic.

Bạn cần:

  • Nginx/HAProxy.
  • Cloud load balancer.
  • Platform router.
  • Kubernetes Service/Ingress.

Nếu chỉ chạy nhiều container trên nhiều port mà không có routing rõ, hệ thống khó dùng và khó deploy.

---

45.16. Container không thay thế process manager/orchestrator

Container process cũng có thể crash.

Cần ai đó restart nó.

Ở mức đơn giản:

  • Docker restart policy.
  • systemd chạy Docker container.
  • Docker Compose restart policy.

Ở mức lớn hơn:

  • Kubernetes.
  • Nomad.
  • ECS.
  • Cloud Run.
  • Fly.io/Render/Railway/Heroku style platform.

Orchestrator trả lời:

Container chạy ở đâu?
Nếu chết thì restart thế nào?
Scale thế nào?
Deploy version mới thế nào?
Health check thế nào?

Docker chỉ là một phần.

---

45.17. Image tag

Image tag là nhãn của image.

Ví dụ:

synvia-api:latest
synvia-api:1.2.3
synvia-api:git-abc123

Production không nên phụ thuộc mơ hồ vào latest.

latest có thể trỏ đến image khác theo thời gian.

Tốt hơn:

Deploy một tag cụ thể hoặc digest cụ thể.

Ví dụ:

synvia-api:2026-05-12-abc123

Khi incident xảy ra, bạn cần biết:

Version nào đang chạy?
Image nào đang chạy?
Rollback về image nào?

Tag rõ giúp deploy đáng tin hơn.

---

45.18. Image registry

Image registry là nơi lưu image.

Ví dụ:

  • Docker Hub.
  • GitHub Container Registry.
  • AWS ECR.
  • Google Artifact Registry.
  • Azure Container Registry.
  • GitLab Container Registry.

Luồng production:

CI build image
-> push registry
-> server/platform pull image
-> run container

Registry cần:

  • Quyền truy cập.
  • Scan security nếu có.
  • Retention policy.
  • Không public image private bừa.
  • Tag rõ.

Image registry là một phần của supply chain.

---

45.19. Build image nhỏ và sạch

Image quá nặng gây:

  • Pull chậm.
  • Deploy chậm.
  • Tốn storage.
  • Tăng bề mặt lỗ hổng.
  • CI chậm.

Cách cải thiện:

  • Dùng base image phù hợp.
  • Multi-stage build.
  • Không copy file không cần.
  • Dùng .dockerignore.
  • Không cài tool dev trong runtime image nếu không cần.
  • Xóa cache package nếu phù hợp.

Ví dụ:

Node build stage:
  npm install + build assets

Runtime stage:
  chỉ copy build output và dependency runtime

Image càng nhỏ và rõ, production càng dễ vận hành.

---

45.20. .dockerignore

.dockerignore giúp loại file khỏi build context.

Không nên gửi vào Docker build:

  • .git
  • node_modules local nếu không cần.
  • .env
  • file secret.
  • test output lớn.
  • cache.
  • local upload.
  • log.

Nếu build context quá lớn, build chậm.

Nếu lỡ copy .env hoặc secret vào image, rất nguy hiểm.

.dockerignore nhỏ nhưng cực kỳ thực tế.

---

45.21. Không đưa secret vào image

Không nên bake secret vào image.

Ví dụ xấu:

ENV GEMINI_API_KEY=...
COPY .env /app/.env

Image có thể được push lên registry.

Ai có image có thể lấy secret.

Secret production nên được inject lúc chạy:

  • Environment variable từ secret manager.
  • Mounted secret.
  • Cloud workload identity.
  • Platform secret.

Image nên dùng được cho nhiều môi trường.

Config/secret khác nhau theo môi trường, không nằm cứng trong image.

---

45.22. Environment variables

Container thường nhận config qua environment variables.

Ví dụ:

DATABASE_URL
REDIS_URL
OBJECT_STORAGE_BUCKET
AI_PROVIDER
LOG_LEVEL

Điều này tốt vì image không cần rebuild cho mỗi môi trường.

Nhưng env var cũng có rủi ro:

  • Có thể bị log.
  • Có thể bị lộ trong process inspection.
  • Có thể bị cấu hình sai.
  • Không thay secret manager hoàn toàn.

Tư duy:

Config bình thường dùng env.
Secret nhạy cảm nên được quản lý bởi secret mechanism của platform.

Tùy nền tảng, secret vẫn có thể được đưa vào env lúc runtime.

Điểm chính là không hardcode vào image/code.

---

45.23. Log trong container

Container thường nên log ra stdout/stderr.

Platform sẽ thu log.

Không nên chỉ ghi log vào file trong container rồi quên collect.

Vì container có thể bị xóa.

Log file local có thể mất.

Pattern:

App logs -> stdout/stderr
Container runtime -> log collector
Log system -> query/alert

Log vẫn phải tránh secret/token/dữ liệu nhạy cảm.

Container không làm log an toàn hơn nếu app log sai.

---

45.24. Health check trong container

Container/platform cần biết app có khỏe không.

Có thể dùng:

/health
/ready

Hoặc command healthcheck.

Health check giúp:

  • Restart container lỗi.
  • Không gửi traffic đến container chưa ready.
  • Rolling deploy an toàn hơn.

Nhưng health check phải đúng.

Nếu quá đơn giản:

Process còn sống nhưng app không dùng được.

Nếu quá nặng:

Health check gây tải hoặc false alarm.

Vừa đủ là tốt.

---

45.25. Resource limit

Container có thể bị giới hạn CPU/RAM.

Nếu không giới hạn, một container lỗi có thể ăn quá nhiều tài nguyên host.

Nếu giới hạn quá thấp, app bị kill hoặc chậm.

Các khái niệm:

CPU limit
Memory limit
Memory request nếu dùng orchestrator

Ví dụ AI Judge:

API container cần RAM vừa phải.
Worker xử lý file/code có thể cần limit riêng.
Sandbox chạy code user càng cần limit chặt.

Resource limit là một phần của production safety.

---

45.26. Container và database

Chạy database trong container rất tiện cho local.

Ví dụ:

postgres container
redis container

Nhưng production database cần cẩn thận:

  • Persistent volume.
  • Backup.
  • Restore test.
  • Upgrade.
  • Monitoring.
  • Disk I/O.
  • Replication.
  • Security.

Với team nhỏ, managed database thường thực dụng hơn tự chạy database container.

Không phải vì container không chạy được database.

Mà vì vận hành database production khó hơn chạy process.

---

45.27. Container và file upload

Không lưu file upload quan trọng trong container filesystem.

Ví dụ xấu:

/app/uploads/submission.zip

Nếu container restart, scale nhiều instance, hoặc deploy mới, file có thể mất/không thấy.

Đúng hơn:

User upload -> object storage
Database lưu metadata
Worker đọc file từ object storage

Nếu cần volume cho file tạm, phải hiểu:

  • File tạm có thể mất không?
  • Cleanup thế nào?
  • Disk limit thế nào?
  • Nhiều worker có cần thấy cùng file không?

File user quan trọng nên rời khỏi container local storage.

---

45.28. Container và worker

Worker rất hợp để chạy trong container.

Ví dụ:

api container:
  command: start web app

worker container:
  command: start celery worker

scheduler container:
  command: start celery beat / scheduler

Scale worker bằng cách:

Tăng số worker container.
Hoặc tăng concurrency mỗi worker.

Nhưng cần tính:

  • Queue length.
  • External API quota.
  • Database connection.
  • Memory/CPU per job.
  • Retry/backoff.

Không phải cứ thêm worker container là tốt.

Nếu AI provider giới hạn 100 RPM, chạy quá nhiều worker có thể bị rate limit.

---

45.29. Container và migration

Database migration trong container cần quy trình rõ.

Không nên để mọi app container cùng tự chạy migration khi start nếu có nhiều instance.

Ví dụ:

api-1 start -> run migration
api-2 start -> run migration
api-3 start -> run migration

có thể race hoặc lock.

Tốt hơn:

  • Migration là một job riêng trong deploy pipeline.
  • Hoặc chỉ một instance chạy migration có lock rõ.
  • Migration backward-compatible.
  • Có backup/rollback plan cho migration nguy hiểm.

Container không tự làm migration an toàn.

CI/CD chương sau sẽ nói kỹ hơn.

---

45.30. Container startup order không đủ

Trong Docker Compose có thể khai báo dependency.

Nhưng:

db container started

không có nghĩa:

database ready nhận connection.

App cần retry connection lúc start.

Production cũng vậy.

Dependency có thể chậm hơn app:

  • Database đang failover.
  • Redis chưa ready.
  • Queue đang reconnect.

App nên xử lý dependency chưa sẵn sàng bằng retry/backoff, readiness check.

Không nên chỉ phụ thuộc thứ tự start.

---

45.31. Container security cơ bản

Một số nguyên tắc:

  • Không chạy root nếu không cần.
  • Image nhỏ, ít package thừa.
  • Không chứa secret.
  • Update base image.
  • Scan vulnerability.
  • Set resource limit.
  • Read-only filesystem nếu phù hợp.
  • Không mount Docker socket vào container nếu không hiểu rủi ro.
  • Chỉ publish port cần thiết.
  • Drop capability nếu cần.

Container không phải sandbox tuyệt đối.

Chạy root trong container với mount nguy hiểm vẫn có thể rất rủi ro.

---

45.32. Docker socket rất nguy hiểm

Docker socket thường là:

/var/run/docker.sock

Nếu container có quyền truy cập Docker socket, nó có thể điều khiển Docker daemon.

Điều này gần như quyền rất cao trên host.

Không nên mount Docker socket vào app container bình thường.

Một số CI/build tool cần dùng, nhưng phải hiểu rủi ro.

Với app production:

Không mount Docker socket nếu không có lý do rất rõ.

---

45.33. Multi-stage build

Multi-stage build giúp tách build environment và runtime environment.

Ví dụ:

Stage build:
  có compiler, dev dependency, build tool.

Stage runtime:
  chỉ có app đã build và dependency cần chạy.

Lợi ích:

  • Image runtime nhỏ hơn.
  • Ít công cụ thừa.
  • Ít bề mặt lỗ hổng.
  • Build rõ ràng hơn.

Rất hữu ích với:

  • Node frontend.
  • Go.
  • Java.
  • Rust.
  • Python cần compile dependency.

Không bắt buộc ngay từ đầu, nhưng nên biết.

---

45.34. Container và local development

Container rất tốt cho local dev khi app có nhiều dependency.

Ví dụ:

PostgreSQL
Redis
Celery worker
API
Object storage giả lập
Mailhog

Compose giúp cả team chạy giống nhau hơn.

Nhưng local container cũng có thể chậm hoặc khó debug nếu cấu hình kém.

Tùy team, có thể:

  • Chạy database/redis bằng Docker.
  • Chạy app trực tiếp trên máy.
  • Hoặc chạy toàn bộ bằng Docker.

Không có một cách duy nhất.

Mục tiêu là onboarding dễ và môi trường nhất quán.

---

45.35. Container và CI/CD

Container rất hợp với CI/CD.

Pipeline có thể:

1. Checkout code.
2. Run tests.
3. Build image.
4. Scan image.
5. Push image to registry.
6. Deploy image tag to staging/production.

Lợi ích:

Thứ được test gần với thứ được deploy.

Nếu build artifact là image, deploy chỉ cần chạy đúng image tag.

Chương sau sẽ nói kỹ hơn về CI/CD.

---

45.36. Container và Kubernetes

Kubernetes chạy container, nhưng không chỉ là Docker.

Kubernetes giải quyết:

  • Scheduling.
  • Restart.
  • Service discovery.
  • Rolling deploy.
  • Scaling.
  • Config/secret.
  • Health check.
  • Volume.
  • Network.

Nhưng Kubernetes cũng phức tạp.

Trước khi học Kubernetes, phải hiểu container.

Nếu chưa hiểu image/container/volume/network, Kubernetes sẽ giống một đống thuật ngữ khó chịu.

Chương 47 sẽ nói kỹ hơn.

---

45.37. AI Judge: container hóa thế nào?

AI Judge có thể có các container:

api
worker
scheduler
frontend
postgres local/dev
redis local/dev

Production có thể là:

api image:
  chạy FastAPI/Django API

worker image:
  chạy Celery/RQ/worker

scheduler image:
  chạy periodic tasks

frontend image/static build:
  serve qua CDN/reverse proxy

Database production nên cân nhắc managed PostgreSQL.

File production nên dùng object storage.

Queue production có thể là Redis/RabbitMQ/SQS/Pub/Sub tùy kiến trúc.

Không cần nhét mọi thứ vào một container.

---

45.38. AI Judge: worker và sandbox

Nếu AI Judge chạy code user, container có thể là một phần của sandbox.

Nhưng cần cẩn thận:

Code user không tin cậy.

Sandbox container nên:

  • Không có production secret.
  • Không có database credential.
  • Không có Docker socket.
  • Network disabled hoặc giới hạn.
  • CPU/memory/time limit.
  • Filesystem tạm.
  • User non-root.
  • Cleanup sau chạy.

Đừng chạy code user trong cùng container worker đang có quyền gọi database/object storage/AI provider.

Worker điều phối.

Sandbox chạy code không tin cậy.

Hai vai trò khác nhau.

---

45.39. AI Judge: Docker Compose local

Local Compose cho AI Judge có thể gồm:

api
worker
redis
postgres
mailhog
minio nếu muốn giả lập object storage

Điều này giúp developer mới chạy hệ thống nhanh.

Nhưng production không nhất thiết giống Compose local 100%.

Production có thể dùng:

  • Managed database.
  • Managed object storage.
  • Managed queue.
  • Cloud load balancer.
  • Container platform.

Compose local là môi trường phát triển.

Production là môi trường vận hành.

Đừng trộn kỳ vọng.

---

45.40. Những lỗi thường gặp khi đưa container lên production

Lỗi 1:

Lưu file upload trong container.

Lỗi 2:

Bake secret vào image.

Lỗi 3:

Dùng tag latest nên không biết version nào đang chạy.

Lỗi 4:

Không có health check.

Lỗi 5:

Container chạy root không cần thiết.

Lỗi 6:

Không set resource limit, một container ăn hết RAM.

Lỗi 7:

Publish database/Redis port public.

Lỗi 8:

Mọi app container tự chạy migration cùng lúc.

Lỗi 9:

Log vào file trong container và không collect.

Lỗi 10:

Nghĩ Docker Compose local là đủ cho mọi production scale.

---

45.41. Checklist container production

Trước khi đưa container lên production, hỏi:

  • Image có tag rõ không?
  • Image có chứa secret không?
  • .dockerignore có loại file nhạy cảm không?
  • Container có chạy non-root được không?
  • Port nào được publish?
  • File upload có ra object storage không?
  • Log có ra stdout/stderr không?
  • Health check/readiness có không?
  • Resource limit có không?
  • Config/secret inject thế nào?
  • Database migration chạy ở đâu?
  • Nếu container restart thì mất gì?
  • Nếu scale nhiều instance thì session/file/job có còn đúng không?
  • Image có được scan vulnerability không?
  • Base image có được update không?
  • Rollback image version thế nào?

Checklist này giúp tránh những lỗi rất đời.

---

45.42. Bảng chọn nhanh

| Nhu cầu | Cách nghĩ thường hợp | |---|---| | Dev chạy stack nhanh | Docker Compose | | Đóng gói app nhất quán | Docker image | | Chạy nhiều API instance | Nhiều container + load balancer/orchestrator | | Chạy worker riêng | Worker container command riêng | | Lưu file user | Object storage, không phải container filesystem | | Lưu database production | Managed DB hoặc volume/backup rất rõ | | Secret production | Secret manager/platform secret, không bake vào image | | Deploy version rõ | Image tag/digest cụ thể | | App tự restart khi crash | Restart policy/orchestrator | | Scale tự động/phức tạp | Orchestrator như ECS/Kubernetes/Cloud Run/etc |

---

45.43. Tóm tắt bằng AI Judge

Với AI Judge, container giúp:

Đóng gói API.
Đóng gói worker.
Đóng gói scheduler.
Chạy local stack bằng Compose.
Build image trong CI/CD.
Deploy cùng một artifact lên staging/production.

Nhưng container không tự giải quyết:

Worker concurrency đúng bao nhiêu.
Gemini/API quota.
Database backup.
Object storage.
Queue durability.
Permission.
Monitoring.
Safe migration.
Scale ngang.

Thiết kế tốt:

API container stateless.
Worker container xử lý job.
File ở object storage.
Job ở queue.
Database ở nơi bền vững.
Secret inject lúc runtime.
Log ra stdout.
Health check rõ.
Image tag rõ.

Nếu chạy code user:

Sandbox riêng, giới hạn tài nguyên, không có secret rộng.

---

45.44. Kết luận của chương

Docker/container là công cụ rất thực dụng.

Nó giúp đóng gói app và môi trường chạy, giảm chuyện "máy tôi chạy được", làm local dev/CI/deploy nhất quán hơn.

Nhưng container chỉ là đơn vị đóng gói và chạy process.

Nó không tự làm app production-ready.

Thông điệp cần nhớ:

> Container làm app dễ chạy nhất quán hơn. Production vẫn cần load balancer, storage bền vững, secret management, health check, logging, backup, monitoring, migration an toàn và orchestration phù hợp.

Ở chương tiếp theo, ta sẽ nói về CI/CD: vì sao deploy tay dễ lỗi, pipeline build/test/deploy nên hoạt động thế nào, rolling/blue-green/canary deploy khác nhau ra sao, rollback cần chuẩn bị gì, và database migration trong lúc deploy nên được nghĩ thế nào.