i007.cc

i007.cc

优先队列-降维打击

05.价值资料

Docker & Kubernetes 深度解析

一、Docker:容器化基础

核心概念

镜像(Image)= 只读模板,包含代码 + 运行时 + 依赖 + 配置
容器(Container)= 镜像的运行实例,隔离的进程

 

Docker vs 虚拟机

虚拟机:
  硬件 → Hypervisor → [Guest OS → App]  [Guest OS → App]
  每个 VM 有完整操作系统,几GB,启动分钟级

Docker:
  硬件 → Host OS → Docker Engine → [Container][Container]
  共享宿主内核,几MB,启动秒级

 

Dockerfile 关键指令

dockerfile
FROM ubuntu:22.04          # 基础镜像

WORKDIR /app               # 工作目录

# 先复制依赖文件,利用层缓存
COPY CMakeLists.txt .
RUN apt-get install -y cmake g++ && cmake . && make

COPY . .                   # 再复制源码(源码变动不会让依赖重新构建)

EXPOSE 8080                # 声明端口(仅文档用,不自动映射)

CMD ["./game_server"]      # 容器启动命令

 

层缓存(Layer Cache) 是 Dockerfile 优化的核心:

  • 每条指令产生一个只读层
  • 某层变化 → 该层及之后所有层重建
  • 所以要把变化频繁的指令放后面(源码 > 依赖 > 基础镜像)

多阶段构建(游戏后端常用)

dockerfile
# 阶段1:编译
FROM gcc:12 AS builder
WORKDIR /src
COPY . .
RUN cmake -DCMAKE_BUILD_TYPE=Release . && make -j$(nproc)

# 阶段2:只打包可执行文件,不带编译工具链
FROM ubuntu:22.04
RUN apt-get install -y libstdc++6    # 只装运行时依赖
COPY --from=builder /src/game_server /app/game_server
CMD ["/app/game_server"]

 

编译镜像 2GB → 运行镜像可能只有 100MB,安全 + 省空间


二、Kubernetes:容器编排

为什么需要 K8s

单机 Docker 够了,但生产环境需要:

  • 跨多台机器部署容器
  • 某个容器挂了自动重启
  • 流量高峰自动扩容
  • 滚动发布不中断服务

K8s 解决这些问题。

核心架构

┌─────────────────── Kubernetes Cluster ───────────────────┐
│                                                           │
│  Control Plane(Master节点)                              │
│  ┌─────────┐  ┌──────────────┐  ┌────────┐  ┌────────┐  │
│  │ API     │  │  Scheduler   │  │  etcd  │  │ Ctrl   │  │
│  │ Server  │  │(决定放哪里) │  │(状态) │  │ Manager│  │
│  └─────────┘  └──────────────┘  └────────┘  └────────┘  │
│                                                           │
│  Worker Node 1              Worker Node 2                 │
│  ┌──────────────────┐       ┌──────────────────┐         │
│  │ kubelet          │       │ kubelet          │         │
│  │ ┌─────────────┐  │       │ ┌─────────────┐  │         │
│  │ │ Pod         │  │       │ │ Pod         │  │         │
│  │ │ [Container] │  │       │ │ [Container] │  │         │
│  │ └─────────────┘  │       │ └─────────────┘  │         │
│  └──────────────────┘       └──────────────────┘         │
└───────────────────────────────────────────────────────────┘

 

核心组件职责

组件 职责
API Server 所有操作的入口,kubectl 就是在调它
etcd 分布式 KV 存储,保存集群所有状态
Scheduler 决定 Pod 放到哪个 Node 上
Controller Manager 确保实际状态 = 期望状态(比如 Pod 挂了重建)
kubelet 每个 Node 上的代理,执行 Pod 的增删

核心资源对象

Pod — 最小调度单元,一个或多个容器的组合

yaml
# 一般不直接创建 Pod,而是通过 Deployment 管理
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: game-server
    image: ea/fc-mobile-server:v1.2.3
    ports:
    - containerPort: 8080
    resources:
      requests:
        memory: "512Mi"
        cpu: "500m"      # 0.5 核
      limits:
        memory: "1Gi"
        cpu: "1000m"     # 1 核,超过会被限流

 

Deployment — 管理无状态服务(最常用)

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: match-service
spec:
  replicas: 3                    # 期望 3 个副本
  selector:
    matchLabels:
      app: match-service
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1          # 更新时最多 1 个不可用
      maxSurge: 1                # 更新时最多多出 1 个
  template:
    spec:
      containers:
      - name: match-service
        image: ea/match-service:v2.0.0

 

StatefulSet — 管理有状态服务(Redis Cluster、数据库)

  • 每个 Pod 有固定名称(redis-0redis-1
  • 有稳定的网络标识和持久化存储
  • 游戏中的 Redis、MySQL 主从集群用这个

Service — 服务发现 + 负载均衡

yaml
apiVersion: v1
kind: Service
metadata:
  name: match-service
spec:
  selector:
    app: match-service       # 转发到带这个 label 的 Pod
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP            # 集群内部访问
  # type: LoadBalancer       # 暴露到外网,云厂商创建 LB

 

Ingress — HTTP 路由层(类似 Nginx 反向代理)

yaml
# /api/match → match-service
# /api/player → player-service
spec:
  rules:
  - host: api.fcmobile.ea.com
    http:
      paths:
      - path: /api/match
        backend:
          service:
            name: match-service
            port: 80

 


自动扩缩容(HPA)

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: match-service-hpa
spec:
  scaleTargetRef:
    kind: Deployment
    name: match-service
  minReplicas: 3
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70    # CPU 超 70% 触发扩容

 

游戏场景:赛季开始/节假日活动流量暴涨 → HPA 自动从 3 个 Pod 扩到 30 个,活动结束自动缩回,节省成本。


滚动发布与回滚

bash
# 更新镜像版本
kubectl set image deployment/match-service \
  match-service=ea/match-service:v2.1.0

# K8s 自动滚动:先起新版 Pod,健康检查通过后再删旧版
# 零停机,符合 maxUnavailable/maxSurge 约束

# 发现问题,一键回滚
kubectl rollout undo deployment/match-service

# 回滚到指定版本
kubectl rollout undo deployment/match-service --to-revision=3

 


ConfigMap & Secret

yaml
# 配置与代码分离,不用重新打镜像
apiVersion: v1
kind: ConfigMap
data:
  MATCH_TIMEOUT: "30"
  MAX_PLAYERS_PER_ROOM: "100"

---
apiVersion: v1
kind: Secret           # Base64 编码存储,实际生产用 Vault
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=

 


三、游戏后端中的实战模式

FC Mobile 可能的 K8s 架构

外网流量
    ↓
[Ingress / API Gateway]
    ↓
[Service Mesh: Istio]  ← 服务间通信、熔断、可观测性
    ↓
┌──────────────────────────────────────────┐
│  Stateless Services(Deployment)        │
│  [Match Service]  [Player Service]       │
│  [LiveOps Service] [Leaderboard Service] │
└──────────────────────────────────────────┘
    ↓
┌──────────────────────────────────────────┐
│  Stateful Services(StatefulSet)        │
│  [Redis Cluster]  [MySQL]  [Kafka]       │
└──────────────────────────────────────────┘

 

健康检查(必须掌握)

yaml
livenessProbe:           # 存活探针:失败则重启容器
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  failureThreshold: 3

readinessProbe:          # 就绪探针:失败则从 Service 摘除(不接流量)
  httpGet:
    path: /ready
    port: 8080
  periodSeconds: 3

 

关键区分

  • liveness 失败 → 重启 Pod(治疗死锁/OOM)
  • readiness 失败 → 从负载均衡摘除,但不重启(服务预热中、依赖未就绪)

四、面试高频问题

Q:Pod 和容器的区别?

Pod 是 K8s 调度的最小单位,一个 Pod 内的容器共享网络命名空间(同一 IP)和存储卷。同一 Pod 内的容器用 localhost 通信,适合紧耦合的 sidecar 模式(比如主服务 + 日志收集 agent)。

Q:Deployment vs StatefulSet 怎么选?

无状态服务(API Server、匹配服务)用 Deployment,Pod 可以随意替换。有状态服务(Redis、数据库、需要 sticky session 的游戏房间服务)用 StatefulSet,Pod 有固定身份和持久化存储。

Q:K8s 如何实现零停机发布?

RollingUpdate 策略 + readinessProbe 配合。新 Pod 就绪探针通过后才接流量,旧 Pod 才开始被删除,保证全程有足够的 ready 副本。

Q:镜像很大,CI/CD 慢怎么解决?

多阶段构建减小体积;合理安排 Dockerfile 层顺序利用缓存;用私有镜像仓库(ECR/ACR)部署在同区域;分层推送只上传变化的层。


五、一句话总结

Docker 解决”在我机器上能跑”问题,K8s 解决”在生产环境规模化、可靠运行”问题。游戏后端用 K8s 管理微服务集群,通过 Deployment + HPA 应对流量波动,通过 StatefulSet 管理有状态中间件,通过滚动发布实现不停服更新。


继续吗?可以深入 Service Mesh/IstioCI/CD Pipeline 设计,或者转向其他 EA 面试准备方向。

发表回复