7. 容器化与微服务

7.1 容器技术基础

7.1.1 Docker 核心概念

镜像(Image)

  • 只读的文件系统模板
  • 包含运行应用所需的所有内容
  • 分层存储结构

容器(Container)

  • 镜像的运行实例
  • 轻量级、可移植的执行环境
  • 与宿主机和其他容器隔离

仓库(Registry)

  • 存储和分发镜像的服务
  • Docker Hub、Harbor等

7.1.2 Docker 基本操作

拉取镜像

docker pull nginx:latest

查看镜像列表

docker images

运行容器

docker run -d -p 8080:80 --name my-nginx nginx

查看运行中的容器

docker ps

查看所有容器

docker ps -a

停止容器

docker stop my-nginx

删除容器

docker rm my-nginx

删除镜像

docker rmi nginx:latest

查看容器日志

docker logs -f my-nginx

进入容器

docker exec -it my-nginx /bin/bash

7.1.3 Dockerfile 编写

基础镜像

FROM node:16-alpine

设置工作目录

WORKDIR /app

复制依赖文件

COPY package*.json ./

安装依赖

RUN npm ci --only=production

复制应用代码

COPY . .

暴露端口

EXPOSE 3000

健康检查

HEALTHCHECK --interval=30s --timeout=3s
CMD node healthcheck.js

启动命令

CMD [“node”, “server.js”]

多阶段构建示例

构建阶段

FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

生产阶段

FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD [“node”, “dist/server.js”]

7.1.4 Docker Compose

docker-compose.yml 示例

version: ‘3.8’

services:
web:
build: ./web
ports:
- “3000:3000”
environment:
- NODE_ENV=production
- DB_HOST=db
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
networks:
- app-network

db:
image: postgres:14
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=secret
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network

redis:
image: redis:7-alpine
networks:
- app-network

nginx:
image: nginx:alpine
ports:
- “80:80”
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
networks:
- app-network

volumes:
db-data:

networks:
app-network:
driver: bridge

常用命令

启动服务

docker-compose up -d

查看服务状态

docker-compose ps

查看日志

docker-compose logs -f web

停止服务

docker-compose down

重启服务

docker-compose restart web

构建并启动

docker-compose up -d --build

7.2 微服务架构

7.2.1 微服务设计原则

单一职责原则

  • 每个服务专注于单一业务功能
  • 高内聚、低耦合

服务自治

  • 独立部署、独立扩展
  • 拥有独立的数据库

去中心化治理

  • 技术栈自由选择
  • 数据管理去中心化

故障隔离

  • 服务间故障不相互影响
  • 熔断、降级机制

7.2.2 微服务架构模式

API Gateway 模式

// Express Gateway 配置示例
module.exports = {
apiEndpoints: {
users: {
host: ‘',
paths: '/api/users/

},
orders: {
host: ‘',
paths: '/api/orders/

}
},
serviceEndpoints: {
userService: {
url: ‘http://user-service:3001’
},
orderService: {
url: ‘http://order-service:3002’
}
},
policies: [‘cors’, ‘rate-limit’, ‘jwt’, ‘proxy’],
pipelines: {
userPipeline: {
apiEndpoints: [‘users’],
policies: [
{
cors: {
action: {
origin: ‘*’
}
}
},
{
jwt: {
action: {
secretOrPublicKey: process.env.JWT_SECRET
}
}
},
{
‘rate-limit’: {
action: {
max: 100,
windowMs: 60000
}
}
},
{
proxy: {
action: {
serviceEndpoint: ‘userService’
}
}
}
]
}
}
};

服务发现模式

// Consul 服务注册
const Consul = require(‘consul’);
const consul = new Consul();

// 注册服务
function registerService() {
const service = {
name: ‘user-service’,
id: ‘user-service-1’,
address: ‘192.168.1.100’,
port: 3001,
check: {
http: ‘http://192.168.1.100:3001/health’,
interval: ‘10s’
}
};

consul.agent.service.register(service, (err) => {
if (err) throw err;
console.log(‘Service registered’);
});
}

// 发现服务
async function discoverService(serviceName) {
return new Promise((resolve, reject) => {
consul.health.service(serviceName, (err, result) => {
if (err) reject(err);
const services = result
.filter(entry => entry.Checks.every(check => check.Status === ‘passing’))
.map(entry => entry.Service);
resolve(services);
});
});
}

7.2.3 服务间通信

RESTful API

// 用户服务
const express = require(‘express’);
const app = express();

app.get(‘/api/users/:id’, async (req, res) => {
try {
const user = await User.findById(req.params.id);
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});

app.post(‘/api/users’, async (req, res) => {
try {
const user = await User.create(req.body);
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});

gRPC 通信

// user.proto
syntax = “proto3”;

package user;

service UserService {
rpc GetUser (GetUserRequest) returns (User) {}
rpc CreateUser (CreateUserRequest) returns (User) {}
}

message GetUserRequest {
string id = 1;
}

message CreateUserRequest {
string name = 1;
string email = 2;
}

message User {
string id = 1;
string name = 2;
string email = 3;
}

// gRPC 服务端实现
const grpc = require(‘@grpc/grpc-js’);
const protoLoader = require(‘@grpc/proto-loader’);

const packageDefinition = protoLoader.loadSync(‘user.proto’);
const userProto = grpc.loadPackageDefinition(packageDefinition).user;

const server = new grpc.Server();

server.addService(userProto.UserService.service, {
getUser: async (call, callback) => {
try {
const user = await User.findById(call.request.id);
callback(null, user);
} catch (error) {
callback(error);
}
},
createUser: async (call, callback) => {
try {
const user = await User.create(call.request);
callback(null, user);
} catch (error) {
callback(error);
}
}
});

server.bindAsync(‘0.0.0.0:50051’,
grpc.ServerCredentials.createInsecure(),
() => {
server.start();
console.log(‘gRPC server running on port 50051’);
}
);

消息队列通信

// RabbitMQ 生产者
const amqp = require(‘amqplib’);

async function publishMessage(queue, message) {
const connection = await amqp.connect(‘amqp://localhost’);
const channel = await connection.createChannel();

await channel.assertQueue(queue, { durable: true });
channel.sendToQueue(queue, Buffer.from(JSON.stringify(message)), {
persistent: true
});

console.log(‘Message sent:’, message);

setTimeout(() => {
connection.close();
}, 500);
}

// RabbitMQ 消费者
async function consumeMessages(queue) {
const connection = await amqp.connect(‘amqp://localhost’);
const channel = await connection.createChannel();

await channel.assertQueue(queue, { durable: true });
channel.prefetch(1);

channel.consume(queue, async (msg) => {
const content = JSON.parse(msg.content.toString());
console.log(‘Received:’, content);

try {
  // 处理消息
  await processMessage(content);
  channel.ack(msg);
} catch (error) {
  console.error('Error processing message:', error);
  channel.nack(msg, false, true); // 重新入队
}

});
}

7.2.4 分布式事务

Saga 模式

// 订单 Saga 编排
class OrderSaga {
async createOrder(orderData) {
const sagaLog = [];

try {
  // 1. 创建订单
  const order = await this.orderService.create(orderData);
  sagaLog.push({ step: 'createOrder', data: order });
  
  // 2. 扣减库存
  await this.inventoryService.reserve(order.items);
  sagaLog.push({ step: 'reserveInventory', data: order.items });
  
  // 3. 处理支付
  const payment = await this.paymentService.process(order.total);
  sagaLog.push({ step: 'processPayment', data: payment });
  
  // 4. 发送通知
  await this.notificationService.send(order.userId, 'Order confirmed');
  
  return order;
} catch (error) {
  // 补偿事务
  await this.compensate(sagaLog);
  throw error;
}

}

async compensate(sagaLog) {
for (let i = sagaLog.length - 1; i >= 0; i–) {
const step = sagaLog[i];

  try {
    switch (step.step) {
      case 'createOrder':
        await this.orderService.cancel(step.data.id);
        break;
      case 'reserveInventory':
        await this.inventoryService.release(step.data);
        break;
      case 'processPayment':
        await this.paymentService.refund(step.data.id);
        break;
    }
  } catch (err) {
    console.error(`Compensation failed for ${step.step}:`, err);
  }
}

}
}

两阶段提交(2PC)

// 分布式事务协调器
class TransactionCoordinator {
async executeTransaction(participants, operation) {
const prepared = [];

// Phase 1: Prepare
for (const participant of participants) {
  try {
    await participant.prepare(operation);
    prepared.push(participant);
  } catch (error) {
    // 回滚已准备的参与者
    await this.abort(prepared);
    throw error;
  }
}

// Phase 2: Commit
try {
  for (const participant of prepared) {
    await participant.commit();
  }
} catch (error) {
  await this.abort(prepared);
  throw error;
}

}

async abort(participants) {
for (const participant of participants) {
try {
await participant.rollback();
} catch (error) {
console.error(‘Rollback failed:’, error);
}
}
}
}

7.3 Kubernetes(K8s)

7.3.1 核心概念

Pod

  • 最小部署单元
  • 包含一个或多个容器
  • 共享网络和存储

Deployment

  • 管理 Pod 的副本
  • 声明式更新
  • 滚动升级

Service

  • 服务发现和负载均衡
  • 稳定的网络端点
  • ClusterIP、NodePort、LoadBalancer

ConfigMap & Secret

  • 配置管理
  • 敏感信息存储

7.3.2 资源清单示例

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myapp:v1.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: “production”
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db.host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: db.password
resources:
requests:
memory: “128Mi”
cpu: “100m”
limits:
memory: “256Mi”
cpu: “200m”
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5

Service

apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:

  • protocol: TCP
    port: 80
    targetPort: 3000
    type: LoadBalancer

Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:

  • host: myapp.example.com
    http:
    paths:
    • path: /
      pathType: Prefix
      backend:
      service:
      name: web-service
      port:
      number: 80

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
db.host: “postgres-service”
db.port: “5432”
cache.ttl: “3600”
app.config: |
server:
port: 3000
timeout: 30
logging:
level: info

Secret

apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
db.password: cGFzc3dvcmQxMjM= # base64 encoded
api.key: YXBpa2V5MTIz

7.3.3 常用命令

创建资源

kubectl apply -f deployment.yaml

查看资源

kubectl get pods
kubectl get deployments
kubectl get services
kubectl get ingress

查看详细信息

kubectl describe pod web-app-xxx
kubectl describe deployment web-app

查看日志

kubectl logs web-app-xxx
kubectl logs -f web-app-xxx # 实时日志
kubectl logs web-app-xxx -c container-name # 多容器时指定

进入容器

kubectl exec -it web-app-xxx – /bin/bash

端口转发

kubectl port-forward pod/web-app-xxx 8080:3000

扩缩容

kubectl scale deployment web-app --replicas=5

滚动更新

kubectl set image deployment/web-app web=myapp:v2.0
kubectl rollout status deployment/web-app

回滚

kubectl rollout undo deployment/web-app
kubectl rollout history deployment/web-app

删除资源

kubectl delete pod web-app-xxx
kubectl delete -f deployment.yaml

查看集群信息

kubectl cluster-info
kubectl get nodes
kubectl top nodes
kubectl top pods

7.3.4 Helm 包管理

Chart 结构

mychart/
Chart.yaml # Chart 元数据
values.yaml # 默认配置值
charts/ # 依赖的 Chart
templates/ # 模板文件
deployment.yaml
service.yaml
ingress.yaml
_helpers.tpl # 模板助手

values.yaml

replicaCount: 3

image:
repository: myapp
tag: v1.0
pullPolicy: IfNotPresent

service:
type: ClusterIP
port: 80

ingress:
enabled: true
host: myapp.example.com

resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi

autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80

templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include “mychart.fullname” . }}
labels:
{{- include “mychart.labels” . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include “mychart.selectorLabels” . | nindent 6 }}
template:
metadata:
labels:
{{- include “mychart.selectorLabels” . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: “{{ .Values.image.repository }}:{{ .Values.image.tag }}”
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 3000
resources:
{{- toYaml .Values.resources | nindent 10 }}

Helm 命令

创建 Chart

helm create mychart

安装 Chart

helm install myapp ./mychart
helm install myapp ./mychart -f custom-values.yaml

升级

helm upgrade myapp ./mychart

回滚

helm rollback myapp 1

查看发布

helm list
helm status myapp

卸载

helm uninstall myapp

查看模板渲染结果

helm template myapp ./mychart

验证 Chart

helm lint ./mychart

打包

helm package mychart

7.4 监控与日志

7.4.1 Prometheus 监控

prometheus.yml

global:
scrape_interval: 15s
evaluation_interval: 15s

scrape_configs:

  • job_name: ‘kubernetes-pods’
    kubernetes_sd_configs:
    • role: pod
      relabel_configs:
    • source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
      action: keep
      regex: true
    • source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
      action: replace
      target_label: metrics_path
      regex: (.+)
    • source_labels: [address, __meta_kubernetes_pod_annotation_prometheus_io_port]
      action: replace
      regex: ([^:]+)(?::\d+)?😭\d+)
      replacement: $1:$2
      target_label: address

应用暴露指标

const express = require(‘express’);
const client = require(‘prom-client’);

const app = express();

// 创建指标
const httpRequestDuration = new client.Histogram({
name: ‘http_request_duration_seconds’,
help: ‘Duration of HTTP requests in seconds’,
labelNames: [‘method’, ‘route’, ‘status_code’]
});

const httpRequestTotal = new client.Counter({
name: ‘http_requests_total’,
help: ‘Total number of HTTP requests’,
labelNames: [‘method’, ‘route’, ‘status_code’]
});

// 中间件记录指标
app.use((req, res, next) => {
const start = Date.now();

res.on(‘finish’, () => {
const duration = (Date.now() - start) / 1000;
const route = req.route ? req.route.path : req.path;

httpRequestDuration
  .labels(req.method, route, res.statusCode)
  .observe(duration);

httpRequestTotal
  .labels(req.method, route, res.statusCode)
  .inc();

});

next();
});

// 暴露指标端点
app.get(‘/metrics’, async (req, res) => {
res.set(‘Content-Type’, client.register.contentType);
res.end(await client.register.metrics());
});

7.4.2 ELK 日志收集

Filebeat 配置

filebeat.inputs:

  • type: container
    paths:
    • /var/lib/docker/containers//.log
      processors:
    • add_kubernetes_metadata:
      host: ${NODE_NAME}
      matchers:
      • logs_path:
        logs_path: “/var/lib/docker/containers/”

output.elasticsearch:
hosts: [‘ELASTICSEARCHHOST:elasticsearch:{ELASTICSEARCH_HOST:elasticsearch}:ELASTICSEARCHHOST:elasticsearch:{ELASTICSEARCH_PORT:9200}’]
index: “filebeat-%{[agent.version]}-%{+yyyy.MM.dd}”

应用结构化日志

const winston = require(‘winston’);
const { ElasticsearchTransport } = require(‘winston-elasticsearch’);

const logger = winston.createLogger({
level: ‘info’,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new ElasticsearchTransport({
level: ‘info’,
clientOpts: { node: ‘http://elasticsearch:9200’ },
index: ‘app-logs’
})
]
});

// 使用日志
logger.info(‘User logged in’, {
userId: ‘12345’,
email: ‘user@example.com’,
ip: req.ip
});

logger.error(‘Payment failed’, {
orderId: ‘67890’,
amount: 100,
error: error.message
});

7.5 最佳实践

7.5.1 容器化最佳实践

  1. 使用多阶段构建减小镜像体积
  2. 使用 .dockerignore排除不必要的文件
  3. 使用官方基础镜像确保安全性
  4. 最小化层数合并 RUN 命令
  5. 不要以 root 用户运行
  6. 使用健康检查确保服务可用性
  7. 合理设置资源限制

7.5.2 微服务最佳实践

  1. 设计合理的服务边界
  2. 实现服务熔断和降级
  3. 使用 API 版本控制
  4. 实现完善的监控和日志
  5. 采用 CI/CD 自动化部署
  6. 做好服务文档
  7. 实施安全策略

7.5.3 K8s 最佳实践

  1. 使用命名空间隔离环境
  2. 设置资源请求和限制
  3. 配置健康检查探针
  4. 使用 ConfigMap 和 Secret 管理配置
  5. 实施 RBAC 权限控制
  6. 使用 HPA 自动扩缩容
  7. 定期备份 etcd 数据
  8. 使用 PodDisruptionBudget 保证可用性

更多推荐