14 KiB
14 KiB
Docker & Traefik - Detailed Reference
Detailed configurations và examples cho Docker và Traefik trong GoodGo.
Table of Contents
- Dockerfile Patterns
- Docker Compose Configurations
- Traefik Configuration
- SSL/TLS Setup
- Load Balancing
- Middleware Examples
- Development Environment
Dockerfile Patterns
Optimized .NET Dockerfile
# ===================================
# Stage 1: Build
# ===================================
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
# EN: Copy only project files first for better caching
# VI: Copy chỉ project files trước để cache tốt hơn
COPY services/iam-service-net/*.slnx ./
COPY services/iam-service-net/src/IamService.API/*.csproj src/IamService.API/
COPY services/iam-service-net/src/IamService.Domain/*.csproj src/IamService.Domain/
COPY services/iam-service-net/src/IamService.Infrastructure/*.csproj src/IamService.Infrastructure/
# EN: Restore dependencies
# VI: Restore dependencies
RUN dotnet restore src/IamService.API
# EN: Copy remaining source files
# VI: Copy các source files còn lại
COPY services/iam-service-net/src/ src/
# EN: Build and publish
# VI: Build và publish
RUN dotnet publish src/IamService.API \
-c Release \
-o /app \
--no-restore \
/p:PublishTrimmed=false \
/p:PublishSingleFile=false
# ===================================
# Stage 2: Runtime
# ===================================
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
# EN: Install required packages
# VI: Cài đặt packages cần thiết
RUN apk add --no-cache \
icu-libs \
tzdata \
&& rm -rf /var/cache/apk/*
# EN: Set timezone
# VI: Đặt timezone
ENV TZ=Asia/Ho_Chi_Minh
# EN: Create non-root user
# VI: Tạo user không phải root
RUN addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S appuser -G appgroup
WORKDIR /app
# EN: Copy published files with correct ownership
# VI: Copy files đã publish với ownership đúng
COPY --from=build --chown=appuser:appgroup /app .
# EN: Switch to non-root user
# VI: Chuyển sang user không phải root
USER appuser
# EN: Environment configuration
# VI: Cấu hình môi trường
ENV ASPNETCORE_URLS=http://+:8080 \
ASPNETCORE_ENVIRONMENT=Production \
DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
EXPOSE 8080
# EN: Health check
# VI: Kiểm tra sức khỏe
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health/live || exit 1
ENTRYPOINT ["dotnet", "IamService.API.dll"]
.dockerignore
# EN: Git / VI: Git
.git
.gitignore
# EN: Build outputs / VI: Build outputs
**/bin/
**/obj/
**/out/
# EN: IDE / VI: IDE
.vs/
.vscode/
.idea/
*.swp
*.user
# EN: Test results / VI: Kết quả test
**/TestResults/
**/coverage/
# EN: Docker / VI: Docker
**/Dockerfile*
**/docker-compose*
**/.dockerignore
# EN: Documentation / VI: Tài liệu
**/README.md
**/docs/
# EN: Secrets / VI: Secrets
**/*.env
**/appsettings.Development.json
**/appsettings.Local.json
Docker Compose Configurations
Complete Development Stack
# deployments/local/docker-compose.yml
version: "3.8"
services:
# ===================================
# TRAEFIK - API Gateway
# ===================================
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
command:
# EN: API and Dashboard
- "--api.dashboard=true"
- "--api.insecure=true"
# EN: Docker provider
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=goodgo-network"
# EN: Entrypoints
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# EN: Logging
- "--log.level=INFO"
- "--accesslog=true"
- "--accesslog.format=json"
# EN: Metrics
- "--metrics.prometheus=true"
- "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/certs:/certs:ro
networks:
- goodgo-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.traefik.service=api@internal"
# ===================================
# IAM SERVICE
# ===================================
iam-service-net:
build:
context: ../..
dockerfile: services/iam-service-net/Dockerfile
container_name: iam-service-net
restart: unless-stopped
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=${IAM_DATABASE_URL}
- Jwt__Authority=http://iam-service-net:8080
- Jwt__Audience=goodgo-api
labels:
- "traefik.enable=true"
# EN: HTTP Router
- "traefik.http.routers.iam-service-net.rule=PathPrefix(`/api/v1/iam`)"
- "traefik.http.routers.iam-service-net.entrypoints=web"
- "traefik.http.routers.iam-service-net.middlewares=iam-headers"
# EN: Service
- "traefik.http.services.iam-service-net.loadbalancer.server.port=8080"
- "traefik.http.services.iam-service-net.loadbalancer.healthcheck.path=/health/live"
- "traefik.http.services.iam-service-net.loadbalancer.healthcheck.interval=10s"
# EN: Response headers middleware
- "traefik.http.middlewares.iam-headers.headers.customresponseheaders.X-Service-Name=iam-service"
networks:
- goodgo-network
depends_on:
postgres:
condition: service_healthy
# ===================================
# STORAGE SERVICE
# ===================================
storage-service-net:
build:
context: ../..
dockerfile: services/storage-service-net/Dockerfile
container_name: storage-service-net
restart: unless-stopped
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=${STORAGE_DATABASE_URL}
- MinIO__Endpoint=minio:9000
- MinIO__AccessKey=${MINIO_ACCESS_KEY}
- MinIO__SecretKey=${MINIO_SECRET_KEY}
- MinIO__UseSSL=false
- IamService__BaseUrl=http://iam-service-net:8080
labels:
- "traefik.enable=true"
- "traefik.http.routers.storage-service-net.rule=PathPrefix(`/api/v1/storage`)"
- "traefik.http.routers.storage-service-net.entrypoints=web"
- "traefik.http.services.storage-service-net.loadbalancer.server.port=8080"
- "traefik.http.services.storage-service-net.loadbalancer.healthcheck.path=/health/live"
networks:
- goodgo-network
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_started
# ===================================
# POSTGRESQL
# ===================================
postgres:
image: postgres:15-alpine
container_name: postgres
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_MULTIPLE_DATABASES=iam_db,storage_db,wallet_db
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/create-multiple-dbs.sh:/docker-entrypoint-initdb.d/create-multiple-dbs.sh
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- goodgo-network
# ===================================
# REDIS
# ===================================
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- goodgo-network
# ===================================
# MINIO (S3-compatible)
# ===================================
minio:
image: minio/minio:latest
container_name: minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin
volumes:
- minio_data:/data
ports:
- "9000:9000"
- "9001:9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
networks:
- goodgo-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.minio-console.rule=Host(`minio.localhost`)"
- "traefik.http.routers.minio-console.service=minio-console"
- "traefik.http.services.minio-console.loadbalancer.server.port=9001"
networks:
goodgo-network:
driver: bridge
name: goodgo-network
volumes:
postgres_data:
redis_data:
minio_data:
Multi-Database Init Script
#!/bin/bash
# scripts/create-multiple-dbs.sh
set -e
set -u
function create_user_and_database() {
local database=$1
echo " Creating database '$database'"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
CREATE DATABASE $database;
GRANT ALL PRIVILEGES ON DATABASE $database TO $POSTGRES_USER;
EOSQL
}
if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then
echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES"
for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do
create_user_and_database $db
done
echo "Multiple databases created"
fi
Traefik Configuration
Static Configuration (traefik.yml)
# infra/traefik/traefik.yml
# EN: API and Dashboard
# VI: API và Dashboard
api:
dashboard: true
insecure: false
# EN: Entrypoints
# VI: Các điểm vào
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
# EN: Certificate resolvers
# VI: Bộ giải quyết chứng chỉ
certificatesResolvers:
letsencrypt:
acme:
email: admin@goodgo.vn
storage: /certs/acme.json
httpChallenge:
entryPoint: web
# EN: Providers
# VI: Providers
providers:
docker:
exposedByDefault: false
network: goodgo-network
file:
directory: /etc/traefik/dynamic
watch: true
# EN: Logging
# VI: Logging
log:
level: INFO
format: json
accessLog:
format: json
filters:
statusCodes:
- "400-599"
# EN: Metrics
# VI: Metrics
metrics:
prometheus:
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
Dynamic Configuration
# infra/traefik/dynamic/middlewares.yml
http:
middlewares:
# EN: Security headers
# VI: Headers bảo mật
secure-headers:
headers:
frameDeny: true
sslRedirect: true
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
customResponseHeaders:
X-Robots-Tag: "noindex,nofollow"
# EN: Rate limiting
# VI: Giới hạn rate
rate-limit:
rateLimit:
average: 100
burst: 50
period: 1m
# EN: Compression
# VI: Nén
compress:
compress: {}
# EN: CORS
# VI: CORS
cors:
headers:
accessControlAllowMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
accessControlAllowHeaders:
- "*"
accessControlAllowOriginList:
- "https://app.goodgo.vn"
- "http://localhost:3000"
accessControlMaxAge: 100
addVaryHeader: true
SSL/TLS Setup
Local Development with mkcert
# EN: Install mkcert / VI: Cài đặt mkcert
brew install mkcert
mkcert -install
# EN: Generate certificates / VI: Tạo certificates
cd infra/traefik/certs
mkcert "*.localhost" localhost 127.0.0.1 ::1
Traefik with Local Certs
# docker-compose.override.yml for local HTTPS
services:
traefik:
volumes:
- ./infra/traefik/certs:/certs:ro
labels:
- "traefik.http.routers.traefik.tls=true"
# Dynamic config: infra/traefik/dynamic/tls.yml
tls:
certificates:
- certFile: /certs/_wildcard.localhost.pem
keyFile: /certs/_wildcard.localhost-key.pem
stores:
default:
defaultCertificate:
certFile: /certs/_wildcard.localhost.pem
keyFile: /certs/_wildcard.localhost-key.pem
Load Balancing
Multiple Instances
services:
iam-service-net:
deploy:
replicas: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.iam.rule=PathPrefix(`/api/v1/iam`)"
- "traefik.http.services.iam.loadbalancer.server.port=8080"
# EN: Sticky sessions (optional)
# VI: Sticky sessions (tùy chọn)
- "traefik.http.services.iam.loadbalancer.sticky.cookie.name=iam_sticky"
- "traefik.http.services.iam.loadbalancer.sticky.cookie.secure=true"
# EN: Health check
- "traefik.http.services.iam.loadbalancer.healthcheck.path=/health/live"
- "traefik.http.services.iam.loadbalancer.healthcheck.interval=5s"