Skip to content

🐳 Docker Knowledge Base - Q&A Notes

📋 Table of Contents


Multi-stage Build

❓ Multi-stage build là gì và tại sao sử dụng?

💡 Trả lời:

  • Multi-stage build = sử dụng nhiều FROM trong 1 Dockerfile
  • Mỗi stage là 1 container riêng biệt, chỉ copy những gì cần thiết sang stage cuối
  • Ưu điểm:
    • Image nhẹ hơn (150MB vs 500MB)
    • Bảo mật tốt hơn (không chứa source code, build tools)
    • Tách biệt concern (deps → build → runtime)
dockerfile
FROM node:alpine AS deps     # Stage 1: Install dependencies
FROM node:alpine AS builder  # Stage 2: Build application
FROM node:alpine AS runner   # Stage 3: Runtime (final image)

COPY --from=deps nghĩa là gì?

💡 Trả lời:

  • Copy file từ stage deps sang stage hiện tại
  • Cú pháp: COPY --from=<stage_name> <source_path> <dest_path>
dockerfile
COPY --from=deps /usr/src/app/node_modules ./node_modules
# Từ stage "deps", copy folder node_modules sang stage hiện tại

COPY & Filesystem

❓ Tại sao phải COPY trong Docker? Code của tôi đâu có copy ở đâu?

💡 Trả lời:

  • Docker Container = 1 máy tính Linux riêng biệt, ban đầu RỖNG
  • Code của bạn ở máy host, container không thấy được
  • COPY = chuyển file từ máy host → container
🏠 Máy bạn (host)          🏢 Container Docker
├── package.json     →     ├── (ban đầu trống)
├── src/             →     ├── (sau COPY mới có)
└── ...                    └── ...

❓ Folder /usr/src/app nằm ở đâu?

💡 Trả lời:

  • /usr/src/app nằm BÊN TRONG container, không phải máy bạn
  • Container có filesystem Linux riêng: /, /usr/, /bin/, /etc/...
  • Bạn có thể xem bằng: docker run -it <image> sh rồi ls /

WORKDIR

❓ Có bắt buộc dùng /usr/src/app không? usr là gì?

💡 Trả lời:

  • KHÔNG bắt buộc! Có thể dùng bất kỳ folder nào
  • usr = Unix System Resources (chứa user programs)
  • /usr/src/app chỉ là convention phổ biến của Node.js

Các lựa chọn:

dockerfile
WORKDIR /app               # ✅ Đơn giản nhất
WORKDIR /usr/src/app       # ✅ Convention Node.js
WORKDIR /opt/myapp         # ✅ Corporate style
WORKDIR /myproject/api     # ✅ Custom

Tránh:

dockerfile
WORKDIR /bin      # ❌ System folders
WORKDIR /etc      # ❌ Config folders
WORKDIR /tmp      # ❌ Temporary folders

Environment Variables

❓ Khi build app cần .env thì xử lý thế nào?

💡 Trả lời:

  • TUYỆT ĐỐI TRÁNH copy .env vào image (nguy hiểm về security)
  • Dùng runtime environment variables

❌ Sai:

dockerfile
COPY .env ./  # Secrets bị "nướng" vào image

✅ Đúng:

bash
# Runtime injection
docker run --env-file .env.production my-app
docker run -e DATABASE_URL=xxx -e JWT_SECRET=yyy my-app

Cho build-time (config không nhạy cảm):

dockerfile
ARG NODE_ENV=production
ARG API_PORT=3000
RUN echo "NODE_ENV=${NODE_ENV}" > .env

❓ Multi-environment với Docker?

💡 Trả lời:

yaml
# docker-compose.yml
services:
  app-dev:
    build: .
    env_file: .env.development

  app-prod:
    build: .
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${PROD_DATABASE_URL}

ENTRYPOINT vs CMD

❓ Tại sao có ENTRYPOINT thì không chạy được docker run -it my-app sh?

💡 Trả lời:

  • ENTRYPOINT luôn chạy, không thể override dễ dàng
  • sh được truyền làm argument cho entrypoint script
  • Nếu script không xử lý arguments → sh bị bỏ qua
dockerfile
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# docker run -it my-app sh
# → Thực tế chạy: /usr/local/bin/entrypoint.sh sh

❓ Làm thế nào để debug khi có ENTRYPOINT?

💡 Trả lời:

Cách 1: Override entrypoint

bash
docker run -it --entrypoint sh my-app

Cách 2: Script entrypoint thông minh

bash
#!/bin/sh
# entrypoint.sh
if [ "$1" = "sh" ] || [ "$1" = "bash" ]; then
    exec "$@"  # Chạy shell
fi
# Chạy app bình thường
yarn start

Cách 3: Dùng CMD thay ENTRYPOINT

dockerfile
CMD ["/usr/local/bin/entrypoint.sh"]  # Dễ override

❓ ENTRYPOINT vs CMD khác nhau thế nào?

💡 Trả lời:

ENTRYPOINTCMD
OverrideKhó (cần --entrypoint)Dễ
Use caseLogic bắt buộc (init, health check)Default command
DebugCần xử lý đặc biệtĐơn giản
dockerfile
# Production app with init logic
ENTRYPOINT ["/init.sh"]
CMD ["yarn", "start"]

# Simple app
CMD ["yarn", "start"]

Docker Shell & Debug

❓ Làm thế nào để thoát khỏi docker run -it my-app sh?

💡 Trả lời:

3 cách chính:

bash
exit        # ✅ Thoát shell + container dừng
# Ctrl + D  # ✅ Tương tự exit
# Ctrl + C  # ⚠️ Chỉ dừng command hiện tại, KHÔNG thoát shell

Workflow debug:

bash
# 1. Vào container debug
docker run -it my-app sh

# 2. Debug
ls -la
cat package.json
env

# 3. Thoát
exit

❓ Làm thế nào để debug container đang chạy?

💡 Trả lời:

bash
# Container đang chạy app
docker run -d -p 8080:8080 my-app

# Debug từ terminal khác
docker ps                              # Lấy container ID
docker exec -it <container_id> sh      # Vào container
# ... debug ...
exit                                   # App vẫn chạy

Best Practices

❓ Dockerfile structure tốt nhất?

💡 Trả lời:

dockerfile
# Multi-stage cho Node.js
FROM node:alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN yarn --frozen-lockfile

FROM node:alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

FROM node:alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/*.json ./
CMD ["yarn", "start"]

❓ Security best practices?

💡 Trả lời:

  • ❌ Không copy .env vào image
  • ❌ Không hardcode secrets trong Dockerfile
  • ❌ Không dùng ARG cho sensitive data
  • ✅ Dùng runtime environment variables
  • ✅ Dùng secrets management (K8s secrets, Docker secrets)
  • ✅ Multi-stage để giảm attack surface

❓ Development workflow với Docker?

💡 Trả lời:

bash
# Development
docker-compose up  # với .env.development

# Debug
docker run -it --entrypoint sh my-app

# Production
docker run --env-file .env.production my-app

# CI/CD
docker build -t my-app .
docker run -e DATABASE_URL="$SECRET_DB_URL" my-app

🔧 Quick Commands Reference

bash
# Build
docker build -t my-app .
docker build --target debug -t my-app:debug .

# Run
docker run my-app                        # Normal
docker run -it my-app sh                 # Debug shell
docker run --entrypoint sh my-app        # Override entrypoint
docker run --env-file .env my-app        # With env file
docker run -e VAR=value my-app           # With env vars

# Debug running container
docker ps                               # List containers
docker exec -it <container_id> sh       # Exec into running container
docker logs <container_id>              # View logs

# Clean up
docker kill <container_id>              # Force stop
docker rm <container_id>                # Remove container
docker rmi <image>                      # Remove image

📚 Additional Resources


📝 Note: Tài liệu này tóm tắt kiến thức từ conversation về Docker fundamentals, multi-stage builds, environment handling, và debugging techniques.

Today I Learned