Skip to content

快递分拨中心 K3s 边缘部署方案

版本: v1.0 | 日期: 2026-08-27


目录

  1. 背景与需求分析
  2. K3s 方案概述
  3. 分拨集群架构设计
  4. 服务自治与不跨区访问设计
  5. 节点规划与部署方案
  6. 网络方案
  7. 存储方案
  8. 多分拨统一管理(含统一入口发布应用 + ArgoCD vs Fleet 对比)
  9. 运维与监控
  10. 高可用与容灾
  11. 方案对比
  12. 总结与建议
  13. 分拨网络不稳定场景下的边缘方案深度对比

1. 背景与需求分析

1.1 业务场景

快递分拨中心是快递网络的核心枢纽,承担包裹的接收、分拣、中转等关键业务。每个分拨中心通常部署 8-13 台服务器,运行以下核心业务系统:

业务系统说明关键要求
分拣控制系统 (SCS)控制分拣机、传送带等自动化设备低延迟、高可用
扫描识别服务 (SRS)条码/RFID 扫描识别与数据采集实时性、高吞吐
路由调度服务 (RDS)包裹路由决策与路径规划本地决策、低延迟
视频监控服务 (VMS)场地安防监控与 AI 识别大带宽、高存储
数据采集服务 (DCS)运营数据采集与本地缓存断网续传
设备管理服务 (EMS)IoT 设备管理与状态监控实时通信
本地管理后台 (LAP)分拨中心本地管理界面独立可用

1.2 核心需求

┌─────────────────────────────────────────────────────┐
│                   核心需求矩阵                        │
├──────────────────┬──────────────────────────────────┤
│ 服务自治          │ 分拨内服务完全自治,不依赖外部区域    │
│ 不跨区访问        │ 服务间通信严格限制在本分拨内          │
│ 断网自治          │ 与总部断网后业务不中断               │
│ 资源受限          │ 8-13台机器,资源有限需轻量化          │
│ 快速部署          │ 新分拨可在数小时内完成部署            │
│ 统一管理          │ 总部可远程管理和监控所有分拨           │
│ 边缘计算          │ 部分计算任务下沉到分拨执行            │
└──────────────────┴──────────────────────────────────┘

1.3 约束条件

  • 每个分拨 8-13 台物理机/虚拟机
  • 分拨间 网络可能不互通 或通过专线有限互通
  • 分拨与总部通过 WAN 专线或 VPN 连接,带宽有限(通常 10-50Mbps)
  • 分拨内业务 必须本地闭环,不得跨区调用其他分拨服务
  • 需要支持 离线/断网运行

2. K3s 方案概述

2.1 为什么选择 K3s

K3s 是 Rancher Labs(现 SUSE)开发的轻量级 Kubernetes 发行版,专为边缘计算和资源受限环境设计。

K3s 核心优势:
├── 轻量级: 单二进制文件 < 100MB,内存占用 < 512MB
├── 全功能: 完整的 Kubernetes API 兼容性
├── 内置组件: 集成 Traefik Ingress、CoreDNS、Flannel CNI、Local-path Provisioner
├── 易部署: 单命令安装,无需复杂的集群初始化
├── 易运维: 内置自动升级、证书自动轮换
├── SQLite/etcd: 支持 SQLite (单节点) 和 etcd (高可用)
└── IoT/Edge: 专为边缘场景设计的 CNCF 认证发行版

2.2 K3s vs 标准 K8s 资源对比

指标K3s标准 K8s (kubeadm)
安装包大小~70MB~1GB+
控制面内存占用~256-512MB~1-2GB
组件数量最少 3 个进程6+ 个独立进程
启动时间< 30 秒2-5 分钟
最低 CPU1 Core2 Cores
最低内存512MB2GB
依赖组件内置所有必要组件需额外安装 CNI/CSI/Ingress 等

3. 分拨集群架构设计

3.1 整体架构(8-13 台机器)

                        ┌─────────────────────────────────────┐
                        │          分拨中心 K3s 集群            │
                        │                                     │
  ┌──── WAN/VPN ────┐  │  ┌───────────┐  ┌───────────┐      │
  │   总部管控中心    │  │  │ K3s Server│  │ K3s Server│      │
  │                 │  │  │  (Master) │  │  (Master) │      │
  │ ┌─────────────┐ │  │  │  Node-01  │  │  Node-02  │      │
  │ │ Fleet Mgr   │ │  │  │  etcd +   │  │  etcd +   │      │
  │ │ Rancher     │ │  │  │  control  │  │  control  │      │
  │ │ GitOps Repo │ │  │  └───────────┘  └───────────┘      │
  │ └─────────────┘ │  │                                     │
  └─────────────────┘  │  ┌───────────┐                      │
         ▲             │  │ K3s Server│                      │
         │ 管理通道     │  │  (Master) │                      │
         │ (仅管理面)   │  │  Node-03  │                      │
         │             │  │  etcd +   │                      │
  ─ ─ ─ ─│─ ─ ─ ─ ─ ─│  │  control  │                      │
         │             │  └───────────┘                      │
         ▼             │                                     │
  ┌─────────────────┐  │  ┌───────────┐  ┌───────────┐      │
  │  NetworkPolicy  │  │  │ K3s Agent │  │ K3s Agent │      │
  │  限制跨区流量    │  │  │  Node-04  │  │  Node-05  │      │
  └─────────────────┘  │  │  (Worker) │  │  (Worker) │      │
                        │  │  SCS/SRS  │  │  RDS/DCS  │      │
                        │  └───────────┘  └───────────┘      │
                        │                                     │
                        │  ┌───────────┐  ┌───────────┐      │
                        │  │ K3s Agent │  │ K3s Agent │      │
                        │  │  Node-06  │  │  Node-07  │      │
                        │  │  (Worker) │  │  (Worker) │      │
                        │  │  VMS/EMS  │  │  LAP/其他  │      │
                        │  └───────────┘  └───────────┘      │
                        │                                     │
                        │  ┌───────────────────────────┐      │
                        │  │  可选 Agent Nodes 8-13    │      │
                        │  │  按业务负载弹性扩展         │      │
                        │  └───────────────────────────┘      │
                        └─────────────────────────────────────┘

3.2 节点角色规划

方案 A:8 台机器(最小配置)

节点角色配置建议部署服务
node-01Server (Master) + etcd4C8G SSDK3s Server, etcd, CoreDNS
node-02Server (Master) + etcd4C8G SSDK3s Server, etcd, CoreDNS
node-03Server (Master) + etcd4C8G SSDK3s Server, etcd, CoreDNS
node-04Agent (Worker)8C16GSCS, SRS
node-05Agent (Worker)8C16GRDS, DCS
node-06Agent (Worker)8C16GVMS, EMS
node-07Agent (Worker)4C8GLAP, Nginx
node-08Agent (Worker)4C8G缓冲/备用

方案 B:10 台机器(推荐配置)

节点角色配置建议部署服务
node-01Server (Master) + etcd4C8G SSDK3s Server, etcd
node-02Server (Master) + etcd4C8G SSDK3s Server, etcd
node-03Server (Master) + etcd4C8G SSDK3s Server, etcd
node-04Agent (Worker)8C16GSCS (分拣控制)
node-05Agent (Worker)8C16GSRS (扫描识别)
node-06Agent (Worker)8C16GRDS (路由调度)
node-07Agent (Worker)4C8GDCS + EMS
node-08Agent (Worker)8C16GVMS (视频监控, GPU)
node-09Agent (Worker)4C8GLAP + Ingress
node-10Agent (Worker)4C8G中间件 (Redis/MQ)

方案 C:13 台机器(完整配置)

节点角色配置建议部署服务
node-01~03Server (Master) + etcd4C8G SSDK3s Server, etcd
node-04Agent (Worker)8C16GSCS 主实例
node-05Agent (Worker)8C16GSCS 备实例
node-06Agent (Worker)8C16GSRS
node-07Agent (Worker)8C16GRDS
node-08Agent (Worker)4C8GDCS + DCS 缓冲
node-09Agent (Worker)8C16GVMS (GPU)
node-10Agent (Worker)4C8GEMS + IoT Gateway
node-11Agent (Worker)4C8GLAP + Ingress
node-12Agent (Worker)4C8GRedis + RabbitMQ
node-13Agent (Worker)4C8G监控 + 日志 (Prometheus/Loki)

4. 服务自治与不跨区访问设计

4.1 设计原则

┌──────────────────────────────────────────────────────────┐
│                 服务自治核心原则                            │
│                                                          │
│  1. 业务闭环: 所有业务服务在分拨内完成全部调用链             │
│  2. 数据本地: 业务数据本地存储,不依赖远端数据库             │
│  3. 网络隔离: NetworkPolicy 严格限制出入流量                │
│  4. DNS 隔离: 仅解析本地服务,外部域名通过白名单控制          │
│  5. 配置独立: 每个分拨独立配置,不共享配置中心               │
│  6. 故障隔离: 单个分拨故障不影响其他分拨                     │
└──────────────────────────────────────────────────────────┘

4.2 服务调用拓扑(分拨内闭环)

                    分拨内服务调用拓扑

  ┌─────────────────────────────────────────┐
  │              Ingress (Traefik)           │
  │              本地流量入口                  │
  └─────────┬───────────────────────────────┘

  ┌─────────▼───────────────────────────────┐
  │         业务服务层 (Namespace: business)  │
  │                                         │
  │  ┌─────┐   ┌─────┐   ┌─────┐          │
  │  │ SCS │──▶│ RDS │──▶│ DCS │          │
  │  └──┬──┘   └──┬──┘   └──┬──┘          │
  │     │         │         │              │
  │     ▼         ▼         ▼              │
  │  ┌─────┐   ┌─────┐   ┌─────┐          │
  │  │ SRS │   │ EMS │   │ VMS │          │
  │  └─────┘   └─────┘   └─────┘          │
  │                                         │
  │  所有调用均在虚线框内完成                   │
  └─────────┬───────────────────────────────┘

  ┌─────────▼───────────────────────────────┐
  │       中间件层 (Namespace: middleware)    │
  │                                         │
  │  ┌───────┐  ┌─────────┐  ┌────────┐   │
  │  │ Redis │  │ RabbitMQ │  │ MySQL/ │   │
  │  │       │  │          │  │ PG     │   │
  │  └───────┘  └─────────┘  └────────┘   │
  └─────────────────────────────────────────┘

  ╳ 禁止: SCS ──▶ 其他分拨.RDS
  ╳ 禁止: SCS ──▶ 总部.API
  ✓ 允许: SCS ──▶ 本分拨.RDS
  ✓ 允许(管控): Agent ──▶ 总部.Fleet (仅管理通道)

4.3 NetworkPolicy 实现跨区访问隔离

yaml
# === 1. 默认拒绝所有入站流量 ===
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: business
spec:
  podSelector: {}
  policyTypes:
    - Ingress

# === 2. 默认拒绝所有出站流量 ===
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: business
spec:
  podSelector: {}
  policyTypes:
    - Egress

# === 3. 仅允许同命名空间内服务互访 ===
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: business
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: business
        - namespaceSelector:
            matchLabels:
              name: middleware
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress  # 允许 Ingress 控制器访问
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: business
        - namespaceSelector:
            matchLabels:
              name: middleware
    # 允许 DNS 查询
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

# === 4. 允许访问中间件层 ===
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-business-to-middleware
  namespace: business
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: middleware
      ports:
        - protocol: TCP
          port: 6379   # Redis
        - protocol: TCP
          port: 5672   # RabbitMQ
        - protocol: TCP
          port: 3306   # MySQL
        - protocol: TCP
          port: 5432   # PostgreSQL

# === 5. 禁止访问外部网络 (仅允许管理通道) ===
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-management-only
  namespace: cattle-system  # Fleet/Rancher agent 命名空间
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    # 仅允许 Fleet Agent 与总部通信
    - to:
        - ipBlock:
            cidr: <总部管理网段>/24
      ports:
        - protocol: TCP
          port: 443

4.4 DNS 隔离策略

yaml
# CoreDNS 自定义配置 - 限制 DNS 解析范围
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns-custom
  namespace: kube-system
data:
  local.override: |
    # 仅允许解析集群内部服务
    cluster.local:53 {
      errors
      cache 30
      forward . /etc/resolv.conf
    }

    # 禁止解析外部域名(白名单方式)
    # 仅允许管理相关域名
    hq.internal.com:53 {
      forward . <总部DNS服务器IP>
    }

    # 其他外部域名全部返回 NXDOMAIN
    .:53 {
      template IN A . {
        rcode NXDOMAIN
      }
    }

4.5 数据自治策略

yaml
# 本地数据同步策略 - 异步上报而非实时依赖
apiVersion: apps/v1
kind: Deployment
metadata:
  name: data-sync-agent
  namespace: business
spec:
  replicas: 1
  selector:
    matchLabels:
      app: data-sync-agent
  template:
    metadata:
      labels:
        app: data-sync-agent
    spec:
      containers:
        - name: sync-agent
          image: registry.internal/data-sync-agent:latest
          env:
            - name: SYNC_MODE
              value: "async-batch"      # 异步批量同步
            - name: LOCAL_DB_PRIMARY
              value: "true"             # 本地数据库为主
            - name: REMOTE_SYNC
              value: "best-effort"      # 尽力同步,不阻塞业务
            - name: SYNC_INTERVAL
              value: "300"              # 5分钟同步一次
            - name: HQ_ENDPOINT
              value: "https://hq.internal.com/api/v1/sync"
          volumeMounts:
            - name: local-data
              mountPath: /data/sync
      volumes:
        - name: local-data
          persistentVolumeClaim:
            claimName: sync-data-pvc

5. 节点规划与部署方案

5.1 K3s Server 节点部署

bash
#!/bin/bash
# === install-server.sh ===
# K3s Server 节点安装脚本(在 node-01, node-02, node-03 上执行)

# 系统准备
echo "=== 系统准备 ==="
swapoff -a
sed -i '/ swap / s/^/#/' /etc/fstab

# 禁用防火墙(或使用 firewalld 精确放行)
systemctl stop firewalld
systemctl disable firewalld

# 加载必要内核模块
cat > /etc/modules-load.d/k3s.conf << EOF
br_netfilter
overlay
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF

modprobe br_netfilter overlay ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack

# 内核参数优化
cat > /etc/sysctl.d/k3s.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 32768
vm.max_map_count = 262144
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288
EOF

sysctl --system

# --- node-01 (首个 Server 节点) ---
echo "=== 安装 K3s Server (首个节点) ==="
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.30.6+k3s1" sh -s - server \
  --cluster-init \
  --write-kubeconfig-mode 644 \
  --node-label "role=server" \
  --node-label "distribution-center=DC-SH-001" \
  --tls-san "10.100.1.1" \
  --tls-san "10.100.1.2" \
  --tls-san "10.100.1.3" \
  --tls-san "k3s-server.local" \
  --disable traefik \
  --disable servicelb \
  --flannel-backend none \
  --disable-network-policy \
  --kube-apiserver-arg="service-node-port-range=30000-32767" \
  --kube-controller-manager-arg="node-cidr-mask-size=24" \
  --data-dir /var/lib/rancher/k3s

# --- node-02, node-03 (加入集群的 Server 节点) ---
# 需要从 node-01 获取 token:
# cat /var/lib/rancher/k3s/server/node-token

echo "=== 安装 K3s Server (加入节点) ==="
K3S_TOKEN="<从node-01获取的token>"
K3S_SERVER_URL="https://10.100.1.1:6443"

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.30.6+k3s1" \
  K3S_TOKEN="${K3S_TOKEN}" sh -s - server \
  --server "${K3S_SERVER_URL}" \
  --node-label "role=server" \
  --node-label "distribution-center=DC-SH-001" \
  --tls-san "10.100.1.1" \
  --tls-san "10.100.1.2" \
  --tls-san "10.100.1.3" \
  --disable traefik \
  --disable servicelb \
  --flannel-backend none \
  --disable-network-policy \
  --data-dir /var/lib/rancher/k3s

5.2 K3s Agent 节点部署

bash
#!/bin/bash
# === install-agent.sh ===
# K3s Agent 节点安装脚本(在 node-04 ~ node-13 上执行)

# 系统准备(同 Server 节点)
swapoff -a
sed -i '/ swap / s/^/#/' /etc/fstab

cat > /etc/modules-load.d/k3s.conf << EOF
br_netfilter
overlay
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF

modprobe br_netfilter overlay ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack

cat > /etc/sysctl.d/k3s.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.core.somaxconn = 32768
vm.max_map_count = 262144
fs.inotify.max_user_instances = 8192
EOF

sysctl --system

# 安装 K3s Agent
K3S_TOKEN="<从node-01获取的token>"
K3S_SERVER_URL="https://10.100.1.1:6443"

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.30.6+k3s1" \
  K3S_TOKEN="${K3S_TOKEN}" K3S_URL="${K3S_SERVER_URL}" sh -s - agent \
  --node-label "role=worker" \
  --node-label "distribution-center=DC-SH-001" \
  --kubelet-arg="max-pods=110" \
  --kubelet-arg="eviction-hard=memory.available<500Mi,nodefs.available<10%" \
  --data-dir /var/lib/rancher/k3s

5.3 自动化部署(Ansible)

yaml
# === playbook: deploy-k3s-cluster.yml ===
---
- name: Deploy K3s Cluster for Distribution Center
  hosts: all
  become: true
  vars:
    k3s_version: "v1.30.6+k3s1"
    distribution_center_id: "DC-SH-001"
    k3s_data_dir: "/var/lib/rancher/k3s"

  tasks:
    - name: System preparation
      block:
        - name: Disable swap
          command: swapoff -a
        - name: Remove swap from fstab
          lineinfile:
            path: /etc/fstab
            regexp: ' swap '
            state: absent
        - name: Load kernel modules
          modprobe:
            name: "{{ item }}"
            state: present
          loop:
            - br_netfilter
            - overlay
            - ip_vs
        - name: Set sysctl parameters
          sysctl:
            name: "{{ item.key }}"
            value: "{{ item.value }}"
            sysctl_file: /etc/sysctl.d/k3s.conf
            reload: yes
          loop:
            - { key: 'net.bridge.bridge-nf-call-iptables', value: '1' }
            - { key: 'net.ipv4.ip_forward', value: '1' }
            - { key: 'vm.max_map_count', value: '262144' }

- name: Deploy K3s Server nodes
  hosts: servers
  become: true
  tasks:
    - name: Install K3s Server (first node)
      when: inventory_hostname == groups['servers'][0]
      shell: |
        curl -sfL https://get.k3s.io | \
          INSTALL_K3S_VERSION="{{ k3s_version }}" sh -s - server \
          --cluster-init \
          --write-kubeconfig-mode 644 \
          --node-label "distribution-center={{ distribution_center_id }}" \
          --disable traefik \
          --disable servicelb \
          --flannel-backend none \
          --disable-network-policy \
          --data-dir {{ k3s_data_dir }}

    - name: Get cluster token
      when: inventory_hostname == groups['servers'][0]
      slurp:
        src: "{{ k3s_data_dir }}/server/node-token"
      register: k3s_token

    - name: Install K3s Server (joining nodes)
      when: inventory_hostname != groups['servers'][0]
      shell: |
        curl -sfL https://get.k3s.io | \
          INSTALL_K3S_VERSION="{{ k3s_version }}" \
          K3S_TOKEN="{{ hostvars[groups['servers'][0]]['k3s_token']['content'] | b64decode | trim }}" \
          sh -s - server \
          --server "https://{{ hostvars[groups['servers'][0]]['ansible_default_ipv4']['address'] }}:6443" \
          --node-label "distribution-center={{ distribution_center_id }}" \
          --disable traefik \
          --disable servicelb \
          --flannel-backend none \
          --disable-network-policy \
          --data-dir {{ k3s_data_dir }}

- name: Deploy K3s Agent nodes
  hosts: agents
  become: true
  tasks:
    - name: Install K3s Agent
      shell: |
        curl -sfL https://get.k3s.io | \
          INSTALL_K3S_VERSION="{{ k3s_version }}" \
          K3S_TOKEN="{{ hostvars[groups['servers'][0]]['k3s_token']['content'] | b64decode | trim }}" \
          K3S_URL="https://{{ hostvars[groups['servers'][0]]['ansible_default_ipv4']['address'] }}:6443" \
          sh -s - agent \
          --node-label "distribution-center={{ distribution_center_id }}" \
          --data-dir {{ k3s_data_dir }}
ini
# === Ansible Inventory: hosts.ini ===
[servers]
node-01 ansible_host=10.100.1.1
node-02 ansible_host=10.100.1.2
node-03 ansible_host=10.100.1.3

[agents]
node-04 ansible_host=10.100.1.4 node_role=scs
node-05 ansible_host=10.100.1.5 node_role=srs
node-06 ansible_host=10.100.1.6 node_role=rds
node-07 ansible_host=10.100.1.7 node_role=dcs
node-08 ansible_host=10.100.1.8 node_role=vms
node-09 ansible_host=10.100.1.9 node_role=lap
node-10 ansible_host=10.100.1.10 node_role=middleware

[all:vars]
ansible_user=root
ansible_ssh_private_key_file=~/.ssh/id_rsa

6. 网络方案

6.1 CNI 选型

方案适用场景优势劣势
Flannel (默认)简单扁平网络K3s 内置、零配置无 NetworkPolicy 支持
Calico ✅ 推荐需要 NetworkPolicy完整的网络策略支持、性能好需额外安装
Cilium高级网络需求eBPF 高性能、L7 策略资源占用稍大

6.2 推荐: Calico CNI(支持 NetworkPolicy)

bash
# 禁用 K3s 默认 Flannel,使用 Calico
# 安装 K3s 时添加参数:
# --flannel-backend=none --disable-network-policy

# 安装 Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml

# 或使用 Calico operator
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml
yaml
# Calico Installation CR
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    bgp: Disabled
    ipPools:
      - blockSize: 26
        cidr: 10.42.0.0/16
        encapsulation: VXLAN
        natOutgoing: Enabled
        nodeSelector: all()

6.3 Ingress 控制器

yaml
# 使用 Nginx Ingress Controller
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: ingress-nginx
  namespace: kube-system
spec:
  repo: https://kubernetes.github.io/ingress-nginx
  chart: ingress-nginx
  targetNamespace: ingress-nginx
  valuesContent: |-
    controller:
      kind: DaemonSet
      hostNetwork: true
      nodeSelector:
        role: ingress
      config:
        use-forwarded-headers: "true"
        proxy-body-size: "50m"
      service:
        enabled: false

6.4 服务网格(可选,用于更细粒度的流量控制)

yaml
# 如果需要更严格的服务间通信控制,可部署 Linkerd (轻量级)
# Linkerd 资源占用远小于 Istio,适合边缘场景

# 通过 Linkerd 的 AuthorizationPolicy 实现更精细的服务间访问控制
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
  name: scs-to-rds-only
  namespace: business
spec:
  server:
    name: rds-grpc
  client:
    meshTLS:
      identities:
        - "scs.business.serviceaccount.identity.linkerd.cluster.local"

7. 存储方案

7.1 存储方案对比

方案类型适用场景优势劣势
Longhorn分布式块存储有状态服务CNCF 项目、Web UI、快照备份至少 3 节点
local-path本地存储单节点有状态K3s 内置、零开销无副本、无迁移
OpenEBS多种引擎灵活需求Jiva/cStor/LVM 多模式资源占用较大
NFS共享文件系统共享数据简单单点故障、性能差
Ceph分布式存储大规模功能完整过重、不适合边缘

7.2 推荐: Longhorn 分布式存储

yaml
# 安装 Longhorn
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: longhorn
  namespace: kube-system
spec:
  repo: https://charts.longhorn.io
  chart: longhorn
  targetNamespace: longhorn-system
  valuesContent: |-
    defaultSettings:
      defaultReplicaCount: 2
      defaultDataPath: /var/lib/longhorn
      backupTarget: ""
      storageOverProvisioningPercentage: 200
      storageMinimalAvailablePercentage: 10
    persistence:
      defaultClass: true
      defaultClassReplicaCount: 2
    longhornUI:
      replicas: 1
      nodeSelector:
        role: server
---
# StorageClass 定义
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: longhorn-ha
provisioner: driver.longhorn.io
parameters:
  numberOfReplicas: "2"
  staleReplicaTimeout: "2880"
  dataLocality: "best-effort"
reclaimPolicy: Retain
volumeBindingMode: Immediate
allowVolumeExpansion: true
---
# 本地存储类(用于临时数据)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-fast
provisioner: driver.longhorn.io
parameters:
  numberOfReplicas: "1"
  dataLocality: "strict-local"
reclaimPolicy: Delete

7.3 数据库部署(本地自治)

yaml
# MySQL 主从部署(分拨内自治)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: middleware
spec:
  serviceName: mysql
  replicas: 2
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: root-password
          volumeMounts:
            - name: mysql-data
              mountPath: /var/lib/mysql
          resources:
            requests:
              memory: "1Gi"
              cpu: "500m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
  volumeClaimTemplates:
    - metadata:
        name: mysql-data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: longhorn-ha
        resources:
          requests:
            storage: 50Gi
---
# Redis 集群模式
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: middleware
spec:
  serviceName: redis
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          command: ["redis-server"]
          args:
            - "--appendonly"
            - "yes"
            - "--maxmemory"
            - "1gb"
            - "--maxmemory-policy"
            - "allkeys-lru"
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: redis-data
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: redis-data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: longhorn-ha
        resources:
          requests:
            storage: 10Gi

8. 多分拨统一管理

8.1 Fleet + Rancher 多集群管理架构

┌──────────────────────────────────────────────────────────────┐
│                     总部管控中心                               │
│                                                              │
│  ┌──────────┐  ┌────────────┐  ┌──────────────────────┐    │
│  │ Rancher  │  │ Fleet      │  │ GitOps Repository    │    │
│  │ Server   │  │ Manager    │  │ (Gitea/GitLab)       │    │
│  └────┬─────┘  └─────┬──────┘  └──────────┬───────────┘    │
│       │              │                    │                  │
│       └──────────────┼────────────────────┘                  │
│                      │                                       │
└──────────────────────┼───────────────────────────────────────┘
                       │ 仅管理通道 (443/TCP)
          ┌────────────┼────────────────────────┐
          │            │                        │
          ▼            ▼                        ▼
  ┌──────────────┐ ┌──────────────┐  ┌──────────────┐
  │ 分拨 DC-001  │ │ 分拨 DC-002  │  │ 分拨 DC-003  │
  │ Fleet Agent  │ │ Fleet Agent  │  │ Fleet Agent  │
  │ (仅管理面)    │ │ (仅管理面)    │  │ (仅管理面)    │
  │              │ │              │  │              │
  │ 业务服务     │ │ 业务服务     │  │ 业务服务      │
  │ 完全自治     │ │ 完全自治     │  │ 完全自治      │
  └──────────────┘ └──────────────┘  └──────────────┘

8.2 Fleet 集群注册

yaml
# 总部 Fleet Manager 注册分拨集群
apiVersion: fleet.cattle.io/v1alpha1
kind: Cluster
metadata:
  name: dc-shanghai-001
  namespace: fleet-distribution-centers
  labels:
    region: east-china
    type: distribution-center
    city: shanghai
    tier: tier-1
spec:
  kubeConfigSecret: dc-shanghai-001-kubeconfig
---
# Fleet Bundle - 统一部署应用
apiVersion: fleet.cattle.io/v1alpha1
kind: Bundle
metadata:
  name: distribution-center-apps
  namespace: fleet-distribution-centers
spec:
  targets:
    - clusterSelector:
        matchLabels:
          type: distribution-center
      clusterGroup: distribution-centers
---
# 集群分组
apiVersion: fleet.cattle.io/v1alpha1
kind: ClusterGroup
metadata:
  name: distribution-centers
  namespace: fleet-distribution-centers
spec:
  selector:
    matchLabels:
      type: distribution-center

8.3 统一入口发布应用

核心思路: 所有分拨运行完全相同的应用版本,通过总部统一入口一次发布, Fleet + GitOps 自动分发到全部分拨集群。分拨间仅通过 ConfigMap 注入差异化配置。

8.3.1 统一发布全景流程

┌──────────────────────────────────────────────────────────────────────────┐
│                        统一发布全景流程                                    │
│                                                                          │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────────────┐   │
│  │ 开发者    │    │ CI 流水线 │    │ 镜像仓库  │    │ GitOps 仓库      │   │
│  │          │    │          │    │ (Harbor) │    │ (Gitea/GitLab)  │   │
│  └────┬─────┘    └────┬─────┘    └────┬─────┘    └────────┬─────────┘   │
│       │ git push      │               │                    │             │
│       ▼               │               │                    │             │
│  ┌─────────┐          │               │                    │             │
│  │ 代码仓库 │─────────►│  构建+测试     │                    │             │
│  │ (GitLab)│          │  镜像打包      │                    │             │
│  └─────────┘          ▼               ▼                    │             │
│                  ┌──────────┐    ┌──────────┐              │             │
│                  │ 镜像推送  │───►│ Harbor   │              │             │
│                  │ 更新标签  │    │ 多分拨同步│              │             │
│                  └──────────┘    └────┬─────┘              │             │
│                                       │                    │             │
│                  CI 自动更新           │  更新 K8s 资源文件  │             │
│                  GitOps 仓库 ─────────┼───────────────────►│             │
│                                       │                    ▼             │
│                                       │            ┌──────────┐          │
│                                       │            │  Fleet   │          │
│                                       │            │  Manager │          │
│                                       │            └────┬─────┘          │
│                                       │                 │                │
│                        ┌──────────────┼─────────────────┼────────┐       │
│                        │              │                 │        │       │
│                        ▼              ▼                 ▼        ▼       │
│                   ┌─────────┐   ┌─────────┐       ┌─────────┐           │
│                   │DC-001   │   │DC-002   │  ...  │DC-200   │           │
│                   │Fleet    │   │Fleet    │       │Fleet    │           │
│                   │Agent    │   │Agent    │       │Agent    │           │
│                   │ 拉取镜像 │   │ 拉取镜像 │       │ 拉取镜像 │           │
│                   │ 部署    │   │ 部署    │       │ 部署    │           │
│                   └─────────┘   └─────────┘       └─────────┘           │
│                                                                          │
│   一次提交 → 自动构建 → 统一镜像 → GitOps 变更 → Fleet 分发 → 全量部署     │
└──────────────────────────────────────────────────────────────────────────┘

8.3.2 CI/CD 流水线(统一入口)

yaml
# === .gitlab-ci.yml ===
# 统一入口: 一次 push 触发,构建一次镜像,更新一次 GitOps 仓库

stages:
  - build           # 构建镜像
  - test            # 自动化测试
  - push            # 推送到 Harbor
  - sync-registry   # 同步到各分拨本地镜像仓库
  - deploy          # 更新 GitOps 仓库触发部署

variables:
  HARBOR_URL: "harbor.hq.internal.com"
  GITOPS_REPO: "https://gitlab.hq.internal.com/platform/dc-gitops.git"
  APP_VERSION: "${CI_COMMIT_SHORT_SHA}"

# === Stage 1: 构建镜像 (只构建一次) ===
build:
  stage: build
  script:
    - docker build -t ${HARBOR_URL}/${APP_NAME}:${APP_VERSION} .
    - docker tag ${HARBOR_URL}/${APP_NAME}:${APP_VERSION}
                 ${HARBOR_URL}/${APP_NAME}:latest

# === Stage 2: 自动化测试 ===
test:
  stage: test
  script:
    - docker run --rm ${HARBOR_URL}/${APP_NAME}:${APP_VERSION} ./run-tests.sh

# === Stage 3: 推送到中心 Harbor ===
push:
  stage: push
  script:
    - docker push ${HARBOR_URL}/${APP_NAME}:${APP_VERSION}
    - docker push ${HARBOR_URL}/${APP_NAME}:latest

# === Stage 4: 同步镜像到各分拨本地 Registry ===
sync-registry:
  stage: sync-registry
  script:
    - |
      for DC_REGISTRY in $(cat /config/dc-registry-list.txt); do
        skopeo copy \
          --src-creds ${HARBOR_USER}:${HARBOR_PASS} \
          --dest-creds ${DC_USER}:${DC_PASS} \
          docker://${HARBOR_URL}/${APP_NAME}:${APP_VERSION} \
          docker://${DC_REGISTRY}/${APP_NAME}:${APP_VERSION} &
      done
      wait
  when: manual  # 镜像同步需人工确认

# === Stage 5: 更新 GitOps 仓库 (触发 Fleet 分发) ===
deploy:
  stage: deploy
  script:
    - git clone ${GITOPS_REPO} /tmp/dc-gitops
    - cd /tmp/dc-gitops
    # 更新应用版本(修改 kustomization 中的镜像标签)
    - |
      sed -i "s|${APP_NAME}:.*|${APP_NAME}:${APP_VERSION}|g" \
        clusters/apps/${APP_NAME}/kustomization.yaml
    # 提交变更 -> Fleet 自动检测并分发
    - git add .
    - git commit -m "release(${APP_NAME}): ${APP_VERSION} [skip ci]"
    - git push origin main
  environment:
    name: all-distribution-centers

8.3.3 Fleet Bundle:一份定义,全量分发

yaml
# === fleet.yaml ===
# Fleet Bundle 定义:一个 Bundle 覆盖所有分拨

apiVersion: fleet.cattle.io/v1alpha1
kind: Bundle
metadata:
  name: scs-service                       # 分拣控制系统
  namespace: fleet-distribution-centers
spec:
  # 目标:所有标记为 distribution-center 的集群
  targets:
    - clusterGroup: all-distribution-centers
  helm:
    chart: ./charts/scs
    valuesFiles:
      - values.yaml                      # 通用默认值

---
# 集群分组定义
apiVersion: fleet.cattle.io/v1alpha1
kind: ClusterGroup
metadata:
  name: all-distribution-centers
  namespace: fleet-distribution-centers
spec:
  selector:
    matchLabels:
      type: distribution-center           # 匹配所有分拨集群

8.3.4 统一应用定义(values.yaml)

yaml
# === values.yaml ===
# 所有分拨共享的通用默认配置 —— "同一份应用"的核心体现

global:
  registry: "registry.local:5000"        # 各分拨本地镜像仓库
  pullPolicy: IfNotPresent
  imagePullSecrets:
    - name: local-registry-secret

scs:                                      # 分拣控制系统
  image: { repository: scs, tag: "v3.2.1" }
  replicas: 2
  resources:
    requests: { cpu: "500m", memory: "512Mi" }
    limits:   { cpu: "1000m", memory: "1Gi" }
  env:
    - name: LOG_LEVEL
      value: "info"
    - name: METRICS_ENABLED
      value: "true"
  readinessProbe:
    httpGet: { path: /healthz, port: 8080 }
    initialDelaySeconds: 10
  livenessProbe:
    httpGet: { path: /healthz, port: 8080 }
    initialDelaySeconds: 30

srs:                                      # 扫描识别服务
  image: { repository: srs, tag: "v2.8.0" }
  replicas: 2
  resources:
    requests: { cpu: "1000m", memory: "1Gi" }
    limits:   { cpu: "2000m", memory: "2Gi" }

rds:                                      # 路由调度服务
  image: { repository: rds, tag: "v4.1.3" }
  replicas: 2
  resources:
    requests: { cpu: "500m", memory: "1Gi" }
    limits:   { cpu: "1000m", memory: "2Gi" }

dcs:                                      # 数据采集服务
  image: { repository: dcs, tag: "v1.9.5" }
  replicas: 1
  resources:
    requests: { cpu: "250m", memory: "256Mi" }
    limits:   { cpu: "500m", memory: "512Mi" }

vms:                                      # 视频监控服务
  image: { repository: vms, tag: "v2.3.0" }
  replicas: 1
  resources:
    requests: { cpu: "2000m", memory: "4Gi" }
    limits:   { cpu: "4000m", memory: "8Gi" }
  nodeSelector:
    gpu: "true"

ems:                                      # 设备管理服务
  image: { repository: ems, tag: "v1.5.2" }
  replicas: 1
  resources:
    requests: { cpu: "250m", memory: "256Mi" }
    limits:   { cpu: "500m", memory: "512Mi" }

lap:                                      # 本地管理后台
  image: { repository: lap, tag: "v3.0.1" }
  replicas: 1
  resources:
    requests: { cpu: "250m", memory: "256Mi" }
    limits:   { cpu: "500m", memory: "512Mi" }

middleware:
  mysql:    { image: "mysql:8.0",                  storage: "50Gi" }
  redis:    { image: "redis:7-alpine", replicas: 3, storage: "10Gi" }
  rabbitmq: { image: "rabbitmq:3.13-management-alpine", storage: "20Gi" }

8.3.5 分拨差异化配置(同应用、不同配置)

yaml
# === overlays/dc-shanghai-001/kustomization.yaml ===
# 上海 001 分拨的差异化覆盖
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../clusters/apps/            # 引用所有分拨共享的应用定义

patchesStrategicMerge:
  - dc-config-patch.yaml            # 本分拨的差异化补丁

configMapGenerator:
  - name: dc-local-config           # 本分拨的本地配置
    literals:
      - DC_ID=DC-SH-001
      - DC_NAME=上海浦东分拨中心
      - DC_REGION=east-china
      - DC_CITY=shanghai
      - SCANNER_DEVICE_ID=SCN-SH-001-A
      - SORTER_DEVICE_ID=SRT-SH-001-A
      - CONVEYOR_COUNT=12
      - LAN_GATEWAY=10.100.1.254
      - NTP_SERVER=ntp.east-china.internal
      - HQ_API_ENDPOINT=https://hq-api.internal.com
      - TIMEZONE=Asia/Shanghai
yaml
# === overlays/dc-shanghai-001/dc-config-patch.yaml ===
# 上海 001 分拨的资源覆盖(仅覆盖需要差异化的部分)

# 上海分拨设备更多,SCS 需要更多副本
apiVersion: apps/v1
kind: Deployment
metadata:
  name: scs
  namespace: business
spec:
  replicas: 3                       # 上海设备多,3副本

---
# 上海分拨 VMS 需要更大存储
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: vms
  namespace: business
spec:
  volumeClaimTemplates:
    - metadata:
        name: vms-data
      spec:
        resources:
          requests:
            storage: 200Gi          # 上海摄像头多,需要更大存储
yaml
# === overlays/dc-beijing-002/kustomization.yaml ===
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../clusters/apps/

patchesStrategicMerge:
  - dc-config-patch.yaml

configMapGenerator:
  - name: dc-local-config
    literals:
      - DC_ID=DC-BJ-002
      - DC_NAME=北京顺义分拨中心
      - DC_REGION=north-china
      - DC_CITY=beijing
      - SCANNER_DEVICE_ID=SCN-BJ-002-A
      - SORTER_DEVICE_ID=SRT-BJ-002-A
      - CONVEYOR_COUNT=8
      - LAN_GATEWAY=10.200.1.254
      - NTP_SERVER=ntp.north-china.internal
      - HQ_API_ENDPOINT=https://hq-api.internal.com
      - TIMEZONE=Asia/Shanghai

8.3.6 Helm Chart 结构(统一应用模板)

charts/
└── distribution-center/            # 统一 Helm Chart
    ├── Chart.yaml
    ├── values.yaml                 # 所有分拨共享的默认值
    ├── templates/
    │   ├── _helpers.tpl
    │   ├── namespace.yaml
    │   ├── network-policies/       # 网络策略(所有分拨一致)
    │   │   ├── default-deny.yaml
    │   │   ├── allow-internal.yaml
    │   │   └── allow-management.yaml
    │   ├── scs/                    # 分拣控制系统
    │   │   ├── deployment.yaml
    │   │   ├── service.yaml
    │   │   ├── hpa.yaml
    │   │   └── configmap.yaml
    │   ├── srs/                    # 扫描识别服务
    │   ├── rds/                    # 路由调度服务
    │   ├── dcs/                    # 数据采集服务
    │   ├── vms/                    # 视频监控服务
    │   │   ├── statefulset.yaml
    │   │   └── service.yaml
    │   ├── ems/                    # 设备管理服务
    │   ├── lap/                    # 本地管理后台
    │   │   ├── deployment.yaml
    │   │   ├── service.yaml
    │   │   └── ingress.yaml
    │   ├── middleware/             # 中间件
    │   │   ├── mysql-statefulset.yaml
    │   │   ├── redis-statefulset.yaml
    │   │   ├── rabbitmq-statefulset.yaml
    │   │   └── services.yaml
    │   ├── infrastructure/         # 基础设施
    │   │   ├── local-registry.yaml
    │   │   ├── image-prepull.yaml
    │   │   └── backup-cronjob.yaml
    │   └── monitoring/             # 监控
    │       ├── servicemonitor.yaml
    │       └── prometheus-rules.yaml
    └── ci/
        └── values-ci.yaml          # CI 测试用的 values

8.3.7 灰度发布策略(分批发布到分拨)

灰度发布核心问题:200+ 个分拨不能同时更新,必须有节奏地分批推进, 每批验证通过后才推进下一批,任一批异常立即回滚。

8.3.7.1 分拨集群分组模型
┌──────────────────────────────────────────────────────────────────┐
│                   分拨灰度分组模型(200+ 分拨)                    │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │ canary 金丝雀组 (3-5 个分拨, ~2%)                       │     │
│  │                                                         │     │
│  │ 选取原则:                                                │     │
│  │  • 覆盖不同区域 (华东/华南/华北 各1个)                    │     │
│  │  • 覆盖不同规模 (大型/中型/小型 各1个)                    │     │
│  │  • 业务量适中 (不选最忙的分拨)                           │     │
│  │  • 运维团队能快速到达现场                                │     │
│  │                                                         │     │
│  │ DC-SH-001 (上海-大型)  ──┐                               │     │
│  │ DC-GZ-003 (广州-中型)  ──┤  金丝雀: 先验证 24-48h        │     │
│  │ DC-BJ-002 (北京-大型)  ──┤                               │     │
│  │ DC-CD-007 (成都-小型)  ──┤                               │     │
│  │ DC-WH-012 (武汉-中型)  ──┘                               │     │
│  └─────────────────────────────────────────────────────────┘     │
│                           │ 验证通过                              │
│                           ▼                                      │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │ staging 灰度组 (20-30 个分拨, ~15%)                      │     │
│  │                                                         │     │
│  │ 选取原则:                                                │     │
│  │  • 按区域分批 (先华东、再华南、再华北)                    │     │
│  │  • 包含不同分拣线型号                                    │     │
│  │  • 包含高峰/平峰不同时段                                 │     │
│  │                                                         │     │
│  │ DC-SH-005, DC-SH-008, DC-NJ-015, DC-HZ-020, ...        │     │
│  └─────────────────────────────────────────────────────────┘     │
│                           │ 验证通过 (运行 48-72h)                │
│                           ▼                                      │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │ production 全量组 (剩余 170+ 分拨, ~85%)                 │     │
│  │                                                         │     │
│  │ Fleet rolloutStrategy 控制:                              │     │
│  │  • maxUnavailable: 10% (每轮最多 17 个分拨同时更新)      │     │
│  │  • maxSurge: 5%                                          │     │
│  │  • 自动逐轮推进,每轮间隔观察                             │     │
│  │                                                         │     │
│  │ DC-XXX-001 ~ DC-XXX-200 (所有剩余分拨)                   │     │
│  └─────────────────────────────────────────────────────────┘     │
└──────────────────────────────────────────────────────────────────┘
8.3.7.2 集群标签体系
yaml
# === 每个分拨集群的 Fleet Cluster 注册标签 ===
# 标签决定了该集群属于哪个灰度组

# --- 上海分拨 001 (金丝雀) ---
apiVersion: fleet.cattle.io/v1alpha1
kind: Cluster
metadata:
  name: dc-sh-001
  namespace: fleet-distribution-centers
  labels:
    # === 灰度分组标签 ===
    type: distribution-center
    release-channel: canary              # ← 决定灰度组归属

    # === 业务属性标签 ===
    dc-id: "DC-SH-001"
    dc-name: "上海浦东分拨中心"
    region: east-china                   # 区域
    province: shanghai
    scale: large                         # 大型(日均处理>50万件)
    sorter-model: cross-belt             # 交叉带分拣机

    # === 基础设施标签 ===
    node-count: "13"
    k3s-version: "v1.28.5+k3s1"
    calico-version: "v3.27"

---
# --- 广州分拨 003 (金丝雀) ---
apiVersion: fleet.cattle.io/v1alpha1
kind: Cluster
metadata:
  name: dc-gz-003
  namespace: fleet-distribution-centers
  labels:
    type: distribution-center
    release-channel: canary
    dc-id: "DC-GZ-003"
    dc-name: "广州白云分拨中心"
    region: south-china
    province: guangdong
    scale: medium                        # 中型(日均处理20-50万件)
    sorter-model: matrix
    node-count: "10"

---
# --- 其他分拨 (全量组) ---
apiVersion: fleet.cattle.io/v1alpha1
kind: Cluster
metadata:
  name: dc-sz-050
  namespace: fleet-distribution-centers
  labels:
    type: distribution-center
    release-channel: production          # ← 默认属于全量组
    dc-id: "DC-SZ-050"
    region: south-china
    scale: small
    # ...
yaml
# === ClusterGroup 定义(按标签自动分组)===

# 金丝雀组: release-channel=canary 的集群
apiVersion: fleet.cattle.io/v1alpha1
kind: ClusterGroup
metadata:
  name: canary-dcs
  namespace: fleet-distribution-centers
spec:
  selector:
    matchLabels:
      type: distribution-center
      release-channel: canary

---
# 灰度组: release-channel=staging 的集群
apiVersion: fleet.cattle.io/v1alpha1
kind: ClusterGroup
metadata:
  name: staging-dcs
  namespace: fleet-distribution-centers
spec:
  selector:
    matchLabels:
      type: distribution-center
      release-channel: staging

---
# 全量组: 所有 distribution-center 集群
apiVersion: fleet.cattle.io/v1alpha1
kind: ClusterGroup
metadata:
  name: production-dcs
  namespace: fleet-distribution-centers
spec:
  selector:
    matchLabels:
      type: distribution-center
8.3.7.3 GitOps 仓库灰度目录结构
dc-gitops/
├── apps/
│   └── scs/                           # SCS 服务(其他服务结构相同)
│       ├── base/                      # 基础定义(所有组共享)
│       │   ├── kustomization.yaml
│       │   ├── deployment.yaml
│       │   ├── service.yaml
│       │   ├── hpa.yaml
│       │   └── servicemonitor.yaml
│       │
│       ├── canary/                    # 金丝雀组版本
│       │   ├── kustomization.yaml     # 引用 base + 覆盖版本号
│       │   └── version-patch.yaml
│       │   # version-patch.yaml 内容:
│       │   # - op: replace
│       │   #   path: /spec/template/spec/containers/0/image
│       │   #   value: harbor/scs:v3.3.0    ← 新版本
│       │
│       ├── staging/                   # 灰度组版本
│       │   ├── kustomization.yaml
│       │   └── version-patch.yaml     # 与 canary 相同版本号
│       │
│       └── production/                # 全量组版本
│           ├── kustomization.yaml
│           └── version-patch.yaml     # 保持上一稳定版本
│               # value: harbor/scs:v3.2.1  ← 当前稳定版

├── clusters/
│   ├── canary-dcs/                    # 金丝雀集群引用
│   │   └── apps/
│   │       └── scs.yaml               # → 指向 apps/scs/canary/
│   │
│   ├── staging-dcs/                   # 灰度集群引用
│   │   └── apps/
│   │       └── scs.yaml               # → 指向 apps/scs/staging/
│   │
│   └── production-dcs/                # 全量集群引用
│       └── apps/
│           └── scs.yaml               # → 指向 apps/scs/production/

└── scripts/
    ├── canary-deploy.sh               # 灰度发布主脚本
    ├── canary-verify.sh               # 自动验证脚本
    ├── promote.sh                     # 升级到下一组
    └── rollback.sh                    # 回滚脚本
8.3.7.4 Fleet Bundle 灰度配置
yaml
# === Bundle: SCS 灰度发布定义 ===
apiVersion: fleet.cattle.io/v1alpha1
kind: Bundle
metadata:
  name: scs-service
  namespace: fleet-distribution-centers
spec:
  targets:
    # ─── 金丝雀组: 新版本 ───
    - name: scs-canary
      clusterGroup: canary-dcs
      helm:
        chart: charts/distribution-center
        version: "1.0.0"
        values:
          scs:
            image:
              tag: "v3.3.0"             # ← 新版本
            replicas: 2

    # ─── 灰度组: 等金丝雀通过后升级 ───
    - name: scs-staging
      clusterGroup: staging-dcs
      helm:
        chart: charts/distribution-center
        version: "1.0.0"
        values:
          scs:
            image:
              tag: "v3.2.1"             # ← 先保持稳定版,canary通过后改v3.3.0

    # ─── 全量组: 保持当前稳定版本 ───
    - name: scs-production
      clusterGroup: production-dcs
      helm:
        chart: charts/distribution-center
        version: "1.0.0"
        values:
          scs:
            image:
              tag: "v3.2.1"             # ← 稳定版本(staging通过后才改v3.3.0)

  rolloutStrategy:
    maxUnavailable: 10%                 # 全量阶段每轮最多 10%
    maxSurge: 5%
bash
#!/bin/bash
# === canary-deploy.sh ===
# 完整灰度发布脚本: 一次发布 SCS v3.2.1 -> v3.3.0

set -euo pipefail

APP="scs"
NEW_VERSION="v3.3.0"
OLD_VERSION="v3.2.1"
GITOPS_REPO="/home/ops/dc-gitops"

echo "========================================"
echo " 🚀 SCS 灰度发布: ${OLD_VERSION} → ${NEW_VERSION}"
echo "========================================"

# ─────────────────────────────────────────
# Phase 1: 金丝雀部署 (3-5 个分拨)
# ─────────────────────────────────────────
echo ""
echo "📋 Phase 1: 金丝雀部署 (canary-dcs)"
echo "   目标: DC-SH-001, DC-GZ-003, DC-BJ-002, DC-CD-007, DC-WH-012"

cd $GITOPS_REPO

# 更新 canary 目录的版本号
sed -i "s|scs:.*|scs:${NEW_VERSION}|" apps/${APP}/canary/version-patch.yaml

git add apps/${APP}/canary/
git commit -m "release(${APP}): ${NEW_VERSION} -> canary [5 clusters]"
git push origin main

echo "   ✅ Git 已提交, Fleet 将自动部署到金丝雀组"
echo "   ⏳ 等待 30 分钟让 Pod 稳定..."
sleep 30m

# ─────────────────────────────────────────
# Phase 2: 金丝雀验证 (24h 持续监控)
# ─────────────────────────────────────────
echo ""
echo "📋 Phase 2: 金丝雀验证 (24h)"
./scripts/canary-verify.sh ${APP} 24h

if [ $? -ne 0 ]; then
    echo "❌ 金丝雀验证失败! 自动回滚..."
    ./scripts/rollback.sh ${APP} canary
    exit 1
fi

echo "   ✅ 金丝雀验证通过 (24h 所有指标正常)"

# ─────────────────────────────────────────
# Phase 3: 灰度部署 (20-30 个分拨)
# ─────────────────────────────────────────
echo ""
echo "📋 Phase 3: 灰度部署 (staging-dcs)"
echo "   目标: 20-30 个分拨 (华东区域优先)"

# 将 staging 也升级到新版本
sed -i "s|scs:.*|scs:${NEW_VERSION}|" apps/${APP}/staging/version-patch.yaml

git add apps/${APP}/staging/
git commit -m "promote(${APP}): ${NEW_VERSION} canary->staging [25 clusters]"
git push origin main

echo "   ✅ Git 已提交, Fleet 部署到灰度组..."
echo "   ⏳ 等待 30 分钟..."
sleep 30m

# 灰度验证 (48h)
./scripts/canary-verify.sh ${APP} 48h --scope staging

if [ $? -ne 0 ]; then
    echo "❌ 灰度验证失败! 回滚 staging..."
    ./scripts/rollback.sh ${APP} staging
    exit 1
fi

echo "   ✅ 灰度验证通过 (48h)"

# ─────────────────────────────────────────
# Phase 4: 全量部署 (170+ 分拨)
# ─────────────────────────────────────────
echo ""
echo "📋 Phase 4: 全量部署 (production-dcs)"
echo "   目标: 剩余 170+ 分拨, maxUnavailable=10%"

sed -i "s|scs:.*|scs:${NEW_VERSION}|" apps/${APP}/production/version-patch.yaml

git add apps/${APP}/production/
git commit -m "promote(${APP}): ${NEW_VERSION} staging->production [170+ clusters]"
git push origin main

echo "   ✅ 全量部署已提交"
echo "   📊 Fleet 将按 maxUnavailable=10% 逐轮推进"
echo "   📋 监控: fleet status --bundle scs-service"
8.3.7.5 自动验证脚本(门禁检查)
bash
#!/bin/bash
# === canary-verify.sh ===
# 金丝雀/灰度自动验证: 检查所有指标是否满足门禁阈值

set -euo pipefail

APP=${1:-scs}
DURATION=${2:-24h}
SCOPE=${3:-canary}    # canary | staging

PROM_URL="http://prometheus.hq.internal.com:9090"
ALERTMANAGER="http://alertmanager.hq.internal.com:9093"

# 获取目标集群列表
if [ "$SCOPE" == "canary" ]; then
    CLUSTERS="dc-sh-001 dc-gz-003 dc-bj-002 dc-cd-007 dc-wh-012"
else
    CLUSTERS=$(fleet get clusters -l release-channel=staging -o jsonpath='{.items[*].metadata.name}')
fi

echo "========================================"
echo " 🔍 ${SCOPE} 验证开始: ${APP}"
echo "    持续监控: ${DURATION}"
echo "    目标集群: ${CLUSTERS}"
echo "========================================"

FAIL_COUNT=0
PASS_COUNT=0
START_TIME=$(date +%s)
END_TIME=$(date -d "+${DURATION}" +%s)

while [ $(date +%s) -lt $END_TIME ]; do
    echo ""
    echo "── $(date '+%Y-%m-%d %H:%M:%S') 第 $((PASS_COUNT+FAIL_COUNT+1)) 轮检查 ──"

    for CLUSTER in $CLUSTERS; do
        echo "  📡 检查集群: ${CLUSTER}"

        # ─── 1. Pod 健康检查 ───
        READY=$(kubectl --context=$CLUSTER get deploy ${APP} -n business \
            -o jsonpath='{.status.readyReplicas}/{.status.replicas}' 2>/dev/null || echo "0/0")
        echo "    Pod Ready: ${READY}"

        if [[ "$READY" == *"0/0"* ]] || [[ "$READY" == *"0/2"* ]]; then
            echo "    ❌ Pod 未就绪"
            FAIL_COUNT=$((FAIL_COUNT+1))
            continue
        fi

        # ─── 2. 重启次数 ───
        RESTARTS=$(kubectl --context=$CLUSTER get pods -n business \
            -l app=${APP} -o jsonpath='{sum(.items[*].status.containerStatuses[*].restartCount)}' 2>/dev/null)
        echo "    Restarts: ${RESTARTS}"
        if [ "${RESTARTS:-0}" -gt 5 ]; then
            echo "    ❌ 重启过多 (>5)"
            FAIL_COUNT=$((FAIL_COUNT+1))
            continue
        fi

        # ─── 3. 业务指标 (通过 Prometheus) ───
        SCAN_RATE=$(curl -s "${PROM_URL}/api/v1/query?query=\
            sum(rate(scs_scan_total{status='success',cluster='${CLUSTER}'}[5m]))\
            /sum(rate(scs_scan_total{cluster='${CLUSTER}'}[5m]))" | \
            jq -r '.data.result[0].value[1]')
        echo "    Scan Success Rate: ${SCAN_RATE}"

        if [ "$(echo "$SCAN_RATE < 0.995" | bc -l)" -eq 1 ]; then
            echo "    ❌ 扫描成功率低于 99.5%"
            FAIL_COUNT=$((FAIL_COUNT+1))
            continue
        fi

        # ─── 4. P99 延迟 ───
        P99=$(curl -s "${PROM_URL}/api/v1/query?query=\
            histogram_quantile(0.99,sum(rate(http_request_duration_seconds_bucket{\
            service='${APP}',cluster='${CLUSTER}'}[5m])) by (le))" | \
            jq -r '.data.result[0].value[1]')
        echo "    HTTP P99: ${P99}s"

        if [ "$(echo "$P99 > 0.5" | bc -l)" -eq 1 ]; then
            echo "    ❌ P99 延迟超过 500ms"
            FAIL_COUNT=$((FAIL_COUNT+1))
            continue
        fi

        # ─── 5. 告警检查 ───
        ACTIVE_ALERTS=$(curl -s "${ALERTMANAGER}/api/v2/alerts" | \
            jq "[.[] | select(.labels.cluster==\"${CLUSTER}\" and .status.state==\"active\")] | length")
        echo "    Active Alerts: ${ACTIVE_ALERTS}"

        if [ "${ACTIVE_ALERTS:-0}" -gt 0 ]; then
            echo "    ⚠️ 有活跃告警"
            FAIL_COUNT=$((FAIL_COUNT+1))
            continue
        fi

        echo "    ✅ ${CLUSTER} 通过"
        PASS_COUNT=$((PASS_COUNT+1))
    done

    # 本轮汇总
    echo ""
    echo "  📊 本轮结果: ✅ 通过 ${PASS_COUNT} | ❌ 失败 ${FAIL_COUNT}"

    # 连续失败超过阈值则终止
    if [ $FAIL_COUNT -ge 10 ]; then
        echo "  🚨 累计失败 ${FAIL_COUNT} 次,超过阈值(10),终止验证!"
        exit 1
    fi

    # 每 15 分钟检查一次
    echo "  ⏳ 下次检查: 15 分钟后"
    sleep 15m
done

echo ""
echo "========================================"
echo " ✅ 验证完成: ${DURATION} 监控结束"
echo "    通过: ${PASS_COUNT} | 失败: ${FAIL_COUNT}"
echo "========================================"
exit 0
8.3.7.6 每阶段精确回滚
bash
#!/bin/bash
# === rollback.sh ===
# 精确回滚: 指定回滚哪个组的哪个应用
# 用法: ./rollback.sh <app> <group>
# 示例: ./rollback.sh scs canary
#       ./rollback.sh scs staging
#       ./rollback.sh scs production

set -euo pipefail

APP=${1:?"用法: ./rollback.sh <app> <group>"}
GROUP=${2:?"用法: ./rollback.sh <app> <group> (canary|staging|production)"}
GITOPS_REPO="/home/ops/dc-gitops"

OLD_VERSION=$(yq ".apps.${APP}.previous" ${GITOPS_REPO}/version-registry.yaml)

echo "⏪ 回滚: ${APP} @ ${GROUP} → ${OLD_VERSION}"

cd $GITOPS_REPO

# 只回滚指定组,其他组不受影响
sed -i "s|${APP}:.*|${APP}:${OLD_VERSION}|" apps/${APP}/${GROUP}/version-patch.yaml

git add apps/${APP}/${GROUP}/
git commit -m "rollback(${APP}): ${GROUP} -> ${OLD_VERSION} [EMERGENCY]"
git push origin main

echo "✅ 回滚已提交, Fleet 自动回滚 ${GROUP} 组"

case $GROUP in
    canary)
        echo "📋 影响: 3-5 个金丝雀分拨 (staging/production 不受影响)"
        ;;
    staging)
        echo "📋 影响: 20-30 个灰度分拨 (canary 保持新版本用于排查)"
        ;;
    production)
        echo "📋 影响: 170+ 分拨 (Fleet 按 maxUnavailable=10% 逐轮回滚)"
        ;;
esac
8.3.7.7 灰度可观测性面板
yaml
# === Grafana 灰度发布看板 ===
apiVersion: v1
kind: ConfigMap
metadata:
  name: canary-release-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "true"
data:
  canary-release.json: |
    {
      "title": "灰度发布看板",
      "panels": [
        {
          "title": "版本分布",
          "type": "stat",
          "targets": [{ "expr": "count(kube_deployment_labels{deployment='scs'}) by (release_channel)" }]
        },
        {
          "title": "扫描成功率 (按组对比)",
          "type": "timeseries",
          "targets": [{ "expr": "avg by (release_channel) (rate(scs_scan_total{status='success'}[5m]) / rate(scs_scan_total[5m]))" }]
        },
        {
          "title": "P99 延迟 (canary vs production)",
          "type": "timeseries",
          "targets": [{ "expr": "histogram_quantile(0.99, sum by (release_channel, le) (rate(http_request_duration_seconds_bucket{service='scs'}[5m])))" }]
        },
        {
          "title": "Fleet Bundle 部署状态",
          "type": "table",
          "targets": [{ "expr": "fleet_bundle_status", "format": "table", "instant": true }]
        }
      ]
    }
┌──────────────────────────────────────────────────────────────────┐
│                    灰度发布看板 (实时)                             │
│                                                                  │
│  版本分布:   canary=5 ✅  |  staging=25 ✅  |  production=170 ✅  │
│                                                                  │
│  ┌──────────── 扫描成功率对比 ─────────────────────────────┐      │
│  │ canary:     ████████████████████ 99.7%  ✅              │      │
│  │ staging:    ████████████████████ 99.6%  ✅              │      │
│  │ production: ████████████████████ 99.8%  (稳定版基线)    │      │
│  └────────────────────────────────────────────────────────┘      │
│                                                                  │
│  ┌──────────── P99 延迟对比 ──────────────────────────────┐      │
│  │ canary:     ▁▂▃▃▄▃▃▂▃  120ms  ✅                      │      │
│  │ production: ▁▁▂▂▂▁▁▂▁  95ms   (基线)                   │      │
│  │ → canary 比 production 高 26% (可接受)                 │      │
│  └────────────────────────────────────────────────────────┘      │
│                                                                  │
│  ┌──────────── Fleet Bundle 状态 ─────────────────────────┐      │
│  │ Bundle      │ canary  │ staging │ production │ Total   │      │
│  │ scs-service │ 5/5  ✅ │ 25/25 ✅│ 170/170 ✅ │ 200/200 │      │
│  └────────────────────────────────────────────────────────┘      │
└──────────────────────────────────────────────────────────────────┘
8.3.7.8 灰度发布 Checklist
┌──────────────────────────────────────────────────────────────────┐
│                SCS v3.3.0 灰度发布 Checklist                      │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  📦 发布前准备                                                    │
│  □ Code Review 通过 + 单元测试通过                               │
│  □ 集成测试在测试环境验证                                        │
│  □ 镜像已推送 Harbor + 同步各分拨本地 Registry                   │
│  □ 回滚脚本已准备                                                │
│  □ 值班运维已通知                                                │
│  □ 金丝雀分拨避开大促高峰期                                       │
│                                                                  │
│  🐤 Phase 1: 金丝雀 (5 分拨, 24h)                                │
│  □ Fleet Bundle: 5/5 Ready                                       │
│  □ Pod 全部 Running + Ready                                      │
│  □ 扫描成功率 >= 99.5% 持续 24h                                  │
│  □ P99 <= 500ms + 无容器重启 > 2次/h                            │
│  □ 无活跃告警 + 业务人员确认正常                                  │
│                                                                  │
│  📊 Phase 2: 灰度 (25 分拨, 48h)                                 │
│  □ Fleet Bundle: 25/25 Ready                                     │
│  □ 覆盖高峰时段验证                                              │
│  □ 中间件负载正常 (MySQL 连接 <70%, Redis 内存 <80%)             │
│  □ 无数据丢失/错件投诉                                          │
│                                                                  │
│  🚀 Phase 3: 全量 (170+ 分拨)                                    │
│  □ Fleet rollout 逐轮完成                                        │
│  □ 全部 200+ 分拨 Ready                                         │
│  □ version-registry.yaml 更新                                    │
│  □ 发布通知已发送 + 72h 后确认无长尾问题                         │
└──────────────────────────────────────────────────────────────────┘
8.3.7.9 灰度发布时间线总览
Day 0 (周一 10:00)             Day 1 (周二 10:00)           Day 3 (周四 10:00)
│                              │                            │
│ Phase 1: 金丝雀              │ Phase 2: 灰度              │ Phase 3: 全量
│ 5 个分拨                     │ 25 个分拨                  │ 170+ 分拨
│                              │                            │
│ 10:00 部署完成               │ 10:00 部署完成             │ 10:00 Fleet 开始逐轮
│ 10:30 开始监控               │ 10:30 开始监控             │ 每轮 ~17 个分拨
│      ↓                       │      ↓                     │ 间隔 15 分钟
│ 24h 持续观察                 │ 48h 持续观察               │ ~12:30 全部完成
│      ↓                       │      ↓                     │      ↓
│ 周二 10:00 判定              │ 周四 10:00 判定            │ 持续监控 72h
│ ✅通过 → 进入 Phase 2       │ ✅通过 → 进入 Phase 3     │      ↓
│ ❌失败 → rollback canary     │ ❌失败 → rollback staging  │ 发布报告 + 通知全员
│                              │                            │
│ 失败自动回滚:                │ 失败自动回滚:              │ 完成后更新:
│ ./rollback.sh scs canary     │ ./rollback.sh scs staging  │ version-registry.yaml

8.3.8 版本管理与一键回滚

yaml
# === version-registry.yaml ===
# 统一版本注册表
apiVersion: v1
kind: ConfigMap
metadata:
  name: version-registry
  namespace: fleet-distribution-centers
data:
  versions.yaml: |
    apps:
      scs:  { current: v3.2.1, previous: v3.2.0, canary: v3.3.0, min: v3.0.0, status: completed }
      srs:  { current: v2.8.0, previous: v2.7.3, min: v2.5.0, status: completed }
      rds:  { current: v4.1.3, previous: v4.1.2, min: v4.0.0, status: staging }
      dcs:  { current: v1.9.5, previous: v1.9.4, min: v1.8.0, status: completed }
      vms:  { current: v2.3.0, previous: v2.2.1, min: v2.0.0, status: completed }
      ems:  { current: v1.5.2, previous: v1.5.1, min: v1.4.0, status: completed }
      lap:  { current: v3.0.1, previous: v3.0.0, min: v2.8.0, status: completed }
      middleware.mysql: "8.0.36"
      middleware.redis: "7.2.4"
      middleware.rabbitmq: "3.13.1"
bash
#!/bin/bash
# === rollback.sh ===
# 一键回滚:所有分拨统一回滚到上一版本

APP_NAME=$1
if [ -z "$APP_NAME" ]; then
  echo "Usage: ./rollback.sh <app-name>"
  exit 1
fi

echo "=== 回滚 ${APP_NAME} ==="
PREVIOUS_VERSION=$(yq ".apps.${APP_NAME}.previous" version-registry.yaml)
echo "回滚到: ${PREVIOUS_VERSION}"

cd /tmp/dc-gitops
sed -i "s|${APP_NAME}:.*|${APP_NAME}:${PREVIOUS_VERSION}|g" \
  clusters/apps/${APP_NAME}/kustomization.yaml

git add .
git commit -m "rollback(${APP_NAME}): -> ${PREVIOUS_VERSION} [URGENT]"
git push origin main

echo "✅ 回滚已提交,Fleet 自动分发到所有分拨"

8.3.9 完整 GitOps 仓库结构

dc-gitops/                          # GitOps 仓库根目录

├── version-registry.yaml           # 统一版本注册表
├── fleet.yaml                      # Fleet 全局配置

├── charts/                         # Helm Charts
│   └── distribution-center/        # 统一应用 Chart
│       ├── Chart.yaml
│       ├── values.yaml             # 所有分拨共享默认值
│       └── templates/
│           ├── scs/ srs/ rds/ dcs/ vms/ ems/ lap/
│           ├── middleware/         # MySQL + Redis + RabbitMQ
│           ├── infrastructure/     # 镜像仓库 + 备份
│           ├── network-policies/   # 网络隔离策略
│           └── monitoring/         # 监控告警

├── fleet-bundles/                  # Fleet Bundle 定义
│   ├── base-system.yaml
│   ├── business-apps.yaml
│   ├── middleware.yaml
│   └── monitoring.yaml

├── cluster-groups/                 # 集群分组(灰度发布用)
│   ├── canary-dcs.yaml             # 金丝雀组 (3-5个)
│   ├── staging-dcs.yaml            # 灰度组 (20-30个)
│   └── production-dcs.yaml         # 全量组 (所有)

└── overlays/                       # 分拨差异化配置
    ├── dc-shanghai-001/
    │   ├── kustomization.yaml
    │   ├── dc-config-patch.yaml    # 资源覆盖(副本数、存储等)
    │   └── values-override.yaml    # Helm values 覆盖
    ├── dc-beijing-002/
    │   └── ...
    └── ...                         # 200+ 分拨配置

8.3.10 发布流程总结

┌─────────────────────────────────────────────────────────────────┐
│              统一发布流程(一次发布、全量分发)                     │
│                                                                 │
│  Step 1: 开发者提交代码                                          │
│     git push -> GitLab                                          │
│                                                                 │
│  Step 2: CI 自动构建 (仅构建一次)                                 │
│     构建镜像 -> 推送 Harbor -> 同步各分拨本地 Registry            │
│                                                                 │
│  Step 3: CI 更新 GitOps 仓库                                     │
│     修改 version tag -> git commit -> git push                  │
│                                                                 │
│  Step 4: Fleet 检测变更,自动分发                                 │
│     Fleet Manager -> Fleet Agent (各分拨) -> kubectl apply      │
│                                                                 │
│  Step 5: 灰度验证                                               │
│     金丝雀(3-5个) -> 灰度(20-30个) -> 全量                       │
│                                                                 │
│  Step 6: 全量生效                                                │
│     200+ 分拨运行相同版本的应用                                    │
│                                                                 │
│  回滚: ./rollback.sh scs -> GitOps 变更 -> Fleet 全局回滚        │
│                                                                 │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  ✅ 一份代码 -> 一个镜像 -> 所有分拨                     │     │
│  │  ✅ 一次提交 -> Fleet 自动分发 -> 无需逐分拨操作         │     │
│  │  ✅ 差异仅通过 ConfigMap overlay 注入                    │     │
│  │  ✅ 灰度发布: canary -> staging -> production           │     │
│  │  ✅ 一键回滚: 修改 GitOps 即触发 Fleet 全局回滚          │     │
│  └────────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────────┘

8.4 ArgoCD vs Fleet 深度对比(统一入口选型)

当前方案选用 Fleet 作为多集群分发工具。ArgoCD 作为最流行的 GitOps 工具, 在功能成熟度和社区生态上有明显优势。本节客观对比两者在快递分拨场景的适用性, 并提供 ArgoCD 替代方案,供选型决策参考。

8.4.1 架构模型对比

┌──────────────────────────────────────────────────────────────────┐
│                    Fleet 架构(Pull 模型)                         │
│                                                                  │
│   总部 Fleet Manager                分拨 K3s 集群                 │
│   ┌────────────────┐               ┌─────────────────────┐      │
│   │ Bundle 定义    │ ◄── 轮询拉取 ──│ Fleet Agent         │      │
│   │ (Git Watch)   │               │ (本地轻量进程)       │      │
│   │               │               │                     │      │
│   │ 不做 apply    │ ── 返回 Bundle │ 拉取 Bundle         │      │
│   │ 不做推送      │    内容 ──────►│ 本地 kubectl apply   │      │
│   └────────────────┘               │ 操作本地 API Server  │      │
│                                    └─────────────────────┘      │
│   通信方向: 分拨 Agent → 总部 Manager (出站连接)                  │
│   断网时: Agent 缓存上次 Bundle,业务不受影响                      │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│                 ArgoCD 架构(Push 模型 / 中心模式)                │
│                                                                  │
│   总部 ArgoCD Server                分拨 K3s 集群                 │
│   ┌────────────────┐               ┌─────────────────────┐      │
│   │ Application    │               │                     │      │
│   │ Controller     │               │  K3s API Server     │      │
│   │               │               │  (被动接收)          │      │
│   │ Git Watch     │               │                     │      │
│   │ Diff + Sync   │── kubectl ────►│  接收 apply 请求     │      │
│   │               │   apply       │                     │      │
│   │ 持有所有集群   │               └─────────────────────┘      │
│   │ kubeconfig    │                                              │
│   └────────────────┘                                             │
│   通信方向: 总部 ArgoCD → 分拨 API Server (入站连接)              │
│   断网时: ArgoCD 无法到达分拨 API Server,同步失败                │
└──────────────────────────────────────────────────────────────────┘

8.4.2 核心能力对比

对比维度FleetArgoCD
通信模型Pull(Agent 拉取)Push(中心推送)
断网行为Agent 缓存上次状态,业务不受影响无法 apply,Sync 失败,需重连后重试
网络方向分拨出站 → 总部(易穿透 NAT/防火墙)总部入站 → 分拨(需端口映射/VPN)
多集群管理Bundle + ClusterGroup 原生支持ApplicationSet 强大但需额外安装
资源占用(中心)Fleet Manager ~200MBArgoCD Server + Controller ~1-2GB
资源占用(边缘)Fleet Agent ~50MB无需部署(中心推送模式)
K8s API 兼容完整完整
K3s 原生集成✅ K3s/Rancher 生态内置⚠️ 需额外安装和配置
UI/DashboardRancher UI(功能全但偏重)ArgoCD UI(轻量、直观、受欢迎)
社区成熟度⭐⭐⭐ SUSE 维护⭐⭐⭐⭐⭐ CNCF 毕业项目
模板能力Helm + KustomizeHelm + Kustomize + Jsonnet + Ksonnet
Sync 策略自动/手动自动/手动/Sync Wave/Retry/Hook
健康检查Bundle 级别状态细粒度 Resource Health + Custom Health
回滚能力Git revert + Fleet 重分发UI 一键回滚 / CLI argocd rollback
200+ 集群Bundle 天然一对多ApplicationSet 支持,但中心压力大
配置复杂度低(K3s 内置)中(需安装 + 注册集群 + 配置 RBAC)

8.4.3 网络不稳定场景关键差异

┌──────────────────────────────────────────────────────────────────┐
│         分拨 WAN 断线 2 小时期间的表现对比                         │
├──────────────────┬───────────────────┬───────────────────────────┤
│ 场景             │ Fleet             │ ArgoCD (中心推送模式)      │
├──────────────────┼───────────────────┼───────────────────────────┤
│ 已有 Pod 运行    │ ✅ 正常           │ ✅ 正常                    │
│ 新建 Pod 调度    │ ✅ 本地 etcd 可用 │ ✅ 本地 etcd 可用           │
│ 配置更新         │ ✅ 本地可操作     │ ✅ 本地可操作               │
│ Git 变更同步     │ ⏸ 暂停,恢复后补  │ ❌ 失败,需手动重试         │
│ Agent 状态       │ ✅ Agent 本地运行 │ N/A (无边缘 Agent)         │
│ 网络恢复后       │ ✅ 自动拉取同步   │ ⚠️ 需检查 Sync 状态        │
│ 大量事件堆积     │ ✅ 无(本地自治)   │ ⚠️ Sync 队列可能堆积       │
│ 中心服务器压力   │ ✅ 低(被动响应)   │ ⚠️ 200集群重连后并发 Sync  │
└──────────────────┴───────────────────┴───────────────────────────┘
ArgoCD 200+ 集群中心模式的潜在问题:

  1. Application Controller 瓶颈
     • 默认单 Controller 管理所有集群
     • 200+ 集群 = 200+ Application = 大量 Watch 连接
     • 推荐分片: 每 Controller 管理 ~50 集群
     • 需要 4+ 个 Application Controller 实例

  2. API Server 连接管理
     • ArgoCD 持有 200+ 个 kubeconfig
     • 每个集群的 Watch 连接需持续维持
     • 网络抖动时大量连接断开/重连,消耗中心资源

  3. 网络穿透问题
     • ArgoCD 需要能访问每个分拨的 K3s API Server (6443)
     • 分拨通常在 NAT/防火墙后面,入站端口受限
     • 需要 VPN 或端口映射,增加网络复杂度

  4. 重连风暴
     • WAN 恢复后 200+ 集群同时重连
     • ArgoCD 尝试并发 Sync 所有 OutOfSync 的 Application
     • 可能造成中心 CPU/内存尖峰

  Fleet 为什么没有这些问题?
     • Agent 在分拨本地,只需出站连接(易穿透 NAT)
     • 每个 Agent 独立工作,无中心瓶颈
     • 恢复后 Agent 逐个拉取,天然错峰

8.4.4 ArgoCD 用于分拨场景的两种模式

模式 A:中心 ArgoCD(传统模式)
yaml
# === 总部 ArgoCD ApplicationSet ===
# 自动为所有分拨集群生成 Application
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: distribution-center-apps
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            type: distribution-center
  template:
    metadata:
      name: 'dc-{{name}}-apps'
    spec:
      project: distribution-centers
      source:
        repoURL: https://gitlab.hq.internal.com/platform/dc-gitops.git
        targetRevision: main
        path: charts/distribution-center
        helm:
          valueFiles:
            - values.yaml
          parameters:
            - name: global.dcId
              value: '{{metadata.labels.dc-id}}'
            - name: global.dcName
              value: '{{metadata.labels.dc-name}}'
      destination:
        server: '{{server}}'
        namespace: business
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        retry:
          limit: 5
          backoff:
            duration: 30s
            factor: 2
            maxDuration: 5m
bash
# 注册分拨集群到中心 ArgoCD(每个分拨执行一次)
argocd cluster add dc-shanghai-001 \
  --name dc-sh-001 \
  --label type=distribution-center \
  --label dc-id=DC-SH-001 \
  --label region=east-china
# ⚠️ 中心 ArgoCD 需能访问分拨 K3s API Server (6443)
# ⚠️ 分拨在 NAT 后时需 VPN 或端口映射
模式 B:分拨本地 ArgoCD(Agent 模式)
yaml
# === 每个分拨本地部署 ArgoCD (类似 Fleet Agent) ===
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dc-local-apps
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://gitlab.hq.internal.com/platform/dc-gitops.git
    targetRevision: main
    path: charts/distribution-center
    helm:
      valueFiles:
        - values.yaml
        - overlays/dc-shanghai-001/values-override.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: business
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
模式 B 资源开销(每个分拨):
┌────────────────────────────────────────┐
│  argocd-server           ~128MB (可选) │
│  argocd-repo-server      ~128MB        │
│  argocd-app-controller   ~256MB        │
│  argocd-redis            ~64MB         │
│  ──────────────────────────────        │
│  总计: ~576MB - 1GB                    │
│  vs Fleet Agent: ~50MB                 │
│  差距: 10-20 倍                        │
└────────────────────────────────────────┘

8.4.5 功能需求匹配度评估

评分标准: 5=完全满足  4=基本满足  3=部分满足  2=勉强可用  1=不满足

                        Fleet    ArgoCD(中心)  ArgoCD(本地)
                       ┌──────┬──────────────┬──────────────┐
断网自治               │  5   │      3       │      5       │
网络穿透简易性         │  5   │      2       │      4       │
200+集群管理           │  5   │      3       │      3       │
统一发布(同应用)       │  4   │      5       │      4       │
灰度发布               │  4   │      5       │      4       │
回滚便捷性             │  4   │      5       │      4       │
UI/Dashboard           │  3   │      5       │      5       │
资源效率(边缘)         │  5   │      5       │      2       │
资源效率(中心)         │  5   │      2       │      5       │
K3s生态集成            │  5   │      3       │      3       │
配置复杂度             │  5   │      3       │      3       │
社区支持               │  3   │      5       │      5       │
Sync策略灵活性         │  3   │      5       │      5       │
健康检查粒度           │  3   │      5       │      5       │
重连后自动恢复         │  5   │      3       │      5       │
                       ├──────┼──────────────┼──────────────┤
总分                   │ 65   │     54       │     62       │
                       └──────┴──────────────┴──────────────┘
                        ✅ 最优    ⚠️ 中等       ✅ 良好

8.4.6 选型决策矩阵

选 Fleet 如果:
├── 分拨网络不稳定(WAN 频繁断线/高延迟)
├── 分拨在 NAT/防火墙后,入站端口受限
├── 分拨数量 > 100 个
├── 已经使用 Rancher 管理 K3s 集群
├── 边缘资源极度受限,Agent 越轻越好
└── 希望最低配置复杂度,开箱即用

选 ArgoCD (中心模式) 如果:
├── 分拨网络稳定(专线/SD-WAN,极少断线)
├── 分拨数量 < 50 个
├── 团队熟悉 ArgoCD,已有运维经验
├── 需要精细的 Sync 策略(Sync Wave、Hook、Health Check)
├── 需要优秀的 Web UI 进行可视化管理
└── 需要 Jsonnet/Ksonnet 等高级模板能力

选 ArgoCD (本地模式) 如果:
├── 分拨网络不稳定 + 团队偏好 ArgoCD
├── 可接受每分拨额外 ~1GB 资源开销
├── 分拨机器充足(13台配置)
├── 需要 ArgoCD 的高级功能 + 本地自治
└── 有能力维护 200+ 个 ArgoCD 实例

混合方案:
├── Fleet 负责 Bundle 分发(轻量、断网友好)
├── ArgoCD (中心) 负责复杂业务编排(网络稳定时)
└── 或 Fleet 分发 + ArgoCD UI 仅做可视化监控

8.4.7 ArgoCD 完整替代配置(如选用 ArgoCD)

yaml
# === 总部 ArgoCD 高可用部署(200 集群规模)===
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
  name: hq-argocd
  namespace: argocd
spec:
  ha:
    enabled: true

  # Application Controller 分片(200 集群建议 4 分片)
  controller:
    replicas: 4
    resources:
      requests: { cpu: "500m", memory: "1Gi" }
      limits:   { cpu: "1000m", memory: "2Gi" }
    env:
      - name: ARGOCD_SHARDING_ALGORITHM
        value: "round-robin"

  server:
    replicas: 2
    resources:
      requests: { cpu: "250m", memory: "256Mi" }

  repo:
    replicas: 2
    resources:
      requests: { cpu: "250m", memory: "256Mi" }

  applicationSet:
    enabled: true

---
# === ArgoCD Project: 分拨项目隔离 ===
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: distribution-centers
  namespace: argocd
spec:
  description: "所有快递分拨中心应用"
  sourceRepos:
    - 'https://gitlab.hq.internal.com/platform/dc-gitops.git'
  destinations:
    - namespace: 'business'
      server: '*'
    - namespace: 'middleware'
      server: '*'
    - namespace: 'monitoring'
      server: '*'
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace

---
# === 灰度: 金丝雀分拨自动同步 ===
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: dc-canary
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            type: distribution-center
            release-channel: canary
  template:
    metadata:
      name: '{{metadata.labels.dc-id}}-apps'
    spec:
      # ... 同 ApplicationSet 配置
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
bash
#!/bin/bash
# === ArgoCD 灰度发布脚本 ===
VERSION=$1
STAGE=$2  # canary | staging | production

case $STAGE in
  canary)
    argocd app list -l dc-release-channel=canary | \
      awk '{print $1}' | tail -n +2 | \
      xargs -I{} argocd app set {} --helm-set scs.image.tag=${VERSION}
    argocd app list -l dc-release-channel=canary | \
      awk '{print $1}' | tail -n +2 | \
      xargs -I{} argocd app sync {}
    echo "✅ 金丝雀集群已更新到 ${VERSION}"
    ;;
  production)
    argocd app list -l dc-release-channel=production | \
      awk '{print $1}' | tail -n +2 | \
      xargs -I{} argocd app set {} --helm-set scs.image.tag=${VERSION}
    # 分批 Sync(每批 20 个,间隔 60 秒)
    argocd app list -l dc-release-channel=production | \
      awk '{print $1}' | tail -n +2 | \
      xargs -n 20 -I{} sh -c "argocd app sync {}; sleep 60"
    echo "✅ 全量发布完成"
    ;;
esac

8.4.8 最终建议

本方案选择 Fleet 的核心原因:

  1. 网络稳定性是第一优先级
     分拨 WAN 频繁断线 → Pull 模型天然断网自治
     Fleet Agent 出站连接 → 无需穿透分拨防火墙

  2. 200+ 集群规模
     Fleet Bundle 一对多天然支持 → 无中心瓶颈
     ArgoCD 中心模式需 4+ Controller 分片 → 架构复杂度翻倍

  3. K3s 生态一致性
     Fleet 是 K3s/Rancher 原生工具 → 零额外安装
     Agent ~50MB vs ArgoCD 本地模式 ~1GB → 10倍资源差距

  4. 运维团队规模
     2-3 人运维 200+ 分拨 → Fleet 自动分发,几乎零运维
     ArgoCD 200+ 集群需要专人维护 Controller/集群注册/Sync 状态

  但如果你:
  ├── 网络条件好(SD-WAN/专线,极少断线)→ ArgoCD 中心模式 OK
  ├── 分拨数量 < 50 → ArgoCD 中心模式完全胜任
  ├── 团队已熟练使用 ArgoCD → 可平滑迁移,学习成本为零
  └── 需要精细 Sync 控制 → ArgoCD 的 Sync Wave/Hook 更强

  推荐:
  ├── 主方案: Fleet (轻量、断网友好、K3s 原生)
  ├── 可选: 总部额外部署 ArgoCD 做可视化监控面板
  └── 未来: 如 ArgoCD 推出原生 Pull 模式 Agent,可重新评估

9. 运维与监控

9.1 监控栈(分拨本地)

yaml
# Prometheus + Grafana 轻量监控栈
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: kube-prometheus-stack
  namespace: kube-system
spec:
  repo: https://prometheus-community.github.io/helm-charts
  chart: kube-prometheus-stack
  targetNamespace: monitoring
  valuesContent: |-
    prometheus:
      prometheusSpec:
        retention: 7d
        storageSpec:
          volumeClaimTemplate:
            spec:
              storageClassName: longhorn-ha
              resources:
                requests:
                  storage: 20Gi
        resources:
          requests:
            memory: 512Mi
            cpu: 250m
          limits:
            memory: 1Gi
            cpu: 500m
    grafana:
      persistence:
        enabled: true
        size: 5Gi
        storageClassName: longhorn-ha
      resources:
        requests:
          memory: 128Mi
          cpu: 100m
        limits:
          memory: 256Mi
          cpu: 200m
    alertmanager:
      enabled: true
      alertmanagerSpec:
        storage:
          volumeClaimTemplate:
            spec:
              storageClassName: longhorn-ha
              resources:
                requests:
                  storage: 5Gi

9.2 日志收集(本地轻量方案)

yaml
# 使用 Fluent Bit(轻量级日志收集)
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: monitoring
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush        5
        Daemon       Off
        Log_Level    info

    [INPUT]
        Name         tail
        Path         /var/log/containers/business-*.log
        Parser       docker
        Tag          business.*
        Mem_Buf_Limit 5MB

    [FILTER]
        Name         kubernetes
        Match        business.*
        Merge_Log    On

    [OUTPUT]
        Name         file
        Match        business.*
        Path         /var/log/collected
        File         business-logs.log

    # 可选: 发送到本地 Loki
    [OUTPUT]
        Name         loki
        Match        business.*
        Host         loki.monitoring.svc.cluster.local
        Port         3100
        Labels       job=fluentbit, dc=${DC_ID}

9.3 告警规则

yaml
# 关键告警规则
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: distribution-center-alerts
  namespace: monitoring
spec:
  groups:
    - name: cluster-health
      rules:
        - alert: NodeDown
          expr: up{job="node-exporter"} == 0
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: "节点 {{ $labels.instance }} 不可达"

        - alert: HighCPUUsage
          expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "节点 {{ $labels.instance }} CPU 使用率超过 85%"

        - alert: HighMemoryUsage
          expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "节点 {{ $labels.instance }} 内存使用率超过 90%"

        - alert: PodRestartTooMany
          expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Pod {{ $labels.pod }} 1小时内重启超过5次"

        - alert: PVSpaceLow
          expr: kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes * 100 < 15
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "PV {{ $labels.persistentvolumeclaim }} 剩余空间不足 15%"

        - alert: CrossRegionTrafficDetected
          expr: increase(calico_denied_connections_total[5m]) > 0
          for: 1m
          labels:
            severity: critical
          annotations:
            summary: "检测到跨区网络流量被拦截"

10. 高可用与容灾

10.1 控制面高可用

K3s 控制面 HA (3 Server 节点 + etcd 集群):

┌─────────┐     ┌─────────┐     ┌─────────┐
│ Server  │     │ Server  │     │ Server  │
│ node-01 │◄───►│ node-02 │◄───►│ node-03 │
│         │     │         │     │         │
│ etcd    │◄───►│ etcd    │◄───►│ etcd    │
│ leader  │     │ follower│     │ follower│
└────┬────┘     └────┬────┘     └────┬────┘
     │               │               │
     └───────────────┼───────────────┘

              ┌──────┴──────┐
              │   VIP/LB    │  <- kube-vip 或 HAProxy
              │ 10.100.1.100│
              └─────────────┘

10.2 kube-vip 实现 API Server 高可用

yaml
# kube-vip 部署(DaemonSet 模式)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-vip
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: kube-vip
  template:
    metadata:
      labels:
        app: kube-vip
    spec:
      hostNetwork: true
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
      nodeSelector:
        role: server
      containers:
        - name: kube-vip
          image: ghcr.io/kube-vip/kube-vip:v0.8.0
          args:
            - manager
          env:
            - name: vip_arp
              value: "true"
            - name: port
              value: "6443"
            - name: vip_interface
              value: eth0
            - name: vip_cidr
              value: "32"
            - name: cp_enable
              value: "true"
            - name: cp_namespace
              value: kube-system
            - name: svc_enable
              value: "true"
            - name: vip_leaderelection
              value: "true"
            - name: address
              value: "10.100.1.100"

10.3 备份与恢复策略

bash
#!/bin/bash
# === backup.sh ===
# 分拨集群备份脚本(每日执行)

BACKUP_DIR="/backup/k3s"
DATE=$(date +%Y%m%d_%H%M%S)
DC_ID="DC-SH-001"

mkdir -p ${BACKUP_DIR}

# 1. etcd 快照备份
k3s etcd-snapshot save \
  --name "etcd-${DC_ID}-${DATE}" \
  --dir ${BACKUP_DIR}/etcd

# 2. Kubernetes 资源备份
for ns in business middleware monitoring ingress-nginx; do
  kubectl get all -n ${ns} -o yaml > ${BACKUP_DIR}/resources-${ns}-${DATE}.yaml
  kubectl get cm,secret,pvc -n ${ns} -o yaml > ${BACKUP_DIR}/config-${ns}-${DATE}.yaml
done

# 3. Longhorn 卷备份
kubectl get volumes.longhorn.io -n longhorn-system -o yaml > ${BACKUP_DIR}/longhorn-volumes-${DATE}.yaml

# 4. 压缩备份
tar -czf ${BACKUP_DIR}/backup-${DC_ID}-${DATE}.tar.gz ${BACKUP_DIR}/

# 5. 保留最近 7 天备份
find ${BACKUP_DIR} -name "backup-*.tar.gz" -mtime +7 -delete

# 6. 可选:上传到总部备份服务器
# rsync -avz ${BACKUP_DIR}/backup-${DC_ID}-${DATE}.tar.gz backup-server:/backup/${DC_ID}/

echo "Backup completed: ${BACKUP_DIR}/backup-${DC_ID}-${DATE}.tar.gz"

10.4 断网自治保障

yaml
# 镜像预热策略 - 确保所有必要镜像在本地
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: image-prepull
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: image-prepull
  template:
    metadata:
      labels:
        app: image-prepull
    spec:
      initContainers:
        - name: prepull-scs
          image: registry.local/scs:latest
          command: ["sh", "-c", "exit 0"]
        - name: prepull-srs
          image: registry.local/srs:latest
          command: ["sh", "-c", "exit 0"]
        - name: prepull-rds
          image: registry.local/rds:latest
          command: ["sh", "-c", "exit 0"]
        - name: prepull-mysql
          image: mysql:8.0
          command: ["sh", "-c", "exit 0"]
        - name: prepull-redis
          image: redis:7-alpine
          command: ["sh", "-c", "exit 0"]
      containers:
        - name: pause
          image: registry.k8s.io/pause:3.9
          resources:
            requests:
              cpu: 1m
              memory: 1Mi
---
# 本地镜像仓库(每个分拨部署)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: local-registry
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: local-registry
  template:
    metadata:
      labels:
        app: local-registry
    spec:
      containers:
        - name: registry
          image: registry:2
          ports:
            - containerPort: 5000
          volumeMounts:
            - name: registry-data
              mountPath: /var/lib/registry
      volumes:
        - name: registry-data
          persistentVolumeClaim:
            claimName: registry-data-pvc

11. 方案对比

11.1 容器编排平台对比

对比维度K3s ✅标准 K8s (kubeadm)KubeEdgeMicroK8s
安装复杂度⭐ 极简,单命令⭐⭐⭐ 复杂⭐⭐ 中等⭐⭐ 简单
资源占用⭐ 极低 (~256MB)⭐⭐⭐ 高 (~2GB)⭐⭐ 低 (Cloud+Edge)⭐⭐ 低 (~512MB)
包大小⭐ ~70MB⭐⭐⭐ ~1GB+⭐⭐ 中等⭐⭐ ~200MB
K8s API 兼容✅ 完全兼容✅ 原生⚠️ 部分兼容✅ 完全兼容
高可用✅ 内置 etcd HA✅ 支持⚠️ 依赖 Cloud✅ 支持
NetworkPolicy✅ (Calico)✅ 原生⚠️ 受限✅ 支持
离线部署✅ 离线安装包⚠️ 需准备⚠️ 依赖⚠️ snap 限制
自动升级✅ 内置⚠️ 需额外工具⚠️ 手动✅ snap 自动
多集群管理✅ Fleet/Rancher✅ Rancher✅ 原生⚠️ 有限
社区生态⭐⭐⭐ CNCF/SUSE⭐⭐⭐⭐ CNCF⭐⭐ CNCF⭐⭐ Canonical
边缘场景适配⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
国内镜像✅ 可用✅ 可用✅ 可用❌ snap 被墙
适合 8-13 台⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

11.2 K3s vs KubeEdge 深度对比

对比维度K3s (每分拨独立集群)KubeEdge (Cloud-Edge 架构)
架构模式每个分拨独立完整 K8s 集群中心 Cloud + 边缘 EdgeCore
控制面位置分拨本地(3 节点 HA)总部/云端集中
断网运行✅ 完全自治,无影响⚠️ 边缘可运行,但无法调度新 Pod
服务自治✅ 天然隔离,完整 K8s 能力⚠️ 受限于 Cloud-Edge 通信
资源开销Server: ~512MB, Agent: ~128MBCloud: ~2GB, EdgeCore: ~128MB
设备管理通过 K8s CRD 实现✅ 内置设备管理 (DeviceTwin)
消息通道K8s API ServerWebSocket / QUIC
OTA 升级K3s 系统升级 + K8s 滚动更新EdgeCore 远程升级
GPU 支持✅ 原生支持⚠️ 需要额外配置
Pod 数量上限每节点 110 (标准)每节点无硬限制
运维复杂度⭐⭐ (每集群独立运维)⭐⭐⭐ (集中管理但架构复杂)
网络要求分拨内 LAN 即可需要 Cloud-Edge 稳定连接
适用分拨数量10-500+100-10000+

11.3 架构方案对比

方案一:每分拨独立 K3s 集群(推荐 ✅)

总部 ──(管理通道)──┬── 分拨A [K3s 独立集群 8台]
                   ├── 分拨B [K3s 独立集群 10台]
                   └── 分拨C [K3s 独立集群 13台]

优势:

  • ✅ 天然服务自治,物理隔离
  • ✅ 断网完全不影响业务
  • ✅ 单分拨故障不影响其他分拨
  • ✅ 部署和运维相对简单
  • ✅ K8s 完整能力,无功能限制

劣势:

  • ❌ 每个分拨需要 3 台 Server 节点(控制面开销)
  • ❌ 多集群管理需要额外工具(Fleet/Rancher)
  • ❌ 大规模分拨数量时管理复杂度上升

方案二:KubeEdge Cloud-Edge 架构

总部 [Cloud (K8s)] ──┬── 分拨A [EdgeCore 8台]
                     ├── 分拨B [EdgeCore 10台]
                     └── 分拨C [EdgeCore 13台]

优势:

  • ✅ 集中式控制面,统一管理
  • ✅ 边缘节点资源开销更小(无 etcd)
  • ✅ 适合超大规模(数千个分拨)

劣势:

  • ❌ 断网后无法调度新 Pod
  • ❌ 控制面单点依赖总部
  • ❌ 服务自治需要额外设计
  • ❌ 架构复杂度高,排障困难
  • ❌ 部分 K8s 功能在 Edge 侧不可用

方案三:混合架构(K3s + KubeEdge)

总部 [Rancher/Fleet] ──┬── 大型分拨 [K3s 完整集群 13台]
                       ├── 中型分拨 [K3s 精简集群 8台]
                       └── 小型站点 [KubeEdge EdgeCore 2-3台]

11.4 成本对比(以 100 个分拨为例)

成本项K3s 方案KubeEdge 方案
每分拨控制面节点3 台 (4C8G)0 (共享总部)
总部控制面Rancher 集群 (3台 8C16G)K8s 集群 (5台 16C32G)
每分拨 Worker 节点5-10 台8-13 台
网络带宽需求低 (仅管理通道)中 (Cloud-Edge 通信)
运维人力2-3 人3-5 人
故障影响范围单分拨可能波及所有分拨
100 分拨总服务器~1000-1300 台~850-1300 台 + 总部

12. 总结与建议

12.1 推荐方案

推荐: 每分拨独立 K3s 集群 + Fleet 多集群管理 + Calico CNI + Longhorn 存储

┌─────────────────────────────────────────────────────┐
│                 推荐技术栈                            │
│                                                     │
│  容器编排:    K3s v1.30.x (LTS)                     │
│  CNI:         Calico (NetworkPolicy 支持)           │
│  Ingress:     Nginx Ingress Controller              │
│  存储:        Longhorn (分布式块存储)                 │
│  镜像仓库:    Harbor (总部) + Registry (分拨本地)     │
│  多集群管理:  Fleet + Rancher                        │
│  GitOps:     Fleet + Kustomize                      │
│  监控:        Prometheus + Grafana (本地精简版)       │
│  日志:        Promtail + Loki (本地) / 异步上报       │
│  备份:        etcd 快照 + Velero                     │
│  VIP:         kube-vip (API Server HA)              │
│  部署工具:    Ansible                                │
│                                                     │
└─────────────────────────────────────────────────────┘

12.2 选型决策树

                    分拨数量?
                   /         \
             <=50个           >50个
              /                \
        需要断网自治?      每分拨>=8台机器?
        /        \           /         \
      是          否       是           否
     /            |        |            |
   K3s         K3s      K3s        KubeEdge
   (独立集群)  (独立集群) (独立集群)  (EdgeCore)

12.3 关键设计决策

决策点选择理由
集群模式每分拨独立集群天然实现服务自治与断网隔离
Server 节点数3 台 (etcd HA)满足 etcd 多数派要求,允许 1 节点故障
CNICalico必须支持 NetworkPolicy 实现网络隔离
存储LonghornCNCF 项目,轻量且支持多副本
DNS 隔离CoreDNS 自定义防止服务解析到其他区域
镜像管理本地 Registry确保断网后可拉取镜像
多集群管理FleetK3s 原生集成,支持 GitOps
数据同步异步批量本地数据优先,异步上报总部

12.4 实施路线图

Phase 1: 试点验证 (2-4 周)
├── 选择 1-2 个分拨作为试点
├── 部署 K3s 集群 + 基础网络策略
├── 验证服务自治与断网场景
└── 性能压测与调优

Phase 2: 标准化 (4-6 周)
├── 标准化 Ansible 部署脚本
├── 标准化 GitOps 仓库结构
├── 部署 Fleet + Rancher 管理平台
├── 完善监控、日志、告警体系
└── 制定运维手册与应急预案

Phase 3: 规模化推广 (持续)
├── 批量部署新分拨
├── 存量分拨迁移
├── 持续优化与自动化
└── 建立分拨运维知识库

13. 分拨网络不稳定场景下的边缘方案深度对比

快递分拨中心网络环境复杂:WAN 链路易受施工断网、运营商抖动、带宽拥塞、高延迟等影响, 分拨内 LAN 也可能因设备老化、交换机故障导致局部不稳定。 本章节从网络不稳定这一核心痛点出发,深度对比各边缘方案的实际表现。

13.1 分拨典型网络故障模型

┌─────────────────────────────────────────────────────────────────┐
│                 分拨网络故障分类与概率模型                         │
├─────────────┬───────────────────────────────┬────────┬──────────┤
│ 故障类型     │ 典型场景                       │ 频率   │ 持续时间  │
├─────────────┼───────────────────────────────┼────────┼──────────┤
│ WAN 断线    │ 光纤施工挖断、运营商设备故障      │ 月1-3次 │ 30分-4h  │
│ WAN 高延迟  │ 链路拥塞、路由绕转、跨境链路      │ 日均   │ 数分钟   │
│ WAN 带宽抖动│ 业务高峰带宽打满、QoS降级        │ 日均   │ 分钟级   │
│ 间歇性丢包  │ 运营商链路质量差、光衰            │ 频繁   │ 秒级抖动  │
│ VPN 隧道断开│ IPSec/SSL 隧道超时或重协商       │ 周1-2次 │ 1-30分钟  │
│ DNS 解析失败│ 远端 DNS 服务器不可达            │ 月2-5次 │ 分钟级   │
│ 分拨内 LAN  │ 交换机端口故障、网线松动          │ 月1-2次 │ 分钟级   │
│ 网络分区    │ VLAN 配置错误、STP 环路          │ 年2-3次 │ 10-60分钟│
└─────────────┴───────────────────────────────┴────────┴──────────┘

13.2 对比方案清单

方案类型控制面位置代表产品
A. K3s 独立集群完整边缘集群分拨本地K3s (推荐)
B. KubeEdgeCloud-Edge总部云端KubeEdge
C. K0s完整边缘集群分拨本地K0s (Mirantis)
D. 纯裸金属 + Ansible无容器编排N/AAnsible + systemd
E. Docker Swarm轻量编排分拨本地Docker Swarm
F. OpenYurtCloud-Edge总部云端OpenYurt (阿里)

13.3 网络不稳定场景逐项对比

场景一:WAN 完全断线(光纤挖断 / 运营商中断)

┌──────────────────────────────────────────────────────────────────────┐
│ WAN 完全断线:分拨与总部网络完全中断,持续 30分钟 ~ 4小时              │
├──────────┬────────────┬──────────────┬──────────────────────────────┤
│ 方案     │ 业务影响    │ 控制面影响    │ 具体表现                     │
├──────────┼────────────┼──────────────┼──────────────────────────────┤
│ A. K3s   │ ✅ 零影响  │ ✅ 完全自治   │ etcd 本地、API Server 本地    │
│          │            │              │ Pod 调度/重启/扩缩容全部正常    │
│          │            │              │ 仅需断线期间不升级镜像版本      │
├──────────┼────────────┼──────────────┼──────────────────────────────┤
│ B.KubeEdge│ ⚠️ 有限   │ ❌ 控制面失联 │ Cloud Hub 断开后:             │
│          │            │              │ • 已有 Pod 继续运行            │
│          │            │              │ • 无法创建/删除/更新 Pod       │
│          │            │              │ • 无法更新 ConfigMap/Secret    │
│          │            │              │ • ServiceAccount token 可能过期 │
│          │            │              │ • 节点故障时无法自动漂移 Pod    │
├──────────┼────────────┼──────────────┼──────────────────────────────┤
│ C. K0s   │ ✅ 零影响  │ ✅ 完全自治   │ 与 K3s 类似,本地完整控制面    │
│          │            │              │ 但资源占用高于 K3s             │
├──────────┼────────────┼──────────────┼──────────────────────────────┤
│ D.裸金属  │ ✅ 零影响  │ N/A          │ 无控制面概念,进程直接跑        │
│          │            │              │ 但故障恢复全靠手动/systemd      │
├──────────┼────────────┼──────────────┼──────────────────────────────┤
│ E.Swarm  │ ✅ 零影响  │ ✅ 本地自治   │ Manager 在本地,调度正常        │
│          │            │              │ 但无 K8s 生态,运维工具匮乏     │
├──────────┼────────────┼──────────────┼──────────────────────────────┤
│ F.OpenYurt│ ⚠️ 有限   │ ❌ 控制面失联 │ 类似 KubeEdge:                │
│          │            │              │ • YurtHub 缓存可维持部分读操作  │
│          │            │              │ • 但写操作(调度/更新)完全不可用  │
│          │            │              │ • 自治能力依赖 NodePool 配置    │
└──────────┴────────────┴──────────────┴──────────────────────────────┘

结论:K3s / K0s / Swarm 在 WAN 断线时业务完全不受影响;KubeEdge / OpenYurt 会丧失调度能力。

场景二:WAN 高延迟(200ms-2000ms)/ 间歇性丢包

┌──────────────────────────────────────────────────────────────────────┐
│ WAN 高延迟 / 间歇性丢包:网络通但质量差,RTT 波动大                    │
├──────────┬─────────────┬─────────────────────────────────────────────┤
│ 方案     │ 业务影响     │ 具体分析                                    │
├──────────┼─────────────┼─────────────────────────────────────────────┤
│ A. K3s   │ ✅ 无影响   │ 控制面全在本地 LAN (< 1ms),WAN 质量不影响    │
│          │             │ 仅 Fleet Agent 心跳可能延迟,但不影响业务      │
│          │             │ 镜像拉取走本地 Registry,不依赖远端            │
├──────────┼─────────────┼─────────────────────────────────────────────┤
│ B.KubeEdge│ ⚠️ 中度影响│ Cloud-Edge 通信走 WebSocket/QUIC:           │
│          │             │ • CloudHub 心跳超时 -> 节点状态频繁翻转        │
│          │             │ • Pod 事件上报堆积 -> 云端视图与实际不一致      │
│          │             │ • 配置下发延迟 -> 新配置无法及时生效            │
│          │             │ • 高延迟下 QUIC 优于 WebSocket 但仍有明显影响   │
├──────────┼─────────────┼─────────────────────────────────────────────┤
│ C. K0s   │ ✅ 无影响   │ 与 K3s 相同                                  │
├──────────┼─────────────┼─────────────────────────────────────────────┤
│ D.裸金属  │ ✅ 无影响   │ 无远端依赖                                   │
├──────────┼─────────────┼─────────────────────────────────────────────┤
│ E.Swarm  │ ✅ 无影响   │ 本地 Manager 节点间通信不受 WAN 影响          │
├──────────┼─────────────┼─────────────────────────────────────────────┤
│ F.OpenYurt│ ⚠️ 中度影响│ YurtHub 本地缓存机制可缓解,但:              │
│          │             │ • 缓存过期后读操作也会失败                     │
│          │             │ • 写操作持续失败导致状态不一致                  │
└──────────┴─────────────┴─────────────────────────────────────────────┘

场景三:WAN 带宽拥塞(分拨业务高峰期打满带宽)

┌──────────────────────────────────────────────────────────────────────┐
│ WAN 带宽拥塞:10-50Mbps 带宽被视频监控等业务占满                       │
├──────────┬──────────────┬────────────────────────────────────────────┤
│ 方案     │ 带宽消耗      │ 具体分析                                   │
├──────────┼──────────────┼────────────────────────────────────────────┤
│ A. K3s   │ < 1Mbps     │ 仅 Fleet Agent 心跳 + 数据异步上报           │
│          │ (管理面极低)  │ 所有业务流量在本地 LAN 闭环                  │
│          │              │ 可通过 QoS 策略保障管理通道优先级             │
├──────────┼──────────────┼────────────────────────────────────────────┤
│ B.KubeEdge│ 2-5Mbps    │ Cloud-Edge 持续通信:                        │
│          │ (持续通信)    │ • 节点状态上报 (每10s)                      │
│          │              │ • Pod 事件流 (持续)                         │
│          │              │ • ConfigMap Watch (持续)                    │
│          │              │ • 日志/指标上报                              │
│          │              │ 带宽争抢可能导致控制面超时                    │
├──────────┼──────────────┼────────────────────────────────────────────┤
│ C. K0s   │ < 1Mbps     │ 与 K3s 相同                                 │
├──────────┼──────────────┼────────────────────────────────────────────┤
│ D.裸金属  │ 0 (无管理)   │ 无远端管理通信(但也意味着无法远程管理)      │
├──────────┼──────────────┼────────────────────────────────────────────┤
│ E.Swarm  │ < 1Mbps     │ 仅远程监控数据上报                            │
├──────────┼──────────────┼────────────────────────────────────────────┤
│ F.OpenYurt│ 2-5Mbps    │ 与 KubeEdge 类似,持续 Cloud-Edge 通信       │
└──────────┴──────────────┴────────────────────────────────────────────┘

场景四:VPN 隧道断开后重连(IPSec/SSL 隧道不稳定)

┌──────────────────────────────────────────────────────────────────────┐
│ VPN 隧道断开 -> 重连(持续 1-30 分钟)                                │
├──────────┬───────────────────────────────────────────────────────────┤
│ 方案     │ 恢复表现                                                  │
├──────────┼───────────────────────────────────────────────────────────┤
│ A. K3s   │ ✅ 无感恢复                                               │
│          │ • VPN 断开期间业务完全正常                                 │
│          │ • VPN 恢复后 Fleet Agent 自动重新连接总部                   │
│          │ • 无需任何人工干预,无状态积压问题                           │
│          │ • 异步数据上报自动补传                                      │
├──────────┼───────────────────────────────────────────────────────────┤
│ B.KubeEdge│ ⚠️ 恢复后需修复状态                                      │
│          │ • 断开期间:事件堆积在 EdgeCore 本地队列                    │
│          │ • 重连后:大量事件批量上报,可能造成 Cloud 端瞬时压力         │
│          │ • 配置不一致:断开期间的云端变更需重新同步                   │
│          │ • 极端情况:长时间断线后需手动触发全量同步                    │
├──────────┼───────────────────────────────────────────────────────────┤
│ C. K0s   │ ✅ 无感恢复(同 K3s)                                     │
├──────────┼───────────────────────────────────────────────────────────┤
│ D.裸金属  │ ⚠️ 恢复后需手动检查                                       │
│          │ • 无自动状态同步机制                                       │
│          │ • 需运维人员逐个分拨检查服务状态                            │
├──────────┼───────────────────────────────────────────────────────────┤
│ E.Swarm  │ ✅ 基本无感                                               │
│          │ • 本地服务不受影响                                        │
│          │ • 远程管理恢复后需手动刷新状态                              │
├──────────┼───────────────────────────────────────────────────────────┤
│ F.OpenYurt│ ⚠️ 恢复后需修复状态                                      │
│          │ • YurtHub 缓存机制可部分缓解                               │
│          │ • 但长时间断线后缓存过期,恢复后需重新同步                   │
└──────────┴───────────────────────────────────────────────────────────┘

场景五:分拨内 LAN 局部网络故障

┌──────────────────────────────────────────────────────────────────────┐
│ 分拨内 LAN 局部故障:交换机端口故障、网线松动、VLAN 配置错误            │
├──────────┬───────────────────────────────────────────────────────────┤
│ 方案     │ 自愈能力                                                  │
├──────────┼───────────────────────────────────────────────────────────┤
│ A. K3s   │ ⭐⭐⭐⭐⭐ 优秀                                          │
│          │ • K8s liveness/readiness probe 自动检测服务健康             │
│          │ • Pod 自动重启(restartPolicy: Always)                    │
│          │ • 节点 NotReady 后 Pod 自动调度到其他健康节点               │
│          │ • Service 自动剔除不健康的 Endpoint                         │
│          │ • NetworkPolicy 可配合 Calico 做故障隔离                    │
├──────────┼───────────────────────────────────────────────────────────┤
│ B.KubeEdge│ ⭐⭐⭐ 一般                                             │
│          │ • 边缘侧 Pod 健康检查可用                                  │
│          │ • 但节点故障后无法跨节点调度(无本地控制面)                  │
│          │ • Pod 只能重启在原节点,节点宕机则 Pod 丢失                  │
│          │ • 需要 Cloud 端介入才能做节点级故障转移                      │
├──────────┼───────────────────────────────────────────────────────────┤
│ C. K0s   │ ⭐⭐⭐⭐⭐ 优秀(同 K3s)                                │
├──────────┼───────────────────────────────────────────────────────────┤
│ D.裸金属  │ ⭐⭐ 较差                                               │
│          │ • 依赖 systemd 自动重启(仅进程级)                         │
│          │ • 无节点级故障转移能力                                     │
│          │ • 无服务发现,故障节点上的服务不可达                         │
│          │ • 需人工登录机器排查和恢复                                  │
├──────────┼───────────────────────────────────────────────────────────┤
│ E.Swarm  │ ⭐⭐⭐⭐ 良好                                            │
│          │ • 支持服务自愈和节点级故障转移                              │
│          │ • 但策略粒度不如 K8s                                       │
│          │ • 无 NetworkPolicy 等价物                                  │
├──────────┼───────────────────────────────────────────────────────────┤
│ F.OpenYurt│ ⭐⭐⭐ 一般                                             │
│          │ • NodePool 内可做故障转移                                  │
│          │ • 但依赖 Cloud 端调度决策                                  │
│          │ • 断网时退化为仅本地重启                                    │
└──────────┴───────────────────────────────────────────────────────────┘

13.4 综合评分卡(网络不稳定维度)

评分标准: 5=优秀  4=良好  3=一般  2=较差  1=很差

                    A.K3s   B.KubeEdge  C.K0s   D.裸金属  E.Swarm  F.OpenYurt
                   ┌──────┬──────────┬──────┬────────┬───────┬──────────┐
WAN 完全断线       │  5   │    2     │  5   │   5    │   4   │    2     │
WAN 高延迟/丢包    │  5   │    3     │  5   │   5    │   5   │    3     │
WAN 带宽拥塞       │  5   │    3     │  5   │   5    │   5   │    3     │
VPN 断开恢复       │  5   │    2     │  5   │   2    │   4   │    3     │
LAN 局部故障自愈   │  5   │    3     │  5   │   2    │   4   │    3     │
DNS 故障容忍       │  5   │    3     │  5   │   4    │   4   │    3     │
网络分区恢复       │  4   │    2     │  4   │   3    │   3   │    2     │
镜像拉取(断网)     │  5   │    4     │  5   │   5    │   4   │    4     │
配置更新(断网)     │  5   │    1     │  5   │   3    │   4   │    2     │
远程管理恢复       │  5   │    3     │  5   │   1    │   3   │    3     │
                   ├──────┼──────────┼──────┼────────┼───────┼──────────┤
总分               │ 49   │   26     │ 49   │  35    │  40   │   28     │
                   └──────┴──────────┴──────┴────────┴───────┴──────────┘
                    ✅ 最优   ⚠️ 较差   ✅ 优    ⚠️ 一般   ⚠️ 中等   ⚠️ 较差

13.5 K3s vs K0s(两个本地集群方案的细微差异)

虽然 K3s 和 K0s 在网络不稳定场景下得分相同,但实际工程中有以下关键差异:

对比维度K3sK0s
二进制大小~70MB~180MB
内存占用(控制面)~256-512MB~400-700MB
安装命令curl | sh 单行需先下载再执行
默认 CNIFlannelCalico ✅ (天然支持 NetworkPolicy)
默认存储local-pathOpenEBS (更重)
etcd HA--cluster-init 一行需配置 controller 多实例
离线安装单文件 + air-gap images单文件 + air-gap images
自动升级system-upgrade-controllerk0sctl (外部工具)
Calico 适配需替换 Flannel开箱即用 ✅
社区中文资料⭐⭐⭐⭐⭐ 丰富⭐⭐ 较少
8-13台资源占用更低 ✅略高
网络不稳定评分49/5049/50

结论: K0s 在 Calico 开箱即用方面有优势,但 K3s 在资源占用、部署便捷性、 社区生态方面更胜一筹。对于快递分拨场景,K3s 综合优势更明显。

13.6 Cloud-Edge 方案(KubeEdge / OpenYurt)网络不稳定根因分析

┌────────────────────────────────────────────────────────────────┐
│         Cloud-Edge 架构在网络不稳定下的根本矛盾                  │
│                                                                │
│   ┌──────────┐          WAN/VPN          ┌──────────┐         │
│   │  Cloud   │◄═══════════╪══════════════►│  Edge    │         │
│   │  (总部)   │          (不稳定)          │  (分拨)   │         │
│   │          │                            │          │         │
│   │ K8s API  │──── 控制面通信 ────────────│ kubelet  │         │
│   │ etcd     │◄──── 状态上报 ────────────│ EdgeCore │         │
│   │ Scheduler│──── 调度决策 ────────────│          │         │
│   │          │◄──── 配置下发 ────────────│          │         │
│   └──────────┘                            └──────────┘         │
│                                                                │
│   根因: 控制面与数据面物理分离                                    │
│                                                                │
│   问题1: 控制面依赖网络                                         │
│     • kubelet -> API Server 注册/心跳 (每10s)                   │
│     • 事件上报、状态同步持续进行                                  │
│     • 网络断开 = 控制面失联                                      │
│                                                                │
│   问题2: 调度依赖中心                                            │
│     • Pod 的创建/删除/漂移都需要 Cloud 端 Scheduler              │
│     • 断网后新 Pod 无法调度                                      │
│     • 节点故障后 Pod 无法迁移                                    │
│                                                                │
│   问题3: 配置更新依赖网络                                        │
│     • ConfigMap/Secret 更新需要 Cloud 下发                       │
│     • 断网期间配置变更无法生效                                    │
│     • 紧急配置回滚操作不可用                                     │
│                                                                │
│   问题4: 恢复后状态一致性                                        │
│     • 断网期间边缘产生的事件在本地排队                             │
│     • 重连后大量事件涌入 Cloud 端                                │
│     • 可能导致 Cloud 端 OOM 或处理超时                           │
│                                                                │
│   K3s 独立集群为什么没有这些问题?                                 │
│     ✅ 控制面在本地 LAN (< 1ms 延迟)                             │
│     ✅ 调度器在本地,随时可用                                     │
│     ✅ 配置存储在本地 etcd,随时可更新                            │
│     ✅ 无需与远端保持状态一致                                     │
└────────────────────────────────────────────────────────────────┘

13.7 KubeEdge / OpenYurt 的自治缓解措施评估

Cloud-Edge 方案也提供了一些自治机制,以下是这些机制的实际效果评估:

KubeEdge 自治机制

机制原理实际效果局限性
MetaServerEdgeCore 本地提供类 API Server⭐⭐⭐ 有限仅支持读操作,不支持写/调度
ServiceAccount Token 缓存本地缓存 Token⭐⭐⭐ 有限Token 过期后无法刷新
EdgeMesh边缘侧服务发现⭐⭐⭐⭐ 较好仅限服务间通信,不涉及调度
DeviceTwin设备状态本地缓存⭐⭐⭐⭐ 较好仅适用于 IoT 设备管理
SQLite 本地存储边缘数据本地持久化⭐⭐⭐ 有限无法替代 etcd 的完整能力

OpenYurt 自治机制

机制原理实际效果局限性
YurtHub本地代理 + 缓存 K8s API 响应⭐⭐⭐⭐ 较好缓存过期后失效;写操作始终不可用
NodePool节点池内自治⭐⭐⭐ 有限仍需 Cloud 端做跨池调度
Yurt Tunnel反向隧道保持连接⭐⭐⭐ 有限网络完全断开时无效
Edge Autonomy节点级自治标记⭐⭐⭐ 有限自治模式下丧失集群级协调能力
自治机制有效性总结(WAN 断线期间):

                    读API  写API  调度  配置更新  故障转移  服务发现
KubeEdge MetaServer  ✅     ❌     ❌     ❌       ❌       ⚠️
KubeEdge EdgeMesh    ❌     ❌     ❌     ❌       ❌       ✅
OpenYurt YurtHub     ⚠️     ❌     ❌     ❌       ❌       ✅
K3s 独立集群          ✅     ✅     ✅     ✅       ✅       ✅

13.8 网络不稳定对业务影响的量化分析

以某快递公司 200 个分拨、日均处理 5000 万件包裹为基准:

┌──────────────────────────────────────────────────────────────────────┐
│           网络故障期间的业务损失估算 (单次 WAN 断线 2 小时)             │
├──────────┬──────────────┬──────────────┬─────────────────────────────┤
│ 方案     │ 分拣效率下降  │ 包裹延误量    │ 经济损失估算                 │
├──────────┼──────────────┼──────────────┼─────────────────────────────┤
│ A. K3s   │ 0%           │ 0 件         │ ¥0                          │
│          │ 业务完全自治  │              │ 仅管理面数据上报延迟          │
├──────────┼──────────────┼──────────────┼─────────────────────────────┤
│ B.KubeEdge│ 5-15%       │ 2500-7500件  │ ¥5-15万                     │
│          │ 无法调度新Pod │ 无法处理异常  │ 含人工介入成本               │
│          │ 故障节点不可迁│ 需等网络恢复  │                              │
├──────────┼──────────────┼──────────────┼─────────────────────────────┤
│ C. K0s   │ 0%           │ 0 件         │ ¥0                          │
├──────────┼──────────────┼──────────────┼─────────────────────────────┤
│ D.裸金属  │ 0-5%         │ 0-2500件     │ ¥0-5万                      │
│          │ 进程级自愈OK  │ 节点故障时需  │ 含运维出差成本                │
│          │ 节点级故障不行│ 人工介入      │                              │
├──────────┼──────────────┼──────────────┼─────────────────────────────┤
│ E.Swarm  │ 0-2%         │ 0-1000件     │ ¥0-2万                      │
│          │ 基本自愈      │ 极端情况      │                              │
├──────────┼──────────────┼──────────────┼─────────────────────────────┤
│ F.OpenYurt│ 5-15%       │ 2500-7500件  │ ¥5-15万                     │
│          │ 同 KubeEdge  │              │                              │
└──────────┴──────────────┴──────────────┴─────────────────────────────┘

年度累计影响 (按每分拨月均 2 次 WAN 断线):
  K3s:      ¥0
  KubeEdge: ¥240-720万/年 (200分拨 x 12月 x 2次 x ¥5-15万)
  裸金属:   ¥0-240万/年 + 运维人力成本

13.9 网络不稳定场景下的 K3s 强化措施

即使 K3s 独立集群在网络不稳定下表现最优,仍需在分拨内做以下强化:

1. 本地镜像仓库高可用

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: local-registry
  namespace: kube-system
spec:
  replicas: 2  # 2副本保证镜像仓库高可用
  selector:
    matchLabels:
      app: local-registry
  template:
    spec:
      containers:
        - name: registry
          image: registry:2
          ports:
            - containerPort: 5000
      # 调度到不同节点,防止单节点故障
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app: local-registry
              topologyKey: kubernetes.io/hostname

2. 服务优雅降级配置

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: network-resilience-config
  namespace: business
data:
  resilience.yaml: |
    # 网络不稳定时的服务降级策略
    services:
      scs:
        network_failure_mode: continue_with_local
        cache_duration: 300  # 本地缓存5分钟
        fallback: last_known_good

      rds:
        network_failure_mode: use_cached_routes
        cache_duration: 600  # 路由缓存10分钟
        fallback: default_routes

      dcs:
        network_failure_mode: buffer_locally
        buffer_max_size: 5GB
        flush_on_reconnect: true
        batch_upload: true

      vms:
        network_failure_mode: local_record_only
        storage_threshold: 80%  # 存储超80%时覆盖旧录像
        upload_priority: low  # 恢复后低优先上传

3. Pod 调度拓扑感知(减少跨节点通信依赖)

yaml
# 尽量将关联服务调度到同一节点或相邻节点
# topology-spread-constraints.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: scs
  namespace: business
spec:
  template:
    spec:
      topologySpreadConstraints:
        # 主备分离,防止单点
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: scs
      affinity:
        # 关联服务尽量同节点
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 80
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: pipeline
                      operator: In
                      values: [sorting]
                topologyKey: kubernetes.io/hostname

13.10 最终对比结论

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│   网络不稳定场景下各方案综合评级                                      │
│                                                                     │
│   ┌──────────────────────────────────────────────────────────┐     │
│   │  A. K3s 独立集群    ████████████████████████████  S 级    │     │
│   │     网络不稳定场景的最优解,控制面完全本地化               │     │
│   ├──────────────────────────────────────────────────────────┤     │
│   │  C. K0s 独立集群    ████████████████████████████  S 级    │     │
│   │     与 K3s 能力相当,资源占用稍高                         │     │
│   ├──────────────────────────────────────────────────────────┤     │
│   │  E. Docker Swarm    ████████████████████████░░░░  A 级    │     │
│   │     本地自治好,但生态和运维工具不足                       │     │
│   ├──────────────────────────────────────────────────────────┤     │
│   │  D. 裸金属+Ansible  ████████████████░░░░░░░░░░░░  B 级    │     │
│   │     无网络依赖,但运维原始,故障恢复慢                     │     │
│   ├──────────────────────────────────────────────────────────┤     │
│   │  B. KubeEdge        ████████░░░░░░░░░░░░░░░░░░░░  D 级    │     │
│   │     控制面远端依赖是网络不稳定场景的致命伤                 │     │
│   ├──────────────────────────────────────────────────────────┤     │
│   │  F. OpenYurt        ██████████░░░░░░░░░░░░░░░░░░  D 级    │     │
│   │     YurtHub 缓存缓解有限,核心问题同 KubeEdge             │     │
│   └──────────────────────────────────────────────────────────┘     │
│                                                                     │
│   核心结论:                                                         │
│                                                                     │
│   1. 控制面本地化 是应对网络不稳定的 第一原则                        │
│      -> K3s/K0s 天然满足,KubeEdge/OpenYurt 结构性缺陷              │
│                                                                     │
│   2. K3s 优于 K0s 的差异点:                                         │
│      -> 资源占用更低 (重要,8-13台机器资源有限)                      │
│      -> 部署更简单 (单命令安装)                                      │
│      -> 社区生态更丰富 (排障和中文资料更多)                           │
│                                                                     │
│   3. Cloud-Edge 方案 (KubeEdge/OpenYurt) 适合:                      │
│      -> 网络稳定的边缘场景 (如门店、4S店)                            │
│      -> 极大规模 (>1000站点) 且可接受断线降级                        │
│      -> 不适合快递分拨这种网络不稳定+业务连续性要求高的场景            │
│                                                                     │
│   4. 混合方案可行性:                                                 │
│      -> 大型分拨 (10-13台): K3s 独立集群                            │
│      -> 小型驿站 (2-3台):  KubeEdge EdgeCore                       │
│      -> 驿站业务简单,断线影响可控                                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

文档维护者: AI Agent 最后更新: 2026-08-27 版本历史:

  • v1.0 - 初始版本
  • v1.1 - 新增第13章:分拨网络不稳定场景下的边缘方案深度对比