美团容器平台架构及容器技术实践 (美团容器平台介绍)

美团容器平台架构及容器技术实践 (美团容器平台介绍)

背景

美团的容器集群管理平台叫做 HULK。漫威动画里的 HULK 在发怒时会变成“绿巨人”,它的这个特性和容器的“弹性伸缩”很像,所以我们给这个平台起名为 HULK。貌似有一些公司的容器平台也叫这个名字,纯属巧合。

2016 年,美团开始使用容器,当时美团已经具备一定的规模,在使用容器之前就已经存在的各种系统,包括 CMDB、服务治理、监控告警、发布平台等等。我们在探索容器技术时,很难放弃原有的资产。所以容器化的第一步,就是打通容器的生命周期和这些平台的交互,例如容器的申请/创建、删除/释放、发布、迁移等等。然后我们又验证了容器的可行性,证实容器可以作为线上核心业务的运行环境。

2018 年,经过两年的运营和实践探索,我们对容器平台进行了一次升级,这就是容器集群管理平台 HULK 2.0。

美团的容器使用状况是:目前线上业务已经超过 3000 个服务,容器实例数超过 30000 个,很多大并发、低延时要求的核心链路服务,已经稳定地运行在 HULK 之上。本文主要介绍我们在容器技术上的一些实践,属于基础系统优化和打磨。

美团容器平台的基本架构

首先介绍一下美团容器平台的基础架构,相信各家的容器平台架构大体都差不多。

首先,容器平台对外对接服务治理、发布平台、CMDB、监控告警等等系统。通过和这些系统打通,容器实现了和虚拟机基本一致的使用体验。研发人员在使用容器时,可以和使用 VM 一样,不需要改变原来的使用习惯。

此外,容器提供弹性扩容能力,能根据一定的弹性策略动态增加和减少服务的容器节点数,从而动态地调整服务处理能力。这里还有个特殊的模块——“服务画像”,它的主要功能是通过对服务容器实例运行指标的搜集和统计,更好的完成调度容器、优化资源分配。比如可以根据某服务的容器实例的 CPU、内存、IO 等使用情况,来分辨这个服务属于计算密集型还是 IO 密集型服务,在调度时尽量把互补的容器放在一起。再比如,我们可以知道某个服务的每个容器实例在运行时会有大概 500 个进程,我们就会在创建容器时,给该容器加上一个合理的进程数限制(比如最大 1000 个进程),从而避免容器在出现问题时,占用过多的系统资源。如果这个服务的容器在运行时,突然申请创建 20000 个进程,我们有理由相信是业务容器遇到了 Bug,通过之前的资源约束对容器进行限制,并发出告警,通知业务及时进行处理。

往下一层是“容器编排”和“镜像管理”。容器编排解决容器动态实例的问题,包括容器何时被创建、创建到哪个位置、何时被删除等等。镜像管理解决容器静态实例的问题,包括容器镜像应该如何构建、如何分发、分发的位置等等。

最下层是我们的容器运行时,美团使用主流的 Linux+Docker 容器方案,HULK Agent 是我们在服务器上的管理代理程序。

把前面的“容器运行时”具体展开,可以看到这张架构图,按照从下到上的顺序介绍:

美团主要使用了 CentOS 系列的开源组件,因为我们认为 Red Hat 有很强的开源技术实力,比起直接使用开源社区的版本,我们希望 Red Hat 的开源版本能够帮助解决大部分的系统问题。我们也发现,即使部署了 CentOS 的开源组件,仍然有可能会碰到社区和 Red Hat 没有解决的问题。从某种程度上也说明,国内大型互联公司在技术应用的场景、规模、复杂度层面已经达到了世界领先的水平,所以才会先于社区、先于 Red Hat 的客户遇到这些问题。

容器遇到的一些问题

在容器技术本身,我们主要遇到了 4 个问题:隔离、稳定性、性能和推广。

容器的实现

容器本质上是把系统中为同一个业务目标服务的相关进程合成一组,放在一个叫做 namespace 的空间中,同一个 namespace 中的进程能够互相通信,但看不见其他 namespace 中的进程。每个 namespace 可以拥有自己独立的主机名、进程 ID 系统、IPC、网络、文件系统、用户等等资源。在某种程度上,实现了一个简单的虚拟:让一个主机上可以同时运行多个互不感知的系统。

此外,为了限制 namespace 对物理资源的使用,对进程能使用的 CPU、内存等资源需要做一定的限制。这就是 Cgroup 技术,Cgroup 是 Control group 的意思。比如我们常说的 4c4g 的容器,实际上是限制这个容器 namespace 中所用的进程,最多能够使用 4 核的计算资源和 4GB 的内存。

简而言之,Linux 内核提供 namespace 完成隔离,Cgroup 完成资源限制。namespace+Cgroup 构成了容器的底层技术(rootfs 是容器文件系统层技术)。

美团的解法、改进和优化

隔离

之前一直和虚拟机打交道,但直到用上容器,才发现在容器里面看到的 CPU、Memory 的信息都是服务器主机的信息,而不是容器自身的配置信息。直到现在,社区版的容器还是这样,比如一个 4c4g 的容器,在容器内部可以看到有 40 颗 CPU、196GB 内存的资源,这些资源其实是容器所在宿主机的信息。这给人的感觉,就像是容器的“自我膨胀”,觉得自己能力很强,但实际上并没有,还会带来很多问题。

上图是一个内存信息隔离的例子。获取系统内存信息时,社区 Linux 无论在主机上还是在容器中,内核都是统一返回主机的内存信息,如果容器内的应用,按照它发现的宿主机内存来进行配置的话,实际资源是远远不够的,导致的结果就是:系统很快会发生 OOM 异常。

我们做的隔离工作,是在容器中获取内存信息时,内核根据容器的 Cgroup 信息,返回容器的内存信息(类似 LXCFS 的工作)。

CPU 信息隔离的实现和内存的类似,不再赘述,这里举一个 CPU 数目影响应用性能例子。

大家都知道,JVM GC(垃圾对象回收)对 Java 程序执行性能有一定的影响。默认的 JVM 使用公式“ParallelGCThreads = (ncpus <= 8) ? ncpus : 3 + ((ncpus

有一段时间,我们的容器是使用 root 权限进行运行,实现的方法是在 docker run 的时候加入‘privileged=true’参数。这种粗放的使用方式,使容器能够看到所在服务器上所有容器的磁盘,导致了安全问题和性能问题。安全问题很好理解,为什么会导致性能问题呢?可以试想一下,每个容器都做一次磁盘状态扫描的场景。当然,权限过大的问题还体现在可以随意进行 mount 操作,可以随意的修改 NTP 时间等等。

在新版本中,我们去掉了容器的 root 权限,发现有一些副作用,比如导致一些系统调用失败。我们默认给容器额外增加了 sys_ptrace 和 sys_admin 两个权限,让容器可以运行 GDB 和更改主机名。如果有特例容器需要更多的权限,可以在我们的平台上按服务粒度进行配置。

Linux 有两种 IO:Direct IO 和 Buffered IO。Direct IO 直接写磁盘,Buffered IO 会先写到缓存再写磁盘,大部分场景下都是 Buffered IO。

我们使用的 Linux 内核 3.X,社区版本中所有容器 Buffer IO 共享一个内核缓存,并且缓存不隔离,没有速率限制,导致高 IO 容器很容易影响同主机上的其他容器。Buffer IO 缓存隔离和限速在 Linux 4.X 里通过 Cgroup V2 实现,有了明显的改进,我们还借鉴了 Cgroup V2 的思想,在我们的 Linux 3.10 内核实现了相同的功能:每个容器根据自己的内存配置有对应比例的 IO Cache,Cache 的数据写到磁盘的速率受容器 Cgroup IO 配置的限制。

Docker 本身支持较多对容器的 Cgroup 资源限制,但是 K8s 调用 Docker 时可以传递的参数较少,为了降低容器间的互相影响,我们基于服务画像的资源分配,对不同服务的容器设定不同的资源限制,除了常见的 CPU、内存外,还有 IO 的限制、ulimit 限制、PID 限制等等。所以我们扩展了 K8s 来完成这些工作。

业务在使用容器的过程中产生 core dump 文件是常见的事,比如 C/C++程序内存访问越界,或者系统 OOM 的时候,系统选择占用内存多的进程杀死,默认都会生成一个 core dump 文件。

社区容器系统默认的 core dump 文件会生成在宿主机上,由于一些 core dump 文件比较大,比如 JVM 的 core dump 通常是几个 GB,或者有些存在 Bug 的程序,其频发的 core dump 很容易快速写满宿主机的存储,并且会导致高磁盘 IO,也会影响到其他容器。还有一个问题是:业务容器的使用者没有权限访问宿主机,从而拿不到 dump 文件进行下一步的分析。

为此,我们对 core dump 的流程进行了修改,让 dump 文件写到容器自身的文件系统中,并且使用容器自己的 Cgroup IO 吞吐限制。

稳定性

我们在实践中发现,影响系统稳定性的主要是 Linux Kernel 和 Docker。虽然它们本身是很可靠的系统软件,但是在大规模、高强度的场景中,还是会存在一些 Bug。这也从侧面说明,我们国内互联网公司在应用规模和应用复杂度层面也属于全球领先。

在内核方面,美团发现了 Kernel 4.x Buffer IO 限制的实现问题,得到了社区的确认和修复。我们还跟进了一系列 CentOS 的 Ext4 补丁,解决了一段时间内进程频繁卡死的问题。

我们碰到了两个比较关键的 Red Hat 版 Docker 稳定性问题:

声明:本文来自用户分享和网络收集,仅供学习与参考,测试请备份。