Docker容器化部署实战指南
•软件部署
Docker容器化部署实战指南
Docker容器化技术已经成为现代应用部署的标准方式。本文将详细介绍Docker容器化部署的完整流程和最佳实践。
Docker基础概念
1. 核心概念
镜像(Image)
- 只读模板,包含应用运行所需的所有内容
- 分层存储,便于共享和复用
- 版本管理,支持标签
容器(Container)
- 镜像的运行实例
- 独立隔离的运行环境
- 可启动、停止、删除
仓库(Registry)
- 存储和分发镜像的服务
- 公共仓库:Docker Hub
- 私有仓库:Harbor、Nexus
2. Dockerfile编写
基础镜像选择
# 多阶段构建示例
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
USER node
CMD ["node", "dist/main.js"]
最佳实践
# 使用官方镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 先复制依赖文件,利用缓存层
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 非root用户运行
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["python", "app.py"]
镜像构建与优化
1. 镜像构建
构建命令
# 基础构建
docker build -t myapp:1.0 .
# 指定Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .
# 构建参数
docker build --build-arg NODE_ENV=production -t myapp:prod .
# 多平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
构建缓存优化
# 不好的做法 - 每次都会重新安装依赖
COPY . .
RUN npm install
# 好的做法 - 利用缓存层
COPY package*.json ./
RUN npm ci
COPY . .
2. 镜像优化
减小镜像体积
# 使用精简基础镜像
FROM alpine:latest
# 合并RUN命令减少层数
RUN apk add --no-cache \
python3 \
py3-pip \
&& pip3 install --no-cache-dir requests
# 清理缓存
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*
# 使用.dockerignore
# .git
# node_modules
# *.md
# .env
镜像安全扫描
# 使用Trivy扫描
trivy image myapp:latest
# 使用Docker Scout
docker scout cves myapp:latest
# 使用Snyk
snyk container test myapp:latest
容器编排
1. Docker Compose
基础配置
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://db:5432/myapp
depends_on:
- db
- redis
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
networks:
- app-network
restart: unless-stopped
command: redis-server --appendonly yes
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- web
networks:
- app-network
restart: unless-stopped
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
生产环境配置
version: '3.8'
services:
app:
image: myapp:${VERSION:-latest}
deploy:
replicas: 3
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
rollback_config:
parallelism: 1
delay: 10s
environment:
- ENV=production
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
2. Kubernetes部署
Deployment配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: config
configMap:
name: app-config
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
topologyKey: kubernetes.io/hostname
Service和Ingress
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
CI/CD集成
1. GitLab CI配置
stages:
- build
- test
- security
- push
- deploy
variables:
DOCKER_REGISTRY: registry.example.com
IMAGE_NAME: $DOCKER_REGISTRY/myapp
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
only:
- main
test:
stage: test
image: $IMAGE_NAME:$CI_COMMIT_SHA
script:
- npm test
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
security:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_NAME:$CI_COMMIT_SHA
allow_failure: true
push:
stage: push
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $DOCKER_REGISTRY
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:latest
only:
- main
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context staging
- kubectl set image deployment/myapp myapp=$IMAGE_NAME:$CI_COMMIT_SHA
- kubectl rollout status deployment/myapp
environment:
name: staging
only:
- main
deploy-production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context production
- kubectl set image deployment/myapp myapp=$IMAGE_NAME:$CI_COMMIT_SHA
- kubectl rollout status deployment/myapp
environment:
name: production
when: manual
only:
- main
2. GitHub Actions配置
name: Docker Build and Deploy
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
security-scan:
runs-on: ubuntu-latest
needs: build
steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
deploy:
runs-on: ubuntu-latest
needs: [build, security-scan]
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Kubernetes
run: |
echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig
export KUBECONFIG=kubeconfig
kubectl set image deployment/myapp myapp=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
kubectl rollout status deployment/myapp
监控和日志
1. 容器监控
Prometheus + Grafana
# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
node-exporter:
image: prom/node-exporter:latest
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
ports:
- "8080:8080"
volumes:
prometheus_data:
grafana_data:
2. 日志收集
EFK Stack
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
fluentd:
build: ./fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
- /var/lib/docker/containers:/var/lib/docker/containers:ro
ports:
- "24224:24224"
- "24224:24224/udp"
kibana:
image: docker.elastic.co/kibana/kibana:8.11.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
volumes:
elasticsearch_data:
安全最佳实践
1. 镜像安全
# 使用非root用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser
# 最小化镜像
FROM scratch
COPY --from=builder /app/myapp /myapp
EXPOSE 8080
ENTRYPOINT ["/myapp"]
# 签名镜像
docker trust sign myregistry/myapp:latest
2. 运行时安全
# Kubernetes安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
总结
Docker容器化部署已经成为现代应用交付的标准方式:
- 镜像优化:多阶段构建、精简基础镜像、安全扫描
- 编排管理:Docker Compose适合小规模,Kubernetes适合大规模
- CI/CD集成:自动化构建、测试、部署流程
- 监控日志:完善的监控和日志收集系统
- 安全实践:镜像安全、运行时安全、网络安全
通过容器化部署,可以实现应用的快速交付、弹性扩展和高效运维。