PouchContainer 容器技术演进助力阿里云原生升级

在容器技术加持的云原生造成趋势的今天,PouchContainer 容器技术支持的业务方也再也不只有集团电商业务和在线业务了,咱们经过标准化的演进,把全部定制功能作了插件化,适配了不一样场景的须要。node

限时福利:张磊、李响等人撰写的 《CNCF X 阿里巴巴云原生技术公开课》 限时 0 元领取中。nginx

做者 | 杨育兵(沈陵) 阿里巴巴高级技术专家
git

咱们从 2016 年开始在集团推广全面的镜像化容器化,今年是集团全面镜像化容器化后的第 4 个 双 11,PouchContainer 容器技术已经成为集团全部在线应用运行的运行时底座和运维载体,每一年 双 11 都有超过百万的 PouchContainer 容器同时在线,提供电商和全部相关的在线应用平稳运行的载体,保障大促购物体验的顺滑。
咱们经过 PouchContainer 容器运行时这一层标准构建了应用开发和基础设施团队的标准界面,每一年应用都有新的需求、新的变化,同时基础设施也有上云/混部/神龙/存储计算分离/网络变革这些升级,两边平行演进,互不干扰。技术设施和 PouchContainer 自身都作了很大的架构演进,这些不少的架构和技术演进对应用开发者都是无感知的。

在容器技术加持的云原生造成趋势的今天,PouchContainer 容器技术支持的业务方也再也不只有集团电商业务和在线业务了,咱们经过标准化的演进,把全部定制功能作了插件化,适配了不一样场景的须要。除了集团在线应用,还有运行在离线调度器上面的离线 job 类任务、跑在搜索调度器上面的搜索广告应用、跑在 SAE/CSE 上面的 Serverless 应用、专有云产品及公有云(ACK+CDN)等场景,都使用了 PouchContainer 提供的能力。
后端

运行时的演进

2015 年以前,咱们用的运行时是 LXC,PouchContainer 为了在镜像化后可以平滑接管原来的 T4 容器,在 LXC 中支持新的镜像组装方式,并支持交互式的 exec 和内置的网络模式。
随着云原生的进程,咱们在用户无感知的状况下对运行时作了 containerd+runc 的支持,用标准化的方式加内部功能插件,实现了内部功能特性的支持和被各类标准化运维系统无缝集成的目标。
不管是 LXC 仍是 runc 都是让全部容器共享 Linux 内核,利用 cgroup 和 namespace 来作隔离,对于强安全场景和强隔离场景是不适用的。为了容器这种开发和运维友好的交付形式能给更多场景带来收益,咱们很早就开始探索这方面的技术,和集团 os 创新团队以及蚂蚁 os 虚拟化团队合做共建了 kata 安全容器和 gvisor 安全容器技术,在容器生态嫁接,磁盘、网络和系统调用性能优化等方面都作了不少的优化。在兼容性要求高的场景咱们优先推广 kata 安全容器,已经支持了 SAE 和 ACK 安全容器场景。在语言和运维习惯肯定的场景,咱们也在 618 大促时上线了一些合适的电商使用了 gvisor 的运行时隔离技术,稳定性和性能都获得了验证。
为了一部分专有云场景的实施,咱们今年还首次支持了 Windows 容器运行时,在容器依赖相关的部署、运维方面作了一些探索,帮助敏捷版专有云拿下了一些客户。
除了安全性和隔离性,咱们的运行时演进还保证了标准性,今年最新版本的 PouchContainer 把 diskquota、lxcfs、dragonfly、DADI 这些特性都作成了可插拔的插件,不须要这些功能的场景能够彻底不受这些功能代码的影响。甚至咱们还对一些场景作了 containerd 发行版,支持纯粹的标准 CRI 接口和丰富的运行时。
缓存

镜像技术的演进

镜像化之后必然会引入镜像分发的效率方面的困难,一个是速度另外一个是稳定性,让发布扩容流程不增长太多时间的状况下,还要保证中心节点不被压垮。
PouchContainer 在一开始就支持了使用 Dragonfly 来作 P2P 的镜像分发,就是为了应对这种问题,这是咱们的第一代镜像分发方案。在研发域咱们也对镜像分层的最佳实践作了推广,这样能最大程度的保证基础环境不变时每次下载的镜像层最小。镜像加速要解决的问题有:build 效率、push 效率、pull 效率、解压效率以及组装效率。第一代镜像加速方案,结合 Dockerfile 的最佳实践解决了 build 效率和 pull 效率和中心压力。
第一代镜像分发的缺点是不管用户启动过程当中用了多少镜像数据,在启动容器以前就须要把全部的镜像文件都拉到本地,在不少场景下都是浪费的,特别影响的是扩容场景。因此第二代的镜像加速方案,咱们调研了阿里云的盘古,盘古的打快照、mount、再打快照这种使用方式完美匹配打镜像和分发的流程;能作到秒级镜像 pull,由于 pull 镜像时只须要鉴权,下载镜像 manifest,而后 mount 盘古,也能作到镜像内容按需读取。<
2018 年 双 11,咱们小规模上线了盘古远程镜像,也验证了咱们的设计思路,这一代的镜像加速方案结合新的 overlay2 技术在第一代的基础上又解决了 PouchContainer 效率/pull 效率/解压效率和组装效率。
可是也存在一些问题。首先镜像数据没有存储在中心镜像仓库中,只有 manifest 信息,这样镜像的分发范围就受限,在哪一个盘古集群作的镜像,就必须在那个盘古集群所在的阿里云集群中使用这个镜像;其次没有 P2P 的能力,在大规模使用时对盘古后端的压力会很大,特别是离线场景下因为内存压力致使不少进程的可执行文件的 page cache 被清理,而后须要从新 load 这种场景,会给盘古后端带来更大的压力。基于这两个缘由,咱们和 ContainerFS 团队合做共建了第三代镜像分发方案:DADI(基于块设备的按需 P2P 加载技术,后面也有计划开源这个镜像技术)。
DADI 在构建阶段保留了镜像的多层结构,保证了镜像在屡次构建过程当中的可重用性,并索引了每一个文件在每层的 offset 和 length,推送阶段仍是把镜像推送到中心镜像仓库中,保证在每一个机房都能拉取到这个镜像。在每一个机房都设置了超级节点作缓存,每一块内容在特定的时间段内,都只从镜像仓库下载一次。若是有时间作镜像预热,像 双 11 这种场景,预热阶段就是从中心仓库中把镜像预热到本地机房的超级节点,后面的同机房的数据传输会很是快。镜像 pull 阶段只须要下载镜像的 manifest 文件(一般只有几 K 大小),速度很是快,启动阶段 DADI 会给每一个容器生成一个块设备,这个块设备的 chunk 读取是按需从超级节点或临近节点 P2P 读取的内容,这样就保证了容器启动阶段节点上只读取了须要的部份内容。为了防止容器运行过程当中出现 iohang,咱们在容器启动后会在后台把整个镜像的内容所有拉到 node 节点,享受超快速启动的同时最大程度地避免后续可能出现的 iohang。
使用 DADI 镜像技术后的今年 双 11 高峰期,每次有人在群里面说有扩容任务,咱们值班的同窗去看工单时,基本都已经扩容完成了,扩容体验作到了秒级。
安全

网络技术演进

PouchContainer 一开始的网络功能是揉合在 PouchContainer 本身的代码中的,用集成代码的方式支持了集团各个时期的网络架构,为了向标准化和云原生转型,在应用无感知的状况下,咱们在 Sigma-2.0 时代使用 libnetwork 把集团现存的各类网络机架构都统一作了 CNM 标准的网络插件,沉淀了集团和专有云都在用的阿里巴巴本身的网络插件。在在线调度系统推广期间,CNM 的网络插件已经再也不适用,为了避免须要把全部的网络插件再从新实现一遍,咱们对原来的网络插件作了包装,沉淀了 CNI 的网络插件,把 CNM 的接口转换为 CNI 的接口标准。
内部的网络插件支持的主流单机网络拓扑演进过程以下图所示:

从单机拓扑能看出来使用神龙 eni 网络模式能够避免容器再作网桥转接,可是用神龙的弹性网卡和 CNI 网络插件时也有坑须要避免,特别是 eni 弹性网卡是扩容容器时才热插上来的状况时。建立 eni 网卡时,udevd 服务会分配一个惟一的 id N,好比 ethN,而后容器 N 启动时会把 ethN 移动到容器 N 的 netns,并从里面更名为 eth0。容器 N 中止时,eth0 会更名为 ethN 并从容器 N 的 netns 中移动到宿主机的 netns 中。
这个过程当中,若是容器 N 没有中止时又分配了一个容器和 eni 到这台宿主机上,udevd 因为看不到 ethN 了,它有可能会分配这个新的 eni 的名字为 ethN。容器 N 中止时,把 eth0 更名为 ethN 这一步能成功,可是移动到宿主机根 netns 中这一步因为名字冲突会失败,致使 eni 网卡泄漏,下一次容器 N 启动时找不到它的 eni 了。能够经过修改 udevd 的网卡名字生成规则来避免这个坑。性能优化

运维能力演进

PouchContainer 容器技术支持了百万级的在线容器同时运行,常常会有一些问题须要咱们排查,不少都是已知的问题,为了解决这个困扰,我还写了 PouchContainer 的一千个细节以备用户查询,或者重复问题问过来时直接交给用户。可是 PouchContainer 和相关链路自己稳定性和运维能力的提高才是最优的方法。今年咱们建设了 container-debugger 和 NodeOps 中心系统,把一些容器被用户问的问题作自动检测和修复,任何修复都作了灰度筛选和灰度部署能力,把一些常常须要答疑的问题作了用户友好的提示和修复,也减轻了咱们自身的运维压力。
网络

  1. 内部的中心化日志采集和即时分析
  2. 自带各模块的健康和保活逻辑
  3. 全部模块提供 Prometheus 接口,暴露接口成功率和耗时
  4. 提供常见问题自动巡检修复的工具
  5. 运维经验积累,对用户问题提供修复建议
  6. 提供灰度工具,任何变动经过金丝雀逐步灰度
  7. 剖析工具,流程中插入代码的能力
  8. Pouch 具有一键发布能力,快速修复


容器使用方式演进

提供容器平台给应用使用,在容器启动以前必然有不少平台相关的逻辑须要处理,这也是咱们之前用富容器的缘由。
1. 安全相关:安全路由生成、安全脚本配置
2. cpushare 化相关配置:tsar 和 nginx 配置
3. 运维 agent 更新相关:运维 agent 更新相对频繁,基础镜像更新特别慢,不能依赖于基础镜像更新来更新运维 agent
4. 配置相关逻辑:同步页头页尾,隔离环境支持, 强弱依赖插件部署
5. SN 相关:模拟写 SN 到/dev/mem,保证 dmidecode 能读到正确的 SN
6. 运维相关的的 agent 拉起,不少运维系统都依赖于在节点上面有一个 agent,无论这个节点是容器/ecs 仍是物理机
7. 隔离相关的配置:好比 nproc 这个限制是在用户上面的,用统一个镜像的容器不能使用统一 uid 否则没法隔离 nproc
如今随着基于 K8s 编排调度系统的推广,咱们有了 Pod 能力,能够把一些预置逻辑放到前置 hook 中去执行,固然富容器能够瘦下来,还要依赖于运维 agent 能够从主容器中拆出来,那些只依赖于 volume 共享就能跑起来的 agent 能够先移动到 sidecar 里面去,这样就能够把运维容器和业务主容器分到不一样的容器里面去,一个 Pod 多个容器在资源隔离上面分开,主容器是 Guaranteed 的 QOS,运维容器是 Burstable 的 QOS。同时在 kubelet 上支持 Pod 级别的资源管控,保证这个 Pod 总体是 Guaranteed 的同时,限制了整个 pod 的资源使用量不超过应用单实例的申请资源。
还有一些 agent 不是只作 volume 共享就能够放到 sidecar 的运维容器中的,好比 arthas 须要能 attach 到主容器的进程上去,还要能 load 主容器中非 volume 路径上面的 jar 文件才能正常工做。对于这种场景 PouchContainer 容器也提供了能让同 Pod 多容器作一些 ns 共享的能力,同时配合 ns 穿越来让这些 agent 能够在部署方式和资源隔离上是和主容器分离的,可是在运行过程当中还能够作它原来能够作的事情。架构

容器技术继续演进的方向

可插拔的插件化的架构和更精简的调用链路在容器生态里面仍是主流方向,kubelet 能够直接去调用 pouch-containerd 的 CRI 接口,能够减小中间一个组件的远程调用,不过 CRI 接口如今还不够完善,不少运维相关的指令都没有,logs 接口也要依赖于 container API 来实现,还有运行环境和构建环境分离,这样用户就不须要到宿主机上面执行 build。全部的运维系统也再也不依赖于 container API。在这些约束下咱们能够作到减小对一个中间组件的系统调用,直接用 kubelet 去调用 pouch-containerd 的 CRI 接口。
如今每一个应用都有不少的 Dockerifle,怎么让 Dockerfile 更有表达能力,减小 Dockerfile 数量。构建的时候并发构建也是一个优化方向,buildkit 在这方面是可选的方案,Dockerfile 表达能力的欠缺也须要新的解决方案,buildkit 中间态的 LLB 是 go 代码,是否是能够用 go 代码来代替 Dockerfile,定义更强表达能力的 Dockerfile 替代品。
容器化是云原生的关键路径,容器技术在运行时和镜像技术逐渐趋于稳定的状况下,热点和开发者的目光开始向上层转移,K8s 和基于其上的生态成为容器技术将来能产生更多创新的领域,PouchContainer 技术也在向着更云原生、更好适配 K8s 生态的方向发展,网络、diskquota、试图隔离等 PouchContainer 的插件,在 K8s 生态系统中适配和优化也咱们后面的方向之一。并发

阅读全文: http://gitbook.cn/gitchat/activity/5e130f76cb002c6386b0f7bf

您还能够下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX