跳到主要内容

容器化改造实战

问题

公司决定将传统虚拟机部署的应用迁移到 Kubernetes 容器化部署,如何规划和执行?

答案

容器化改造路线图

Phase 1:评估与筛选

评估维度高优先(先容器化)暂缓容器化
应用类型无状态 Web/API有状态数据库
变更频率频繁发布(周/日级)很少变更
依赖标准化(Linux + 语言运行时)依赖特殊硬件/驱动
团队能力开发团队了解容器团队刚接触

Phase 2:编写 Dockerfile

# 多阶段构建 - 减小镜像体积
# Stage 1: 构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Stage 2: 运行
FROM node:20-alpine
# 安全:不用 root 运行
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
USER appuser
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -q --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/main.js"]
Dockerfile 最佳实践
  1. 多阶段构建:编译镜像和运行镜像分离,减小体积
  2. 非 root 运行:创建专用用户,避免容器逃逸后获得主机 root
  3. COPY 顺序:先 COPY package.json,再 COPY 源码,利用缓存
  4. 使用 alpine 基础镜像:体积从 1GB 降到 100MB 以下

Phase 3:Kubernetes 部署清单

apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 滚动更新不减少可用实例
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: registry.example.com/web-app:v1.2.3 # 不用 latest
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-credentials
key: host

Phase 4:灰度切流

# 通过 Nginx upstream 权重控制流量
# 先将 10% 流量切到 K8s
upstream backend {
server vm-app-01:3000 weight=9; # 旧 VM
server k8s-ingress:80 weight=1; # 新 K8s
}

# 观察 1~2 天无异常后,逐步调整为 50%、100%

# 或者通过 DNS 权重
# k8s.example.com A 10.0.1.1 weight=10
# k8s.example.com A 10.0.2.1 weight=90

常见面试问题

Q1: 容器化改造中最常见的坑有哪些?

答案

问题说明解决方案
日志写本地文件容器重启后丢失输出到 stdout,由日志系统收集
硬编码 IP/路径容器环境 IP 动态分配用环境变量或服务发现
时区问题容器默认 UTC挂载 /etc/localtime 或设置 TZ 环境变量
资源未设限单个 Pod 吃满节点必须设置 requests 和 limits
镜像过大拉取慢、部署慢多阶段构建、alpine 基础镜像
Session 丢失多副本时 Session 不共享Session 存 Redis

相关链接