Docker 基础
核心概念
┌──────────────────────────────────────────────────────────────┐
│ Docker 核心概念 │
├──────────────────────────────────────────────────────────────┤
│ │
│ Image (镜像) - 应用的只读模板 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ FROM node:20-alpine │ │
│ │ WORKDIR /app │ │
│ │ COPY package*.json ./ │ │
│ │ RUN npm ci │ │
│ │ COPY . . │ │
│ │ CMD ["npm", "start"] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Container (容器) - 镜像的运行实例 │
│ ┌────────────┐ │
│ │ Container │ ← Image 的运行实例 │
│ │ │ ← 可读写层 │
│ │ 应用进程 │ ← 隔离的文件系统 │
│ │ │ ← 独立的网络 │
│ └────────────┘ │
│ │
│ Registry (镜像仓库) - 存储和分发镜像 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker Hub / Harbor / ECR / GCR │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
Dockerfile 最佳实践
# 多阶段构建
# 阶段 1:构建
FROM node:20-alpine AS builder
WORKDIR /app
# 复制依赖文件(利用 Docker 缓存)
COPY package*.json ./
RUN npm ci --only=production && \
npm cache clean --force
# 复制源码
COPY . .
# 构建
RUN npm run build
# 阶段 2:运行
FROM node:20-alpine AS production
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
WORKDIR /app
# 只复制构建产物
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
# 切换用户
USER nextjs
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "dist/server.js"]
Docker Compose
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
image: myapp:latest
container_name: myapp
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/app
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
networks:
- app-network
db:
image: postgres:15-alpine
container_name: postgres
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d app"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
cache:
image: redis:7-alpine
container_name: redis
command: redis-server --appendonly yes
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- app-network
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
restart: unless-stopped
networks:
- app-network
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
Kubernetes 基础
核心概念
┌──────────────────────────────────────────────────────────────┐
│ Kubernetes 核心概念 │
├──────────────────────────────────────────────────────────────┤
│ │
│ Cluster (集群) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Control │ │ Worker │ │ Worker │ │ │
│ │ │ Plane │ │ Node 1 │ │ Node 2 │ │ │
│ │ └─────────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │
│ │ └─────┬──────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Pods │ │ │
│ │ │ ┌───┐ ┌───┐ │ │ │
│ │ │ │App│ │ DB │ │ │ │
│ │ │ └───┘ └───┘ │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
Pod、Deployment、Service
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: production
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: myapp-lb
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
Helm Charts
Helm 模板
# values.yaml
replicaCount: 3
image:
repository: myapp
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
config:
database_url: ""
redis_url: ""
# 部署
helm install myapp ./charts/myapp \
--namespace production \
--values ./charts/myapp/values.prod.yaml
# 升级
helm upgrade myapp ./charts/myapp \
--namespace production \
--values ./charts/myapp/values.prod.yaml
# 回滚
helm rollback myapp 1 --namespace production
GitOps 工作流
ArgoCD 配置
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myapp
targetRevision: main
path: k8s/overlays/production
kustomize:
images:
- myapp=myorg/myapp:v1.2.3
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
GitOps 流程
┌──────────────────────────────────────────────────────────────┐
│ GitOps 流程 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 开发者 ──→ Git ──→ CI/CD ──→ Kubernetes │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌───────────┐ ┌───────────┐ │
│ │ │ 镜像构建 │ │ 自动部署 │ │
│ │ └───────────┘ └───────────┘ │
│ │ │ │
│ └─────────────────────────────────────────┘ │
│ Git 为唯一真相来源 │
│ │
└──────────────────────────────────────────────────────────────┘
Kustomize
# k8s/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
template:
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 3000
# k8s/overlays/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: staging
replicas:
- name: myapp
count: 2
commonLabels:
variant: staging
patches:
- path: replicas.yaml
- path: env.yaml
# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
replicas:
- name: myapp
count: 5
commonLabels:
variant: production
patches:
- path: replicas.yaml
- path: env.yaml
环境管理
多环境配置
# config.yaml
environments:
development:
apiUrl: http://localhost:8080
logLevel: debug
features:
enableDebugTools: true
staging:
apiUrl: https://staging-api.example.com
logLevel: info
features:
enableDebugTools: false
production:
apiUrl: https://api.example.com
logLevel: warn
features:
enableDebugTools: false
ConfigMap 和 Secret
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
app.yaml: |
server:
port: 3000
logging:
level: info
features:
enableAnalytics: true
---
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
stringData:
database-url: "postgres://user:pass@db:5432/app"
api-key: "secret-api-key"
监控与日志
Prometheus + Grafana
# prometheus-monitoring.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
spec:
ports:
- port: 9090
targetPort: 9090
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
path: /metrics
interval: 15s
日志收集
# fluentd-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
data:
fluent.conf: |
<source>
@type tail
@id input-tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<filter kubernetes.**>
@type kubernetes_metadata
@id filter-kube_metadata
</filter>
<match kubernetes.**>
@type elasticsearch
@id output-elasticsearch
host elasticsearch.logging
port 9200
logstash_format true
logstash_prefix kubernetes
</match>
这一章想说的
容器化和 DevOps 让前端应用可以可靠地部署和运维:
- Docker:容器化应用,实现一致的运行环境
- Docker Compose:本地多容器编排
- Kubernetes:生产级容器编排平台
- Helm:Kubernetes 包管理
- GitOps:以 Git 为核心的声明式部署
- 监控与日志:完整的可观测性
掌握这些技术,前端工程师可以更好地参与运维工作。