其他
爱奇艺容器实践(内附云原生落地沙龙干货下载)
其中,来自爱奇艺的技术专家赵慰为大家带来了爱奇艺容器实践的分享,讲述了爱奇艺的容器应用场景和在容器网络、容器运行时方面的实践经验。
PS:关注公众号,在后台回复关键词“云原生”,就可以获得本次技术会嘉宾分享PPT和录播视频。
以下为“爱奇艺容器实践”干货分享,根据【i技术会】现场演讲整理而成。
爱奇艺容器实践/分享嘉宾:爱奇艺技术专家赵慰
本场分享的主要内容包括爱奇艺近年来在容器方面的实践经验和遇到过的问题,以及我们在选型和探索过程中的一些心路历程。
1、爱奇艺容器应用场景
2、容器网络实践
可能有些同学对CNM(Container Network Model,容器网络模型)和CNI(Container Network Interface,容器网络接口)的容器标准之争不太了解,下面做一个简单的解释:
Docker将自己的网络方面剥离出一个独立项目libnetwork并提出了CNM,定义了网络、接入点等概念以及创建网络、加入网络等操作,允许第三方插件按照标准与Docker对接;CoreOS则提出了更为简单的CNI,只定义了往网络里面加减容器的接口标准。当时对于两款接口的使用,两家公司各执一词。对于K8S方来说,他们觉得CNM的接口和Docker的结合过于紧密,所定的用户操作标准也过于复杂,CNI相比CNM来说更加安全、简单、松耦合;Docker方对此的官方回复是,K8S等社区的意见和建议不符合Docker的整体设计。最终K8S选择了CNI作为网络方案。现在CNI在行业应用中占据主导地位,但对于插件来说,无论对哪个接口,所做的工作内容都是差不多的,例如CNM要求网络配置要通过Docker的libKV存储,而CNI没有这方面的要求,但对于网络插件来说,它的存储总是要有的,只不过由插件自己管理或由K8S统一管理对于K8S用户来说更加方便。
(1)Bridge + NAT
回到Mesos环境中。由于当时Mesos还没有很好地支持CNI,而爱奇艺在使用CNM中遇到了很多管理和运维上的困难,最简单可靠的Bridge + NAT方案成为了唯一选择。当时我们想的也比较简单、理想化,认为不管走NAT还是不走NAT,在服务注册发现把这些信息掩饰掉就好。但在长期的实践中,这层NAT还是给我们的运维工作带来了无比多的烦恼。 我们遇到的常见问题包括RPC暴露服务地址、排障时IP PORT查应用不能正常使用、Nginx Keepalive失效等。另外还有一些偶发问题比如网卡无法释放、IP冲突等等,但整体来说还是比较可靠。
Nginx Keepalive失效是这几个问题里面比较棘手的一个。
问题:多个 RS,通过 Nginx 代理请求,QPS 极低,偶发 502,有一定规律复现。
解决:1)抓包:——Nginx:502 时,直接收到 RST;——RS 容器内:中间发送过 FIN,502 时没有包。
2)解决:推测几个可能性:
——iptables 有 bug——然而相关文章表明,这种情况只会小概率出现并不会稳定出现,不太符合这个 bug 的现象,而且该服务使用短链接访问 FIN 正常,故排除此原因;
——bridge 网络问题:小概率出现,同样不符合;
——iptables NAT 规则问题:继续抓包,从RS容器发出的FIN包入手,主机上被没有处理,进而发现时主机NAT表里已经没有了Nginx到RS的连接转换规则,联系到低频请求的情况,最终判断为是RS空闲超时后主动断开keepalive连接,而Nginx并不知情仍尝试使用旧连接导致访问失败。
这个问题没有完美的解决方案,缓解思路有几个:1)将内核参数net.netfilter.nf_conntrack_tcp_timeout_established调大,使NAT规则容忍的空闲时间超过RS容忍的空闲连接时间;2)Nginx或其他客户端使用Keepalive时使用TCP心跳等机制维持连接;3)全部使用短连接请求。
(2)Bridge/CNI + VXLAN
应用K8S之后,一开始爱奇艺尝试了Bridge/CNI + VXLAN。对于二层网络,K8S官方至今没有给出最佳实践方案。我们只了解到类似GCE等公有云通过这种方式使用,但整个工作应该和Docker早期一个叫pipework的工具差不多,还是比较简单好用的。
在应用过程中也遇到过一些问题:
问题:该模式下,Pod内访问Service IP的请求如果被转发到同节点的实例,则收不到响应。
解决:这个问题是也和iptables有关,如果内核参数net.bridge.bridge-nf-call-iptables 值为1,则数据包会经过主机iptables处理并遗憾丢失。将参数开机置为0后,仍发现各节点的参数值为1;经过一系列排查,最终发现是Docker启动时会加载除bridge外还加载br_netfilter模块,同时修改参数为1。于是将br_netfilter设置为开机加载,加之内核参数开机置0,问题即被解决。另一个解决思路是,放弃Docker,更换为Containerd。
(3)Cilium/CNI + BGP
Cilium这部分是比较冒险的一个尝试,它的工作原理简单而言,就是把一些工作通过eBPF机制实现到内核里。这种程序本身不会比普通程序执行更快,但它大大缩短了执行路径。
IPAM的思路一般有完全分布式的CIDR per host或集中式的CIDR per IDC、global CIDR几种。各种选择都有利弊,比如CIDR per host的路由表虽然简单,但IP漂移局限性较大,并且会因为碎片化浪费大量IP;而CIDR per IDC、global CIDR方案IP漂移局限性小,也能节省IP,但会导致路由表变得即为庞大难以维护。经协调,我们最终决定按照TOR做规划,尽可能在路由复杂度、IP资源与灵活性之间做出权衡。具体的网络规划涉及保密信息,此处不便展开。 BGP配置涉及到交换机和主机较大的改动。宿主机延续了之前的双物理网卡HA设计,通过分别于两个交换机建立BGP连接,并建立等价路由,既能提升网络带宽又能在一定程度保障网络高可用。
问题:——Cilium + BGP 改造周期长,不能及时满足交付时间; ——Cilium 技术本身潜在风险,需要准备快速恢复的方案; ——存量 Bridge 集群平滑迁移到 Cilium。
解决:——Bridge、Cilium 在内网网段统一规划,跨节点走交换机; ——为不同网络节点打标签,通过 Daemon Set 部署 CNI Agent。
3、容器运行时
Docker Daemon早期的工作模式和稳定性为很多使用者所诟病;后来我们为更好地保持集群和容器状态一致性,尝试使用Mesos推出的Unified Container,然而遇到的问题并不比Docker少太多。由于当时镜像、容器的存储可靠性和使用效率不尽人意,以及缺少排障工具等等原因,这段尝试最终告一段落,当然现在还有一些特殊的应用场景仍在沿用这个环境。
这篇文章要分享的方案主要还是Containerd + RunC/Kata。
使用容器时经常要被以下几个问题困扰:
首先,容器的隔离性并不充分,容器之间会有一定程度的相互影响,很多情况下,一个容器出问题会导致整个主机的运行瘫痪。比如我们之前遇到过一些问题,一个容器中的进程数飙升导致整个宿主机load升高,并伴随着高频的线程切换,这种情况cgroup的简单限制无法帮助其他容器正常运行;另外还遇到过一些低级错误,例如请求完忘记关掉连接,导致很快耗尽FD进而整机故障;
其次,容器内检测到的资源通常为宿主机的资源,比如有些JAVA程序通过检测CPU、内存来自动适配自己的线程数等运行状态。当然Java等也都在逐渐往适配容器的方向发展,但效果还不是太理想;
最后是安全需求,比如各种资源的可见性、可访问性控制。
解决以上问题的一个常见思路是将容器放在虚拟机内运行。实际应用中要突破两个点,一是虚拟机要尽可能轻量、启动快,二是与方便使用、与Kubernetes等方便集成。
容器运行时的实践
(1)Kata
值得注意的是,实际应用场景中表现出来的性能通常与基准测试有一定差距。比如我们深度学习的图片推理场景进行测试,Kata相比RunC有5%到10%的损耗,勉强在可接受范围之内,但是如果要规模应用,还需要进一步优化。
不过,Kata本身因为套了一层虚机,实际应用还有一些限制,列举如下:
·需要 CPU 开启 vmx 虚拟化支持·不支持 host 网络模式·不支持加入其它容器 network namespace·不支持 docker checkpoint、restore 功能·不完全支持 events,例如不支持 OOM notification·Update command 不支持 block IO weight·不支持 docker run 参数 --shm-size 设定共享内存·不支持 docker run 参数 –sysctl等
在实际应用时,除了不支持host网络模式偶尔对我们有一些影响之外,其他限制基本没有影响。
(2)gVisor
在这里放上它的官方文档和兼容性描述连接:官方文档:https://gvisor.dev/docs/兼容性:https://gvisor.dev/docs/user_guide/compatibility/
爱奇艺没有选择gVisor的原因是很多目前生产环境中的必需的工具它暂时无法完美支持,比如IP、SSHD、netstat等命令。