Docker Phần 2 - Docker Registry và viết Dockerfile chuyên nghiệp
Chào các bạn! Sau khi đã tìm hiểu về Docker cơ bản trong phần 1, hôm nay chúng ta sẽ cùng khám phá hai chủ đề siêu quan trọng: Docker Registry và cách viết Dockerfile chuyên nghiệp. Đây chính là những kiến thức giúp bạn đưa ứng dụng từ máy local lên "mây xanh" một cách smooth nhất! 🚀
🏪 Phần 1: Docker Registry - Kho báu chứa image
Bạn có bao giờ tự hỏi: "Image Docker của mình build xong rồi, giờ làm sao để chia sẻ với team hoặc deploy lên server?" Câu trả lời chính là Docker Registry!
1.1. Docker Registry là gì?
Docker Registry giống như một "kho báu" trực tuyến, nơi bạn có thể:
- 📦 Lưu trữ các Docker image
- 🔄 Chia sẻ image với team hoặc cộng đồng
- 🚀 Deploy từ registry lên server production
Có thể hiểu đơn giản: Registry = GitHub cho Docker images!
1.2. Các loại Docker Registry phổ biến
Loại Registry | Mô tả | Ưu điểm | Nhược điểm |
---|---|---|---|
Docker Hub | Registry chính thức của Docker | Miễn phí, dễ sử dụng, cộng đồng lớn | Giới hạn private repo với tài khoản miễn phí |
Amazon ECR | Registry của AWS | Tích hợp tốt với AWS, bảo mật cao | Tính phí theo usage |
Google GCR | Registry của Google Cloud | Tích hợp với GCP, hiệu suất cao | Chỉ tốt nếu dùng GCP |
Private Registry | Tự host trên server riêng | Kiểm soát hoàn toàn, bảo mật tối đa | Phải tự quản lý và maintain |
🐳 Phần 2: Làm việc với Docker Hub
Docker Hub là lựa chọn phổ biến nhất để bắt đầu. Hãy cùng thực hành!
2.1. Đăng ký và chuẩn bị
# Bước 1: Đăng ký tài khoản tại https://hub.docker.com
# Bước 2: Đăng nhập từ terminal
docker login
# Bước 3: Kiểm tra đã đăng nhập thành công
docker info | grep Username
2.2. Build và push image lên Docker Hub
Giả sử bạn có một ứng dụng Node.js đơn giản:
# Dockerfile
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Quy trình push lên Docker Hub:
# Bước 1: Build image với tag đúng format
docker build -t username/my-app:v1.0 .
# Bước 2: Test image trước khi push
docker run -p 3000:3000 username/my-app:v1.0
# Bước 3: Push lên Docker Hub
docker push username/my-app:v1.0
# Bước 4: Push thêm tag latest (tùy chọn)
docker tag username/my-app:v1.0 username/my-app:latest
docker push username/my-app:latest
2.3. Pull và sử dụng image từ Docker Hub
# Pull image về máy khác
docker pull username/my-app:v1.0
# Hoặc chạy trực tiếp (sẽ tự động pull nếu chưa có)
docker run -p 3000:3000 username/my-app:v1.0
2.4. Quản lý image versions
# Xem tất cả tag của image
docker images username/my-app
# Push version mới
docker build -t username/my-app:v1.1 .
docker push username/my-app:v1.1
# Cập nhật tag latest
docker tag username/my-app:v1.1 username/my-app:latest
docker push username/my-app:latest
🏗️ Phần 3: Tạo Private Docker Registry
Đôi khi bạn cần một "kho bí mật" cho các image nội bộ. Hãy tạo Private Registry trên server riêng!
3.1. Cài đặt Private Registry
# Tạo thư mục cho registry data
mkdir -p /opt/docker-registry/data
# Chạy registry container
docker run -d \
--name registry \
--restart=always \
-p 5000:5000 \
-v /opt/docker-registry/data:/var/lib/registry \
registry:2
3.2. Cấu hình với SSL (Production-ready)
# Tạo thư mục cho certificates
mkdir -p /opt/docker-registry/certs
# Copy SSL certificate (giả sử bạn đã có)
cp domain.crt /opt/docker-registry/certs/
cp domain.key /opt/docker-registry/certs/
# Chạy registry với SSL
docker run -d \
--name registry-ssl \
--restart=always \
-p 443:5000 \
-v /opt/docker-registry/data:/var/lib/registry \
-v /opt/docker-registry/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
3.3. Cấu hình authentication
# Tạo file htpasswd cho authentication
mkdir -p /opt/docker-registry/auth
docker run --rm --entrypoint htpasswd httpd:2 \
-Bbn admin secretpassword > /opt/docker-registry/auth/htpasswd
# Chạy registry với auth
docker run -d \
--name registry-auth \
--restart=always \
-p 5000:5000 \
-v /opt/docker-registry/data:/var/lib/registry \
-v /opt/docker-registry/auth:/auth \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
3.4. Sử dụng Private Registry
# Đăng nhập vào private registry
docker login your-registry-domain.com:5000
# Build và tag image cho private registry
docker build -t your-registry-domain.com:5000/my-app:v1.0 .
# Push lên private registry
docker push your-registry-domain.com:5000/my-app:v1.0
# Pull từ private registry
docker pull your-registry-domain.com:5000/my-app:v1.0
3.5. Docker Compose cho Private Registry
# docker-compose.yml
version: '3.7'
services:
registry:
image: registry:2
container_name: docker-registry
restart: always
ports:
- "5000:5000"
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
volumes:
- ./data:/var/lib/registry
- ./auth:/auth
- ./certs:/certs
registry-ui:
image: joxit/docker-registry-ui:latest
container_name: registry-ui
restart: always
ports:
- "8080:80"
environment:
REGISTRY_TITLE: My Private Docker Registry
REGISTRY_URL: https://your-registry-domain.com:5000
depends_on:
- registry
📝 Phần 4: Viết Dockerfile chuyên nghiệp
Dockerfile là "công thức nấu ăn" để tạo ra Docker image. Viết tốt Dockerfile = image nhẹ, nhanh, bảo mật!
4.1. Các best practices khi viết Dockerfile
🎯 1. Chọn base image phù hợp
# ❌ Không nên - image quá nặng
FROM ubuntu:latest
# ✅ Nên - sử dụng Alpine cho image nhẹ
FROM node:16-alpine
# ✅ Hoặc multi-stage cho production
FROM node:16-alpine AS builder
🎯 2. Tối ưu hóa layer caching
# ❌ Không tối ưu - mỗi lần code thay đổi phải chạy lại npm install
COPY . .
RUN npm install
# ✅ Tối ưu - chỉ chạy lại npm install khi package.json thay đổi
COPY package*.json ./
RUN npm install
COPY . .
🎯 3. Minimize số lượng layer
# ❌ Nhiều layer không cần thiết
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN rm -rf /var/lib/apt/lists/*
# ✅ Gộp thành một layer
RUN apt-get update && \
apt-get install -y curl git && \
rm -rf /var/lib/apt/lists/*
🎯 4. Sử dụng .dockerignore
# .dockerignore
node_modules/
npm-debug.log
.git/
.DS_Store
*.md
.env*
coverage/
.nyc_output/
4.2. Dockerfile templates cho từng framework
Dưới đây là các template "ngon nghẻ" cho các framework phổ biến:
🎨 Frontend Applications
4.2.1. React Application
# Build stage
FROM node:16-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci
# Copy source code
COPY . .
# Build ứng dụng
RUN npm run build
# Production stage
FROM node:16-alpine AS production
WORKDIR /app
# Cài đặt serve để chạy static files
RUN npm install -g serve
# Copy build files từ builder stage
COPY --from=builder /app/build ./build
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/ || exit 1
# Chạy ứng dụng
CMD ["serve", "-s", "build", "-l", "3000"]
4.2.2. Next.js Application
# Dependencies stage
FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Cài đặt dependencies dựa trên package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Builder stage
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Tắt telemetry của Next.js
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
# Production stage
FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
# Tạo user không phải root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Tự động tận dụng output traces để giảm kích thước image
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
⚙️ Backend Applications
4.2.3. Express.js Application
# Build stage
FROM node:16-alpine AS builder
WORKDIR /app
# Copy package files trước (để tối ưu caching)
COPY package*.json ./
# Cài đặt tất cả dependencies
RUN npm ci
# Copy source code
COPY . .
# Build ứng dụng (nếu có build step)
RUN npm run build 2>/dev/null || echo "Không có build script"
# Production stage
FROM node:16-alpine AS production
WORKDIR /app
# Tạo user không phải root
RUN addgroup -g 1001 -S nodejs && \
adduser -S expressjs -u 1001
# Copy package files
COPY package*.json ./
# Chỉ cài đặt production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy ứng dụng đã build từ builder stage
COPY --from=builder --chown=expressjs:nodejs /app/dist ./dist 2>/dev/null || \
COPY --from=builder --chown=expressjs:nodejs /app/src ./src
# Copy các file cần thiết khác
COPY --chown=expressjs:nodejs *.js ./
# Chuyển sang user không phải root
USER expressjs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Khởi động ứng dụng
CMD ["npm", "start"]
4.2.4. NestJS Application
# Build stage
FROM node:16-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Cài đặt dependencies
RUN npm ci
# Copy source code
COPY . .
# Build ứng dụng NestJS
RUN npm run build
# Production stage
FROM node:16-alpine AS production
WORKDIR /app
# Tạo user không phải root
RUN addgroup -g 1001 -S nodejs && \
adduser -S nestjs -u 1001
# Copy package files
COPY package*.json ./
# Chỉ cài đặt production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy ứng dụng đã build và các file cần thiết
COPY --from=builder --chown=nestjs:nodejs /app/dist ./dist
# Chuyển sang user không phải root
USER nestjs
# Expose port
EXPOSE 3000
# Health check cho NestJS
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Khởi động ứng dụng NestJS
CMD ["node", "dist/main"]
4.2.5. Golang Application
# Build stage
FROM golang:1.19-alpine AS builder
WORKDIR /app
# Cài đặt git (cần cho go modules)
RUN apk add --no-cache git
# Copy go mod files
COPY go.mod go.sum ./
# Download dependencies
RUN go mod download
# Copy source code
COPY . .
# Build binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# Production stage
FROM alpine:latest AS production
# Cài đặt ca-certificates cho HTTPS requests
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Tạo user không phải root
RUN adduser -D -s /bin/sh appuser
# Copy binary từ builder stage
COPY --from=builder /app/main .
# Copy các config files hoặc static assets
COPY --from=builder /app/config ./config
# Thay đổi quyền sở hữu cho user không root
RUN chown -R appuser:appuser /root/
# Chuyển sang user không phải root
USER appuser
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
# Chạy binary
CMD ["./main"]
🛡️ Phần 5: Security Best Practices
5.1. Sử dụng non-root user
# Tạo user không phải root
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
# Chuyển quyền sở hữu file
COPY --chown=appuser:appgroup . .
# Chuyển sang user không root
USER appuser
5.2. Scan vulnerabilities
# Sử dụng Docker scan
docker scan your-image:tag
# Hoặc sử dụng Trivy
trivy image your-image:tag
# Snyk
snyk container test your-image:tag
5.3. Multi-stage build để giảm attack surface
# Build stage - chứa tools và dependencies để build
FROM node:16-alpine AS builder
# ... các bước build
# Production stage - chỉ chứa những gì cần thiết để chạy
FROM node:16-alpine AS production
COPY --from=builder /app/dist ./dist
# Không copy build tools và dev dependencies
🚀 Phần 6: Tips và Tricks nâng cao
6.1. Build context optimization
# Chỉ build những file cần thiết
echo "node_modules" > .dockerignore
echo ".git" >> .dockerignore
echo "*.md" >> .dockerignore
# Build với custom context
docker build -f Dockerfile.prod .
6.2. Multi-platform builds
# Build cho nhiều architecture
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
# Push multi-platform image
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest --push .
6.3. Build arguments và environment variables
# Sử dụng build arguments
ARG NODE_ENV=production
ARG API_URL=https://api.example.com
# Set environment variables
ENV NODE_ENV=${NODE_ENV}
ENV API_URL=${API_URL}
# Build với custom args
# docker build --build-arg NODE_ENV=staging .
6.4. Health checks cho production
# Simple HTTP health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Advanced health check với script
COPY healthcheck.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/healthcheck.sh
HEALTHCHECK --interval=30s --timeout=3s CMD /usr/local/bin/healthcheck.sh
6.5. Logging best practices
# Forward logs tới Docker logging driver
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log
# Hoặc trong application
console.log("Log này sẽ được Docker logs capture")
🎯 Phần 7: Workflow hoàn chỉnh từ dev đến production
7.1. Development workflow
# 1. Develop locally
docker build -t myapp:dev .
docker run -p 3000:3000 myapp:dev
# 2. Test
docker run --rm myapp:dev npm test
# 3. Build for production
docker build -f Dockerfile.prod -t myapp:v1.0 .
# 4. Push to registry
docker tag myapp:v1.0 username/myapp:v1.0
docker push username/myapp:v1.0
7.2. CI/CD Pipeline example (GitHub Actions)
# .github/workflows/docker.yml
name: Docker Build and Push
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
username/myapp:latest
username/myapp:${{ github.sha }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'username/myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
7.3. Production deployment
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: username/myapp:v1.0
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./certs:/etc/ssl/certs
depends_on:
- app
🎉 Kết luận
Woa! Chúng ta vừa cùng nhau khám phá một hành trình dài từ Docker Registry đến việc viết Dockerfile chuyên nghiệp. Những gì bạn vừa học được:
📚 Tóm tắt kiến thức:
- Docker Registry: Hiểu cách lưu trữ và chia sẻ image
- Docker Hub: Push/pull image từ registry công cộng
- Private Registry: Tạo kho riêng cho dự án nội bộ
- Dockerfile Best Practices: Viết Dockerfile tối ưu và bảo mật
- Framework Templates: Các template cho React, Next.js, Express, NestJS, Golang
- Security: Bảo mật container và image
- Production Workflow: Quy trình từ development đến production
🚀 Bước tiếp theo:
- Thực hành với các template Dockerfile ở trên
- Set up một private registry cho team/công ty
- Tích hợp Docker vào CI/CD pipeline
- Tìm hiểu về Container Orchestration (Kubernetes, Docker Swarm)
💡 Lời khuyên cuối:
- Start small: Bắt đầu với Docker Hub và các template đơn giản
- Practice: Thực hành với dự án thật, không chỉ đọc lý thuyết
- Security first: Luôn quan tâm đến bảo mật từ đầu
- Monitor: Theo dõi performance và logs của container
Hy vọng bài viết này giúp bạn tự tin hơn khi làm việc với Docker! Nếu có thắc mắc gì, đừng ngần ngại comment bên dưới nhé. Happy Dockerizing! 🐳✨
Đọc thêm: