容器化微服务全自动化
发布: (2025年12月10日 GMT+8 05:18)
5 min read
原文: Dev.to
Source: Dev.to
大局概览
我们正在构建的内容
- 多个微服务(5 种不同的编程语言)
- 自动化基础设施供应(Terraform)
- 自动化配置管理(Ansible)
- CI/CD 流水线并带漂移检测
- 使用 Traefik 自动 HTTPS
- 零信任安全模型
架构
graph TD
A[Traefik (HTTPS/Proxy)] --> B[Frontend (Vue.js)]
A --> C[Auth API (Go)]
A --> D[Todos API (Node.js)]
C --> E[Users API (Java Spring)]
C --> F[Redis Queue]
C --> G[Log Processor (Python)]
理解应用
服务
1. 前端(Vue.js)
- 用户界面、登录页 → TODO 仪表盘
- 与后端 API 通信
- 通过 Traefik 在 80/443 端口暴露
2. 认证 API(Go)
- 处理用户认证并签发 JWT 令牌
- 端点:
/api/auth
3. Todos API(Node.js)
- 管理 TODO 项目(增删改查)
- 需要有效的 JWT 令牌
- 端点:
/api/todos
4. 用户 API(Java Spring Boot)
- 用户管理和个人资料操作
- 端点:
/api/users
5. 日志处理器(Python)
- 处理后台任务,从 Redis 队列消费,写入审计日志
6. Redis 队列
- 异步操作的消息中间件
第 1 阶段:容器化
前端 Dockerfile(Vue.js)
# Multi‑stage build for optimized production image
# Stage 1 – build the application
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2 – serve with nginx
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
为什么使用多阶段构建?
构建阶段约 800 MB(包括构建工具);最终阶段约 25 MB(仅 nginx + 静态文件),大小减少了 97 %。
前端 nginx 配置
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# SPA routing – send all requests to index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
认证 API Dockerfile(Go)
# Multi‑stage build for Go
# Stage 1 – build the binary
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# Stage 2 – minimal runtime image
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
EXPOSE 8080
CMD ["./main"]
为什么采用这种方式?
构建阶段约 400 MB;最终阶段约 15 MB(静态二进制文件,无运行时依赖)。启动更快,攻击面更小。
Todos API Dockerfile(Node.js)
FROM node:18-alpine
WORKDIR /app
# Install dependencies first (better caching)
COPY package*.json ./
RUN npm ci --only=production
# Copy application code
COPY . .
# Create non‑root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js || exit 1
EXPOSE 3000
CMD ["node", "server.js"]
安全提示: 以非 root 用户运行可在容器被攻破时限制损害。
用户 API Dockerfile(Java Spring Boot)
# Multi‑stage build for Java
# Stage 1 – build with Maven
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml ./
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# Stage 2 – runtime
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Java 特定优化
ENTRYPOINT ["java",
"-XX:+UseContainerSupport",
"-XX:MaxRAMPercentage=75.0",
"-XX:+ExitOnOutOfMemoryError",
"-jar", "/app/app.jar"]
日志处理器 Dockerfile(Python)
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Create non‑root user
RUN useradd -m -u 1001 processor && \
chown -R processor:processor /app
USER processor
HEALTHCHECK --interval=30s --timeout=3s \
CMD ps aux | grep processor.py || exit 1
CMD ["python", "processor.py"]
Docker Compose – 整体编排
version: '3.8'
services:
# Traefik reverse proxy
traefik:
image: traefik:v2.10
container_name: traefik
command:
- "--api.dashboard=true"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
# Let's Encrypt (example configuration)
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=you@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "letsencrypt:/letsencrypt"
# Frontend (Vue.js)
frontend:
build: ./frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`todo.example.com`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.routers.frontend.tls.certresolver=myresolver"
# Auth API (Go)
auth:
build: ./auth
labels:
- "traefik.enable=true"
- "traefik.http.routers.auth.rule=Host(`auth.todo.example.com`)"
- "traefik.http.routers.auth.entrypoints=websecure"
- "traefik.http.routers.auth.tls.certresolver=myresolver"
# Todos API (Node.js)
todos:
build: ./todos
labels:
- "traefik.enable=true"
- "traefik.http.routers.todos.rule=Host(`api.todo.example.com`)"
- "traefik.http.routers.todos.entrypoints=websecure"
- "traefik.http.routers.todos.tls.certresolver=myresolver"
# Users API (Java Spring Boot)
users:
build: ./users
labels:
- "traefik.enable=true"
- "traefik.http.routers.users.rule=Host(`users.todo.example.com`)"
- "traefik.http.routers.users.entrypoints=websecure"
- "traefik.http.routers.users.tls.certresolver=myresolver"
# Log Processor (Python)
logprocessor:
build: ./logprocessor
# Redis Queue
redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes:
- "redis-data:/data"
volumes:
letsencrypt:
redis-data: