作者 | 蔡中原
策划 | marsxxl
云原生为实践者指明了一条能够充分利用云的能力、发挥云的价值的最佳途径,现已成为企业数字化转型的必经之路。随着云计算的普及,企业应用容器化的趋势已势不可挡,并主要面临以下几个重要问题:激增的流量负载与资源容量规划的矛盾如何解决?资源成本与系统可用性如何平衡?
容器扩容是解决以上问题的关键,扩容关注的主要是在负载出现突增时,如何通过调整容器集群规模来提高集群的承载能力,从而提升用户体验和保障系统服务的高可用性。目前最常用的策略是通过增加容器副本数来提升系统整体的处理能力,即容器横向扩容,但只适用于无状态服务,而对于一些有状态服务(如数据库服务、消息队列等中间件应用)并不适用。还有一种常用的策略则是通过提升单个容器的处理能力来实现系统整体处理能力的提升,即容器纵向扩容。容器纵向扩容虽然可以快速调整容器资源限制,但会影响业务连续性,对于应用特别是 7*24 小时重点保障应用,这是无法接受的。
目前,业界并没有容器在线纵向扩容的成熟方案。鉴于此,工商银行在无业界经验参考的情况下,积极探索并率先于同业实现容器的不停机纵向扩容,在平衡资源成本与系统可用性的同时,也有效缓和了激增的流量负载与资源容量规划之间的矛盾。
1 行业现状
从调研情况来看,大型互联网公司(如亚马逊、谷歌、IBM 等)均在容器纵向扩容领域开展了较多实践(如表 1 所示)。容器纵向扩容的主要实现方式有两种:Vertical Pod Autoscaler(VPA)方式和修改 Kubernetes 源码方式。其中,VPA 方式能够根据容器资源使用率自动设置 CPU 和内存的软硬限制,从而为每个容器提供适当的资源,但需要重建容器。修改 Kubernetes 源码方式可以保证容器在线扩容,但对 Kubernetes 源码侵入性较强,对于后续 Kubernetes 版本升级有较大影响,对现有版本影响也有待进一步评估。
厂商 | 具体方案 | 优点 | 缺点 |
---|---|---|---|
亚马逊 | Kubernetes VPA | 1.根据容器资源使用率自动设置资源限制2.无需修改K8s源码 | 需重建容器 |
谷歌 | 基于Kubernetes VPA修改 | 1.根据容器资源使用率自动设置资源限制2.无需重建容器 | 需修改K8s源码 |
IBM | 修改Kubernetes源码 | 1.无需重建容器 | 需修改K8s源码 |
华为 | 新起一个预期规格的备库挂上,待数据同步完成后停掉旧库 | 1.操作简单,2.风险较小 | 需重建容器 |
表 1 业界主流纵向扩容方案
2 中国工商银行在容器纵向扩容领域的探索及应用
中国工商银行基于 Docker 和 Kubernetes 架构自主研发建设了 PaaS 云平台,目前已承载行内 200 多个应用,20 多万普通应用容器。同时,工商银行积极探索中间件容器领域,已实现涵盖 MySQL、Redis、ElasticSearch、ZooKeeper 等多种有状态应用的容器化部署,其中 MySQL 容器上云率已达到 90% 以上。
随着容器种类和规模的不断扩大,上云后资源利用率和服务高可用也变得尤为重要,而容器在线纵向扩容正是保障应用服务高可用的一个重要的手段和措施。
积极研究探索创新思路,着手开展实践案例验证
经过行业现状调研,明确业界现有方案无法满足需求后,工商银行对于容器在线扩容提出了探索思路并开展案例验证:
- 创建一个 QoS 类别为 Guaranteed 的 Pod(规格:2C4G)。
- 通过 docker stats <containerId> 查看到此时的容器内存限制及容器的 Cgroup 限制(如图 1 所示),二者的值是相等的,均为 3999997952Kb(3.725GB)。
图 1 容器内存限制截图
- 通过 docker update 命令将内存限制修改为 8G,docker update --memory 7999995904 --memory-swap -1,
- 查看此时宿主机上容器 Cgroup 的值已经修改为 8G(如图 2 所示)。至此,容器限制已完成修改,在此过程中容器没有重启。
图 2 容器 Cgroup 内存限制截图
- 取出 ETCD 中 PodSpec 数据,修改完限制参数再更新至 ETCD。
在上述过程中,Pod 及内部容器的状态变化一直在监视当中,截至目前,Pod 一直未发生重启,但容器却已经发生重启,为何只是修改 ETCD 中静态文件就会引发容器重启?
透析容器资源限制原理,明确容器参数调整措施
带着 2.1 节末尾遗留的问题,工商银行对于 Pod 的创建流程(如图 3 所示)及 K8s 源码展开了深入地研究:
图 3 Pod 创建流程图
- 用户向 API Server 发起一个 Pod 创建请求,API Server 接收请求后,将 PodSpec 写入 ETCD。
- Scheduler 通过查看 ETCD 中存储的信息,找到宿主机信息为空的 Pod,并进行调度计算,找到最合适的宿主机,然后将信息更新至 ETCD 中的 PodSpec。
- Kubelet 通过定期监测 ETCD,找到分配给自己的 Pod,即 ETCD 中与自身 IP 相同的 PodSpec 记录,紧接着就调用 Docker 和创建 Pod,并在 Docker 的配置文件中添加一个 PodSpec 的 Hash 值的标注。
通过 Pod 的创建流程可以发现,Pod 的最终创建是由 Kubelet 来处理,于是就根据容器 id 关键字查找 Kubelet 日志,可以看到如下信息:“Container spec hash changed (1073425520 vs 2818111920)..Container will be killed and recreated”。
日志表明容器的 hash 值已经发生变化,容器将会重启,倒推至 K8s 源码位于 pkg/kubelet/kuberuntime/kuberuntime_manager.go 中的 computePodActions 方法(如图 4 所示),该方法是用来计算 PodSpec 的哈希值是否发生变化,内部则是调用 containerChanged 方法(如图 5 所示)决定了容器是否需要重启。
图 4 computePodActions 方法源码截图
图 5 containerChanged 方法源码截图
可以清楚地看到只要 v1.Container 的任何一个字段发生改变都会导致期望的容器 hash 值变化。如果 hash 值变化则返回 true,告知 Kubelet SyncPod 方法(如图 6 所示)触发 Pod 内容器重建或者 Pod 重建。SyncPod 通过以下步骤完成保证运行中的 Pod 与期望的配置时刻保持一致:
- 根据从 API Server 获得的 PodSpec 以及当前 Pod 状态比较并计算所需要执行的操作(重启、删除等);
- 在必要情况下删除当前 Pod 的沙箱容器;
- 根据需要(如重启)删除掉 Pod 中的业务容器;
- 根据需要创建 Pod 的沙箱容器;
- 启动下一个根容器;
- 启动 Pod 中的业务容器;
图 6 syncPod 方法源码截图
深入剖析内核限制机制,找准内核参数改造方向
通过 2.2 节已然明确了容器重启的原因,就可以针对性地制定改造措施:
- 修改 PodSpec 资源限制后,再将其更新至 ETCD;
- 重新计算 PodSpec 的 Hash 值,将最新的 Hash 值更新至 Docker 容器配置文件。
通过上述步骤后,容器确实能够不停机扩容,Pod 也未发生重启。但是通过混沌测试工具 ChaosBlade 测试内存占用时,发现容器内存最大上限依然是 4G,说明容器的 Cgroup 虽然修改了,但是最大使用量依然是扩容前的值,于是又引发了一个新的问题:为何容器 Cgroup 已经完成了修改,但是使用量却依然被限制?
为了解决上述疑问,工商银行对 Cgroup 机制进行深入地研究。Linux CGroup(Control Group)是 Linux 内核的一项重要功能,用于隔离、限制一组特定进程的资源使用(如 CPU、内存、磁盘、网络等)。Kubernetes 作为容器编排和管理的工具,其底层原理就是通过 CGroup 技术实现容器的资源限制和隔离。
图 7 宿主机 CGroup 隔离结构图
Kubelet 启动时,会按需创建一个四层的 CGroup 树(如图 7 所示),具体列举如下:
第一层(Container CGroup):容器级别的资源限制,由容器运行时(例如 Docker)来负责创建和维护,通过 docker update 命令即可进行实时调整。
第二层(Pod CGroup):将 Pod 使用到的资源都纳入管辖范围内,与 Pod 一一对应,其限制取决于 Pod 中容器的资源限制之和。通过调整宿主机相应目录的参数即可实现 Pod 层级资源限制调整。
第三层(QoS CGroup):是对于不同级别 Pod 的总限制,对于 Guaranteed 级别的 Pod,本身已经指定了资源软、硬限制,无需再增加 CGroup 来约束。对于 Burstable Pod 中容器必须没有设置内存和 cpu 限制或请求级别的 Pod,由于部分容器没有指定资源限制,在极端条件下会无限地占用资源,因此需要分别设置 Burstable CGroup 和 BestEffort CGroup。通过调整宿主机相应目录的参数即可实现 CGroup 层级资源限制调整。
第四层(Kubepods CGroup):Kubelet 将所有的 Pod 都创建在一个 Kubepods 的 CGroup 下,用来限制宿主机上所有运行 Pod 最大可以使用的资源。Kubepods CGroup 已是所有 Pod 资源使用上限,无需进行调整。
经过对 Linux CGroup 机制的深入剖析,不同等级的 Pod 就明确了准确的内核参数改造方向:
- 对于 Guaranteed 级别 Pod,按照 Container Cgroup、Pod Cgroup 二层参数的调整即可实现 Guaranteed 级别 Pod 内核限制的修改。
- 对于 Burstable、BestEffort 级别 Pod,按照 Container Cgroup、Pod Cgroup、QoS CGroup 三层参数的调整即可实现 Burstable、BestEffort 级别 Pod 内核限制的修改。
至此,工商银行已顺利实现容器在线纵向扩容的创新探索及案例验证。
依托云运维能力,实现容器在线纵向扩容落地
工商银行基于容器自服务云平台,集成容器在线纵向扩容功能,形成可视化、易操作的自服务体系,已成功实现生产环境落地,并在 MySQL 容器实现全面推广,效果立竿见影。通过结合 Prometheus 等监控手段,MySQL 容器在性能容量达到一定阈值时,能够实现秒级扩容,降低停机扩容的运维成本和时间,并通过在线方式保障了业务的连续性和高可用性。
3 未来展望
随着工商银行 IT 架构转型的持续推进,越来越多的有状态服务将实现云化部署,这对大规模中间件容器的高可用性、服务器资源利用率提出了更高的要求。后续工商银行将持续推进容器在线纵向扩容系统建设,规划包括内存自动扩缩容、CPU 自动扩缩容等场景,在逐步平衡资源成本与系统可用性的同时,不断缓和激增的流量负载与资源容量规划之间的矛盾。同时,通过对接监控体系实现集监控、自调整、快速恢复于一体的更加完善的自动纵向扩容体系,进一步提升机器资源利用率,全面推进中间件容器的高可用能力建设。