Skip to content

Buổi 7: Docker trong Production

🎯 Mục tiêu

  • Biết cách push image lên Docker Hub và private registry
  • Cấu hình health checks cho containers
  • Thiết lập restart policies đúng cách
  • Giới hạn resource (CPU, Memory) cho containers
  • Áp dụng security best practices

1. Docker Registry

Docker Hub

bash
# Đăng nhập Docker Hub
$ docker login
Username: myusername
Password: ********
Login Succeeded

# Tag image theo format: username/image:tag
$ docker tag my-app:latest myusername/my-app:1.0
$ docker tag my-app:latest myusername/my-app:latest

# Push lên Docker Hub
$ docker push myusername/my-app:1.0
$ docker push myusername/my-app:latest

# Pull về máy khác
$ docker pull myusername/my-app:1.0

Luồng CI/CD với Registry

  Developer          CI/CD              Registry           Production
     │                 │                   │                    │
     │  git push       │                   │                    │
     │────────────────▶│                   │                    │
     │                 │  docker build     │                    │
     │                 │  docker push      │                    │
     │                 │──────────────────▶│                    │
     │                 │                   │   docker pull      │
     │                 │                   │◀───────────────────│
     │                 │                   │   docker run       │
     │                 │                   │───────────────────▶│

Private Registry

bash
# Chạy registry riêng
$ docker run -d -p 5000:5000 --name registry registry:2

# Tag cho private registry
$ docker tag my-app:latest localhost:5000/my-app:1.0

# Push
$ docker push localhost:5000/my-app:1.0

# Pull
$ docker pull localhost:5000/my-app:1.0

So sánh các Registry phổ biến

RegistryMiễn phíPrivate reposUse case
Docker Hub✅ 1 privateTrả phíPublic images
GitHub Container RegistryGitHub projects
GitLab RegistryGitLab CI/CD
AWS ECRTrả phíAWS ecosystem
Google Artifact RegistryTrả phíGCP ecosystem
Self-hostedFull control

2. Health Checks

Tại sao cần Health Check?

      Không có Health Check          Có Health Check
┌───────────────────────┐     ┌───────────────────────┐
│ Container: Running ✅  │     │ Container: Running ✅  │
│                       │     │ Health: Healthy ✅     │
│ Nhưng app bên trong   │     │                       │
│ đã crash/hang! 😱     │     │ App crash → Unhealthy │
│                       │     │ → Auto restart ♻️     │
│ Không ai biết...      │     │ → Alert team 🔔       │
└───────────────────────┘     └───────────────────────┘

Health Check trong Dockerfile

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev

# Health check mỗi 30s
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

EXPOSE 3000
CMD ["node", "server.js"]

Health Check trong docker-compose.yml

yaml
services:
  api:
    build: ./api
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 30s
      timeout: 10s
      start_period: 10s
      retries: 3

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

Các trạng thái Health

Trạng tháiÝ nghĩa
startingĐang trong start_period, chưa check
healthyHealth check thành công
unhealthyHealth check thất bại liên tục (retries)
bash
# Kiểm tra health status
$ docker inspect --format='{{.State.Health.Status}}' api
healthy

# Xem health check logs
$ docker inspect --format='{{json .State.Health}}' api | jq

3. Restart Policies

Các chính sách restart

yaml
services:
  api:
    restart: unless-stopped    # Khuyến nghị cho production
PolicyRestart khi crashRestart khi Docker restartRestart khi stop thủ công
no
on-failure
always
unless-stopped

Ví dụ

bash
# Docker CLI
$ docker run -d --restart unless-stopped --name api my-api

# Kiểm tra restart count
$ docker inspect -f '{{.RestartCount}}' api

💡 Khuyến nghị

Dùng unless-stopped cho production. Container tự restart khi crash hoặc khi Docker daemon restart, nhưng không restart nếu bạn docker stop thủ công.


4. Resource Limits

Giới hạn Memory

bash
# CLI
$ docker run -d --memory=512m --memory-swap=1g --name api my-api

# Compose
yaml
services:
  api:
    image: my-api
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
        reservations:
          memory: 256M
          cpus: '0.5'

Giới hạn CPU

bash
# Giới hạn 1.5 CPU cores
$ docker run -d --cpus=1.5 --name api my-api

# Giới hạn CPU shares (relative weight)
$ docker run -d --cpu-shares=512 --name api my-api

Bảng khuyến nghị resources

ServiceMemoryCPUGhi chú
Node.js API256M–512M0.5–1.0Tuỳ workload
PostgreSQL512M–2G1.0–2.0Cần RAM cho cache
Redis128M–512M0.25–0.5In-memory store
Nginx64M–128M0.25Rất nhẹ
MongoDB512M–2G1.0–2.0Cần RAM

Theo dõi tài nguyên

bash
# Real-time monitoring
$ docker stats

# Output:
CONTAINER ID   NAME   CPU %   MEM USAGE / LIMIT   MEM %   NET I/O
a1b2c3d4e5f6   api    2.5%    128MiB / 512MiB     25%     1.2MB / 648KB
b2c3d4e5f6a7   db     1.0%    256MiB / 1GiB       25%     500KB / 200KB
c3d4e5f6a7b8   redis  0.1%    8MiB / 128MiB       6.25%   100KB / 50KB

5. Security Best Practices

1. Không chạy với root

dockerfile
# ❌ BAD: Chạy với root (mặc định)
FROM node:20-alpine
WORKDIR /app
COPY . .
CMD ["node", "server.js"]

# ✅ GOOD: Chạy với user non-root
FROM node:20-alpine
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]

2. Dùng image chính thức và pinned version

dockerfile
# ❌ BAD
FROM node:latest
FROM myregistry/custom-node

# ✅ GOOD
FROM node:20.11.1-alpine3.19
FROM python:3.12.1-slim-bookworm

3. Scan vulnerabilities

bash
# Docker Scout (tích hợp sẵn)
$ docker scout cves my-app:latest

# Trivy (open-source)
$ docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image my-app:latest

4. Read-only filesystem

bash
# Container chỉ đọc
$ docker run -d --read-only --tmpfs /tmp my-app

# Compose
yaml
services:
  api:
    image: my-api
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

5. Không mount Docker socket

bash
# ❌ NGUY HIỂM – cho phép container kiểm soát Docker host
$ docker run -v /var/run/docker.sock:/var/run/docker.sock my-app

Checklist security

#Kiểm traTrạng thái
1User non-root
2Pinned image version
3Scan vulnerabilities
4.dockerignore (không leak secrets)
5Không hardcode secrets
6Read-only filesystem
7Resource limits
8Health checks
9Không mount Docker socket
10Dùng COPY thay vì ADD

6. Production docker-compose.yml mẫu

yaml
services:
  api:
    image: myregistry/api:${VERSION:-latest}
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DATABASE_URL: postgres://app:${DB_PASS}@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
    read_only: true
    tmpfs:
      - /tmp
    user: "1000:1000"

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASS}
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'

volumes:
  pgdata:

🏋️ Bài tập thực hành

Bài 1: Push image lên Docker Hub

  1. Tạo tài khoản Docker Hub (nếu chưa có)
  2. Build image từ Buổi 2
  3. Tag image: docker tag myapp username/myapp:1.0
  4. Push: docker push username/myapp:1.0
  5. Pull về kiểm tra: docker pull username/myapp:1.0

Bài 2: Health Checks

  1. Viết endpoint /health cho Node.js app
  2. Thêm HEALTHCHECK vào Dockerfile
  3. Build và chạy
  4. Kiểm tra: docker inspect --format='{{.State.Health.Status}}' app

Bài 3: Resource Limits

  1. Chạy container với --memory=128m
  2. Thử stress test: docker exec app node -e "let a=[]; while(true) a.push(new Array(1e6))"
  3. Xem container bị kill (OOMKilled)
  4. Kiểm tra: docker inspect -f '{{.State.OOMKilled}}' app

Bài 4: Security audit

  1. Lấy Dockerfile đã viết
  2. Áp dụng checklist security ở mục 5
  3. Scan image bằng docker scout cves
  4. Fix tất cả issues tìm được