3. 医疗容器集群部署与初始化

医疗容器集群与通用互联网集群最大的区别,不在于 Kubernetes 本身,而在于它承载的业务具有强合规、强连续性和强数据敏感性。HIS、LIS、PACS、EMR 等系统一旦中断,直接影响门急诊与住院业务;患者影像与检验数据一旦泄露,涉及《个人信息保护法》与等保 2.0 的合规责任。因此本章的部署目标不是"能跑起来",而是"规划可审计、节点可隔离、运行时可控、网络可追溯、存储可恢复"。

一、生产级集群规划:先定边界,再定规模

规划的第一步是划分集群边界。建议按业务域拆分为三个逻辑集群:核心诊疗集群(HIS、EMR、LIS,要求最高可用性)、影像与检验集群(PACS、RIS,特点是高吞吐、大存储)、对外服务集群(互联网医院、预约挂号、报告查询,暴露在 DMZ)。三者之间通过 API 网关或消息队列通信,不共享 etcd,避免一个集群的故障域扩散到全院。

规模估算要落到具体参数。以核心诊疗集群为例,假设日均门诊 8000 人次、峰值并发 1200 会话,按每会话 0.15 vCPU、256 MiB 内存估算,业务负载约需 180 vCPU、300 GiB 内存。考虑 1.5 倍冗余与 30% 预留,控制面 3 节点(4 vCPU / 16 GiB / SSD),工作节点起步 6 台(16 vCPU / 64 GiB / NVMe),后续按节点池横向扩展。etcd 必须使用独立 SSD,且与 kubelet 数据盘物理分离,否则镜像拉取 IO 会拖垮 etcd 的 fsync 延迟——这是生产环境最常见的隐性故障源。

二、节点角色分配:用标签与污点固化隔离

节点角色不能只靠命名区分,必须通过 label 与 taint 强制约束。推荐的角色划分如下:

  • control-plane:3 台,仅运行 apiserver、controller-manager、scheduler、etcd,打上 node-role.kubernetes.io/control-plane 并设置 NoSchedule 污点。
  • infra:2~3 台,承载 Ingress Controller、CoreDNS、监控、日志、镜像仓库代理,标签 role=infra。
  • app-core:承载 HIS/EMR 等核心业务,标签 role=app-core,配合 PodDisruptionBudget 保证滚动升级时至少 2 副本在线。
  • app-pacs:承载影像处理,标签 role=app-pacs,节点需挂载大容量本地盘或直连存储。
  • edge:DMZ 区对外服务节点,标签 role=edge,网络策略最严格。

污点与容忍的典型写法:

kubectl taint nodes node-infra-01 role=infra:NoSchedule
kubectl label nodes node-core-01 role=app-core
# 业务 Pod 中声明
tolerations:
- key: "role"
  operator: "Equal"
  value: "infra"
  effect: "NoSchedule"
nodeSelector:
  role: "app-core"

这样即使调度器出现异常,核心业务也不会被挤到 DMZ 节点上,满足等保对区域边界隔离的要求。

三、容器运行时选型:containerd 为主,兼顾合规审计

自 Kubernetes 1.24 起 Dockershim 被移除,生产环境应直接选用 containerd 作为 CRI 运行时。相比 Docker,containerd 组件更少、调用链更短、资源开销更低,且原生支持 CRI。配置要点是启用 SystemdCgroup 驱动,避免与 kubelet 的 cgroup 管理冲突:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true

对于多租户或需要更强隔离的影像处理任务,可额外配置 gVisor 或 Kata Containers 作为 RuntimeClass,通过 runtimeClassName: kata 按 Pod 粒度选择。医疗场景还需关注镜像来源合规:所有镜像必须来自院内 Harbor 私服,开启镜像签名与漏洞扫描,禁止直接拉取公网镜像。

四、网络插件与存储插件安装

网络插件推荐 Calico,原因是它同时提供 BGP 路由与 NetworkPolicy,能满足医疗环境"默认拒绝、按需放通"的东西向管控要求。安装时选择 IPIP 或 VXLAN 模式需与院内网络团队确认:若底层是传统三层网络且不允许 BGP 穿透,选 VXLAN;若机房支持 BGP 且追求性能,选 BGP 直连。安装后必须验证跨节点 Pod 互通与 NetworkPolicy 生效:

kubectl apply -f calico.yaml
kubectl get pods -n kube-system -l k8s-app=calico-node
# 验证策略:默认拒绝后,仅放通 8080
kubectl apply -f deny-all.yaml
kubectl exec -it test-pod -- curl http://svc-his:8080

存储插件方面,核心业务使用 Ceph RBD 或商用 SAN 提供的 CSI 驱动,保证块存储的低延迟与快照能力;PACS 影像建议使用 NFS 或对象存储 CSI,配合生命周期策略将冷数据下沉。StorageClass 必须区分性能等级,例如 sc-gold(SSD,核心库)、sc-silver(SAS,一般业务)、sc-archive(大容量,影像归档),并通过 volumeBindingMode: WaitForFirstConsumer 避免跨可用区调度失败。

医疗 Kubernetes 集群部署拓扑与数据流 控制面 control-plane ×3 apiserver / scheduler controller-manager etcd(独立 SSD) NoSchedule 污点 infra 节点 ×2 Ingress / CoreDNS 监控 / 日志 / Harbor Calico 控制组件 role=infra app-core 节点 ×6 HIS / EMR / LIS containerd + runc sc-gold(SSD) role=app-core app-pacs 节点 ×4 PACS / RIS Kata 隔离运行时 sc-archive(大容量) role=app-pacs Calico 网络层:VXLAN / BGP + NetworkPolicy(默认拒绝,按需放通) 东西向:Pod ↔ Pod 加密可选 · 南北向:Ingress → Service → Pod sc-gold · Ceph RBD 核心库 · 低延迟 · 快照 WaitForFirstConsumer sc-silver · SAN CSI 一般业务 · 中等 IOPS 支持在线扩容 sc-archive · NFS/对象存储 影像归档 · 生命周期下沉 冷热分层 部署检查清单(关键项) ✓ etcd 独立 SSD 且 fsync 延迟 < 10ms ✓ 节点污点/标签与业务 nodeSelector 匹配 ✓ containerd SystemdCgroup=true ✓ NetworkPolicy 默认拒绝已生效

五、医疗环境部署检查清单

集群初始化完成后,必须逐项验证,不能只看 Pod 是否 Running。以下清单按"基础设施—运行时—网络—存储—合规"五类组织,每项都给出可执行的判断依据:

类别检查项判断依据
基础设施etcd 健康与延迟etcdctl endpoint health 全部 healthy,fsync 延迟 < 10ms
基础设施节点资源预留kubelet 配置 --system-reserved 与 --kube-reserved,避免系统进程被 OOM
运行时containerd cgroup 驱动SystemdCgroup = true,与 kubelet 一致
运行时镜像来源合规所有镜像来自院内 Harbor,签名验证通过,无 latest 标签
网络跨节点 Pod 互通不同节点 Pod 互 ping 成功,延迟 < 1ms
网络NetworkPolicy 生效默认拒绝后,未放通的端口访问超时
存储StorageClass 绑定PVC 能成功 Bound,Pod 重启后数据不丢失
存储快照与恢复VolumeSnapshot 可创建并成功恢复到新 PVC
合规审计日志apiserver 审计策略开启,日志留存 ≥ 180 天
合规区域隔离DMZ 节点与内网节点间无直接路由,仅经网关通信
常见排障提示:若 Pod 长时间 Pending,先看 kubectl describe pod 的 Events。医疗集群最常见的原因是 PVC 因 WaitForFirstConsumer 等待调度、节点污点与 tolerations 不匹配、或资源预留后实际可分配量不足。不要急于扩容节点,先确认调度约束是否自相矛盾。