目标读者:DevOps 工程师、系统运维工程师
阅读时间:约 25 分钟
前置知识:Docker、Kubernetes 基础
一、部署方案
1.1 部署架构概述
MES 系统的部署架构根据企业规模和需求分为三种方案:
| 方案 | 适用场景 | 复杂度 | 成本 |
|---|---|---|---|
| 单机 Docker Compose | 开发测试、小型工厂 | 低 | 低 |
| Kubernetes 集群 | 中大型工厂、多产线 | 中 | 中 |
| 混合云部署 | 集团多工厂、高可用 | 高 | 高 |
1.2 方案一:Docker Compose 单机部署
java
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mes-mysql
environment:
MYSQL_ROOT_PASSWORD: Mes@2026
MYSQL_DATABASE: mes_order
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./sql/init:/docker-entrypoint-initdb.d
restart: always
redis:
image: redis:7-alpine
container_name: mes-redis
ports:
- "6379:6379"
command: redis-server --requirepass Mes@2026
volumes:
- redis_data:/data
restart: always
# MES 工单服务
mes-order:
build: ./mes-order
container_name: mes-order
ports:
- "8081:8081"
environment:
SPRING_PROFILES_ACTIVE: docker
DB_URL: jdbc:mysql://mysql:3306/mes_order
DB_USER: root
DB_PASS: Mes@2026
REDIS_HOST: redis
REDIS_PASS: Mes@2026
depends_on:
- mysql
- redis
restart: always
# MES 质量服务
mes-quality:
build: ./mes-quality
container_name: mes-quality
ports:
- "8082:8082"
environment:
SPRING_PROFILES_ACTIVE: docker
depends_on:
- mysql
- redis
restart: always
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: mes-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- mes-order
- mes-quality
restart: always
# 前端
mes-web:
build: ./mes-web
container_name: mes-web
ports:
- "3000:80"
restart: always
volumes:
mysql_data:
redis_data:
1.3 方案二:Kubernetes 集群部署
java
# k8s/mes-order-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mes-order
namespace: mes
labels:
app: mes-order
spec:
replicas: 3
selector:
matchLabels:
app: mes-order
template:
metadata:
labels:
app: mes-order
spec:
containers:
- name: mes-order
image: registry.example.com/mes-order:1.0.0
ports:
- containerPort: 8081
env:
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
- name: DB_URL
valueFrom:
secretKeyRef:
name: mes-db-secret
key: url
- name: DB_USER
valueFrom:
secretKeyRef:
name: mes-db-secret
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: mes-db-secret
key: password
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8081
initialDelaySeconds: 15
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: mes-order-svc
namespace: mes
spec:
selector:
app: mes-order
ports:
- port: 8081
targetPort: 8081
type: ClusterIP
---
# HPA 自动扩缩
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mes-order-hpa
namespace: mes
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mes-order
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
1.4 Nginx 反向代理配置
javascript
# nginx/nginx.conf
upstream mes-order {
server mes-order:8081;
}
upstream mes-quality {
server mes-quality:8082;
}
server {
listen 80;
server_name mes.example.com;
# 强制 HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name mes.example.com;
ssl_certificate /etc/nginx/ssl/mes.crt;
ssl_certificate_key /etc/nginx/ssl/mes.key;
ssl_protocols TLSv1.2 TLSv1.3;
# 工单服务
location /api/v1/work-orders/ {
proxy_pass http://mes-order;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 质量服务
location /api/v1/quality/ {
proxy_pass http://mes-quality;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 前端静态资源
location / {
proxy_pass http://mes-web:80;
}
# WebSocket 看板推送
location /ws/ {
proxy_pass http://mes-order;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
二、CI/CD 流水线
2.1 GitLab CI 配置
java
# .gitlab-ci.yml
stages:
- build
- test
- docker-build
- deploy
variables:
REGISTRY: registry.example.com
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
# 阶段一:构建
build:
stage: build
image: maven:3.9-eclipse-temurin-17
script:
- mvn clean package -DskipTests
artifacts:
paths:
- mes-order/target/*.jar
- mes-quality/target/*.jar
expire_in: 1 hour
# 阶段二:测试
unit-test:
stage: test
image: maven:3.9-eclipse-temurin-17
script:
- mvn test
artifacts:
reports:
junit:
- "*/target/surefire-reports/TEST-*.xml"
# 代码质量扫描
sonarqube:
stage: test
image: sonarsource/sonar-scanner-cli
script:
- sonar-scanner
-Dsonar.projectKey=mes
-Dsonar.host.url=$SONAR_HOST_URL
-Dsonar.login=$SONAR_TOKEN
only:
- main
- merge_requests
# 阶段三:构建 Docker 镜像
docker-build:
stage: docker-build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $REGISTRY_USER -p $REGISTRY_PASS $REGISTRY
script:
- cd mes-order
- docker build -t $REGISTRY/mes-order:$IMAGE_TAG .
- docker push $REGISTRY/mes-order:$IMAGE_TAG
- cd ../mes-quality
- docker build -t $REGISTRY/mes-quality:$IMAGE_TAG .
- docker push $REGISTRY/mes-quality:$IMAGE_TAG
- cd ../mes-web
- docker build -t $REGISTRY/mes-web:$IMAGE_TAG .
- docker push $REGISTRY/mes-web:$IMAGE_TAG
only:
- main
# 阶段四:部署到 K8s
deploy-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/mes-order mes-order=$REGISTRY/mes-order:$IMAGE_TAG -n mes-staging
- kubectl set image deployment/mes-quality mes-quality=$REGISTRY/mes-quality:$IMAGE_TAG -n mes-staging
- kubectl rollout status deployment/mes-order -n mes-staging
environment:
name: staging
only:
- develop
deploy-production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/mes-order mes-order=$REGISTRY/mes-order:$IMAGE_TAG -n mes-prod
- kubectl rollout status deployment/mes-order -n mes-prod
environment:
name: production
only:
- main
when: manual # 生产部署需手动触发
2.2 Dockerfile 示例
java
# mes-order/Dockerfile
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S mes && adduser -S mes -G mes
USER mes
WORKDIR /app
COPY target/mes-order-*.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java", "-Xms512m", "-Xmx1g", "-jar", "app.jar"]
三、监控告警
3.1 监控架构
3.2 Spring Boot Micrometer 指标暴露
java
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
tags:
application: ${spring.application.name}
3.3 Prometheus 配置
java
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'mes-order'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['mes-order:8081']
labels:
service: 'mes-order'
- job_name: 'mes-quality'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['mes-quality:8082']
labels:
service: 'mes-quality'
3.4 核心告警规则
java
# alert.rules.yml
groups:
- name: mes-critical
rules:
- alert: HighErrorRate
expr: rate(http_server_requests_seconds_count{status=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "服务 {{ $labels.service }} 5xx 错误率超过 5%"
- alert: HighResponseTime
expr: histogram_quantile(0.99, rate(http_server_requests_seconds_bucket[5m])) > 3
for: 3m
labels:
severity: warning
annotations:
summary: "服务 {{ $labels.service }} P99 延迟超过 3 秒"
- alert: DatabaseConnectionPoolExhausted
expr: hikaricp_connections_active / hikaricp_connections_max > 0.9
for: 2m
labels:
severity: critical
annotations:
summary: "数据库连接池使用率超过 90%"
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "服务 {{ $labels.job }} 已宕机"
3.5 ELK 日志收集
java
# docker-compose 添加 ELK 服务
elasticsearch:
image: elasticsearch:8.11
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
logstash:
image: logstash:8.11
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
depends_on:
- elasticsearch
kibana:
image: kibana:8.11
ports:
- "5601:5601"
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
depends_on:
- elasticsearch
四、性能优化
4.1 优化清单
| 层级 | 优化项 | 措施 | 预期效果 |
|---|---|---|---|
| JVM | GC 调优 | G1GC: -XX:+UseG1GC -XX:MaxGCPauseMillis=200 | 减少 STW 时间 |
| 数据库 | 连接池 | HikariCP,maxPoolSize=20,minimumIdle=5 | 减少连接等待 |
| 数据库 | 慢 SQL 索引优化 | 分析慢查询日志,添加缺失索引 | 查询速度提升 10-100x |
| 数据库 | 读写分离 | 主库写 + 从库读(ShardingSphere) | 分散读压力 |
| 缓存 | 热点数据缓存 | Redis 缓存工单列表、物料主数据 | 减少 DB 查询 60%+ |
| 缓存 | 缓存策略 | TTL 5min + 写时失效 | 平衡实时性与性能 |
| 接口 | 分页查询 | 强制分页,默认 pageSize=20,最大 100 | 避免大结果集 |
| 接口 | 异步化 | 非核心业务异步处理 | 降低响应时间 |
| 前端 | CDN | 静态资源 CDN 分发 | 加快页面加载 |
4.2 数据库优化示例
sql
-- 慢查询日志配置
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 0.5; -- 0.5秒以上记录
SET GLOBAL log_queries_not_using_indexes = 1;
-- 常见优化:添加缺失索引
ALTER TABLE work_order_routing ADD INDEX idx_wo_status (work_order_id, status);
-- 分析执行计划
EXPLAIN SELECT * FROM work_order WHERE status = 'IN_PRODUCTION' ORDER BY priority ASC;
五、安全策略
5.1 安全防护措施
| 层面 | 措施 |
|---|---|
| 网络层 | K8s NetworkPolicy + 防火墙 + WAF |
| 传输层 | TLS 1.3 加密,禁用 TLS 1.0/1.1 |
| 应用层 | JWT 认证 + RBAC 权限 + 接口签名 |
| 数据层 | 敏感字段加密 + 数据库加密 + 备份加密 |
| 容器层 | 非 root 用户运行 + 只读根文件系统 |
| 审计层 | 所有关键操作日志不可篡改 |
5.2 K8s 安全上下文
java
securityContext:
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
六、灾备与恢复
| 能力 | 方案 | RTO | RPO |
|---|---|---|---|
| MySQL 主从 | 一主两从 + 自动故障转移 | < 5 分钟 | < 1 分钟 |
| MySQL 备份 | 全量每日 + Binlog 实时 | --- | 可恢复到任意时间点 |
| Redis | 哨兵模式自动故障转移 | < 1 分钟 | 少量数据丢失 |
| 应用层 | K8s 多副本 + 自动重启 | < 30 秒 | 0 |
| 异地灾备 | MySQL 异地同步 + 每日备份 | < 30 分钟 | < 1 小时 |
七、运维 Checklist
日常巡检(每日)
- 各服务健康检查(/actuator/health)
- 数据库主从延迟检查
- 磁盘空间使用率 < 80%
- 应用错误日志无异常增长
周度维护
- 慢查询日志分析
- 缓存命中率检查
- 安全漏洞扫描
月度维护
- SSL 证书有效期检查
- 备份恢复演练
- 容量规划(QPS 趋势)