做3d动画网站,网站内容授权书,wordpress问答插件哪个好,外包加工网是正规的吗戳蓝字“CSDN云计算”关注我们哦#xff01;作者#xff1a;稻农来源#xff1a;阿里系统软件技术稻农#xff08;叶磊#xff09;阿里云智能事业群高级技术专家参与主导容器运行时及网络创新工作#xff1b;目前的工作侧重于基于进程虚拟化的研究及增强#xff08;网络… 戳蓝字“CSDN云计算”关注我们哦作者稻农来源阿里系统软件技术稻农叶磊阿里云智能事业群高级技术专家参与主导容器运行时及网络创新工作目前的工作侧重于基于进程虚拟化的研究及增强网络及热迁移方面在阿里推行微安全容器及热迁移等力图在保持容器简单高资源利用率前提下提供高安全及热迁移等增强功能。本文根据稻农在「Kubernetes Cloud Native Meetup-广州站」现场演讲内容整理。大家好我的花名是稻农首先我简单介绍一下我在这个领域的工作。在阿里我们现在主要的侧重点是做大规模的运维和新的容器运行时。目前大家可能已经对 Kubernetes 进行了广泛地使用但多数还没有达到一定规模有很多痛点以及内部的问题还没有得到充分暴露。本次分享将介绍阿里在大规模集群腾挪过程中遇到的有状态应用处理实践以及在解决这些问题过程中应如何平衡成本和稳定性。容器迁移背景及现状目前大多数容器的使用还在百台到千台的规模。我先简单介绍一下阿里目前内部容器服务。阿里的淘系应用如天猫、淘宝目前已经全部实现了容器化在集团的场景下面是没有虚拟机的。阿里用了大概三年到四年的时间做到了 100% 容器化。大家都知道使用容器有很多好处比如它在资源耗费方面有很大优势。对于“双十一”大家应该有很明显地感受相比之前现在的“双十一”会“顺滑”很多这样地转变也有容器化的功劳。如果你有存量的业务那你一定会面临从虚拟机或物理机迁移到容器的过程。绝大多数开发人员其实认为这是一个负担因为他们的应用已经跑起来了就不太希望因为基础设施地改变去做更多工作去进行适配。所以出现了一些我们叫“富容器”或者“复杂应用容器”的特殊容器。简单说所谓富容器就是我们回在容器内放置一些管理组件。阿里内部组件叫 star agent它会提供登陆服务提供各种各样的包管理命令行的执行诸如此类的事情。在真正运维和使用的过程中整个容器与虚拟机的差别不大。当然这个东西在业界是存在争议的比如我们是不是应该先做微服务化把所有服务都变成单一、不可改变的镜像再 run 起来还是我们为了迁就一些技术债务引入富容器这种技术这个地方是存在争议的。但是可以告诉大家的是如果你要完全按照理想化的微服务去执行基本上很多大的应用像淘系这些非常复杂的应用改造一下可能要几个月可能在第一步就被卡死了。因为我们有富容器所以这个应用是有状态的并不是随便说我砍掉他然后异地重启就可以了所以就是双刃剑的另一面上线改造容易运维变得复杂了。我们有富容器的一些传统应用很难对他们进行微服务无状态的改造所以我们看到有很多场景比如说容器出现故障时开发或者运维的同学非常希望故障之后的新容器长得跟原来容器一模一样比如 IP 、名字等任何东西都不变非常符合他们的理想。有时候我们面对一些大规模的容器迁移比如说在地方开一个很大的机房我们就会把杭州或者是上海的容器全部迁走。在过程中非常麻烦的是有一些容器是有状态的你迁的时候你还不敢动它因为万一砍掉可能红包就发不了了……大的有状态的应用会占住物理机造成没有办法去迁移。以上都是容器可携带状态迁移成为规模化运维的典型场景。容器可携带状态迁移成为规模化运维难点在于 K8s 或者说整个容器与虚拟机的运维。Docker 公司曾给出说法虚拟机像宠物一样需要受到很精心地呵护才能永远活得很好只要不好就需要去修它这个就是宠物式的管理。K8s 认为容器应该是牛群式的放养死了就直接重启而不需要对每一头牛做特别好地呵护因为成本很高。在 K8s 里面我们经常看到的就是扩缩容针对他的假设都是里面的应用是无状态。然后在执行层面大家现在用的一般都是普通容器或者说是标准容器引擎就是 RunC虽然 RunC 里面有个 checkpoint 和 restore 的机制大家用起来就会发现基本上是不可用的坑非常多。容器可携带状态迁移成为规模化运维有两个难点恰好是我们要解决的两个问题:首先是管理面K8s 上支撑 Pod 的迁移与伸缩不一样我们所认为迁移就是这个容器要原封不动地在异地再重生另外我们认为冷迁移就是业务时间中断比较长中断时间短的就是热迁移。那这个长短的分界岭在哪呢每个云厂商会有一些不同的看法。我们认为大概到毫秒级以下一百毫秒或者十个毫秒这样的级别可以认为它是热迁移。其实任何迁移基本上都会有业务中断的时间任何一种机制去实现都不可能实现零时间切换。我们看一下 K8s 系统对整个容器迁移2015 年开始我们就讨论过 Pod 的迁移要不要放到 K8s 里面去大家可以去翻 K8s 社区 issue但一直没有下文。其次在执行层面RunC 作为容器运行时主流虽有 CRIU 的项目辅助仍然无法提供完善可靠的迁移机制。管理面支撑 Pod 迁移接下来我们看看 K8s 为什么不能够做迁移当前 K8s 系统的 Pod 迁移仍为空白。其中存在以下问题因为每个 Pod 有独立的标识还有名字、ID 等。这个东西是要保证唯一性的不然 K8s 自己也管理不了这些东西。假设有两个同学他们的学号、名字长相完全一样校长是要糊涂的。K8s 主推了对业务的伸缩就是靠无状态伸缩他不会把某一个容器从这迁到那去。另外K8s 骨干系统不支持 Pod 标识及 IP 冲突。我们认为 API server、schedule 这种必不可少的部分是骨干系统。几个骨干系统是不支持任何标识冲突的。如果有两个 Pod他们 ID 一样, API server 就会糊涂逻辑就会出问题。K8s 是一套容器的管理系统阿里周边对网盘 ID、对 IP各种各样的资源都有自己的管理系统。这些管理系统在 K8s 的世界里面表现为不同的资源 controler因为这些资源都是钱买来的要跟底层账务系统联动将来大家都会遇到这些问题比如说这个部门是否有预算等。在阿里的 K8s 周边我们已经开发了大量的这种 controler 了他们都是按照这个标识我们叫 SN 来管理应用的。数据库里面记录每个容器都有一个的标识。伸缩跟迁移是冲突。因为迁移的时候你可能需要砍掉旧容器。砍掉容器之后伸缩控制如果正在生效RC它就会自动起一个新容器而不是把这个容器迁移过去所以这个地方我们对 RC 这些控制器都要做一定程度地改造。其他问题比如说很多远程盘不支持多 Mount。因为我们在做迁移的时候这个盘一定要做到至少有两个 Mount。就是我的旧容器跟新容器能够同时把 PV mount 上去很多远程盘还是不支持的。剩下还有一些传统底层支撑系统比如 IP 管理系统不允许出现地址冲突。比如我们在分 IP 的时候分两个一样的是不可想象的这个是我们最简单的迁移过程。迁移过程是这样的我们想最小地去改造 K8s当你的系统真正上了管理系统复杂了以后大家都会对 K8s 有一些适应性地改造。这些改造最好的表现形式可能就是 controler 了你不要去对骨干系统做改动改动之后就很难再回到主线来了。在迁移过程中我们还是没有办法一定要对骨干系统做一些改造那么我们想尽量减少它的改造量。第一步我们会生成一个从资源上来讲跟原来的 Pod 或者容器完全一样的一个 Pod它需要几核几 U它需要一个什么远程盘它需要一个什么的多少个 IP多 IP 的话还要考虑多少个 IP多少个直通网卡或者是非直通的网卡资源完全一样这个是完全标准的。我们创建一个资源一个新 Pod这就像一个占位符一样。假设我这台物理机要坏了那么我在一个打标之后我在一个新的好的物理集群上生产一个这样的 Pod让我拿到了资源。第二个过程就是说我们两边的 agent比如说是 RunC 或者是阿里做的 pouch-container 也好我们这种 OCI 的 Agent 之间会有一个协商的过程它的协商过程就是会把旧的 Pod 的状态同步过去刚才我们新生成的 Pod 镜像实际它是占位符。我们会把新的镜像动态地插入 Pod 里API 对 CRI 的接口是支持的。当前我们没有办法在一个已经产生的 Pod 里面去插入新的 container。但实际上 OCI 接口本身是支持的可以在一个 sandbox 里面去删掉已有的 container 和增加新的 container不需要做什么新的工作只要打通管理层的事情就可以了。另外唤醒记忆的过程其实就是两边状态同步状态同步完毕我们会做一个切流切流就是把旧的容器不再让新的需求过来一旦我们监控到一个静默期它没有新的需求过来我们会把旧的 Pod 停掉。其实暂时不停掉也没关系因为反正没有客户来找他进行服务了已经被隔离到整个系统外面去了。删除资源是危险操作一般会放置一两天以备万一要回滚。最后一个过程新开发工作量比较大我们要把前面那个占位作用的 Pod 标识改掉IP 与旧的设置成一样然后一切需要同步的东西都在这一步完成。完成之后就上去通知 API server 说迁移过程完成最后完成整个过程。所以大家会看到其实第一步基本上标准的 K8s 就支持。第二步我们是 K8s 不感知的就是我们在两个宿主机上做两个 agent 做状态同步。对 K8s 的改造也比较小。那么最后一个会比较多API server 肯定是要改。RC 控制器可能改如果你有 CI 的这种就是 IPM 的管理IPM 的管理这个地方要改。接下来我从 OCI 的运行这个地方来来讨论这个过程因为其实是有两层面一个是我们篮框这里是一个 Pod从它的状态 Dump 落盘到远端把它恢复整个同步过程中我们会插入对 K8s 系统的调用涉及对容器管理系统的改造。看外面这两个白框上面这个我们叫预处理过程其实就是前面讲的我们要去创建新的 Pod、占位符然后在那边把资源申请到最后一个后期建议。我们刚才说的最后一步我们叫标识的重构重建跟旧的 Pod 完全一样。大家在我们开发过程中会遇到各种各样的冲突如果 API server 有两个标识是一样的这个代码就要特殊处理。APM 有时候会跳出来说你有 IP 冲突这样也要特殊处理至少有几个骨干系统肯定是要做的。这部分因为涉及到 K8s 骨干的改造 patch 我们还没有提上来。接下来还要跟社区讨论他们要不要 follow 我们的做法因为现在 K8s 的容器就是无状态的观点还比较占上风。刚才我们讲到管理面我们认为是事务处理的路上会有很多障碍但是这些障碍都是可以搬掉的就是说无非是这个东西不允许冲突我改一改让他允许冲突或者允许短时间的一个并存那个东西不允许我再改一改。比较硬核的部分是底层引擎去支撑热迁移尤其是热迁移冷迁移其实问题不大冷迁移就是说我只是恢复那些外部可见的状态不迁移内存页表等内部数据如果对我的业务恢复时间没有什么要求的话就比较容易做。RunC 引擎的可迁移性RunC 应该是大家用的最多的它就是标准的 container 去进行的迁移改造。如果大家去看过 checkpoint 开放的这部分代码可以发现 RunC 依靠的机制就是一个叫 CRIU 的东西。CRIU 的优势技术已经出现比较长的时间了用户态把一个进程或者一个进程数完全落盘在把它存到磁盘上然后在异地从磁盘把进程恢复包括它的 PC 指针它的栈它的各种各样的资源经过一段时间地摸索基本上可以认为内存状态是没有问题的就是页表页表是可以做到精确恢复的。不管你这边涉及多少物理页是脏的还是干净的这个都是百分之百可以还原出来的。进程执行的上下文比如各种寄存器调用 stack 等这些都没问题。跟纯进程执行态相关的问题都已经完全解决了这个不用担心。然后大家比较担心的就是一个网络状态比如说大家都知道 TCP 是带状态的它是已连接等待连接还是断开其实这个网络 Socket 迁移的工作也基本完成了。它的实现方法是在 Linux set 里面加了一个修复模式修复模式一旦启动就不再向外发送真正的数据包而是只进行状态及内部 buffer 的同步比如你下达的这种 close不会向外发包只是体现为对状态信息的导出。比如说你要进入修复模式那在原端就要关闭 Socket它并不会真正的去发 close 的 TCP 包它只是把信息 Dump 出来在新的目的地端去 connect。它也不会真正去包最后的结果就是除了 mac 地址不一样TCP 里面的状态也恢复到远端了里面的内存状态都转过去了经过实际验证其实也是比较可靠。还有打开的文件句柄恢复你打开的文件你现在文件比如说读写指针到了 0xFF文件的 off set 恢复都是没有问题的。其实我们在冷热迁移中最担心的就是耗时问题我一个容器究竟花多长时间一个 Pod 多长时间可以迁移到新目的地的宿主机上去耗时的就是内存。也就是说像很多 Java 应用假设你的内存用得越多你的迁移时间就是准备时间就越长。可以看到我们刚才其实是有一个协商过程。在协商过程中旧的 Pod 还在继续提供服务但是它会不停的把它的状态 Sync 到远端去。这个时间其实并不是业务中断的时间如果耗时特别长也会因为业务时在不停转的如果你的内存总是在不停地做大量改动你的准备时间和最后的完成时间就会非常长有可能会超时。我们去评估一个业务能不能做热迁移它所使用的内存大小是一个比较大的考量。然后剩下就是我们踩到的坑。现在还有很多东西它支持的不大好。这个地方大家可以理解一个进程一个进程其实就是一个自己的页表有自己的堆栈一个可执行的活体。那么它支持不好的部分都是外部的如果它依赖一些主机设备就很难把一个设备迁移走。接下来是文件锁如果这个文件是多个进程共用就会加锁。因为这个锁的状态还涉及到别的进程所以你只迁移这一个进程的时候会出问题。这个地方逻辑上会有问题其实大家可以笼统地这样去判断如果我依赖的东西是跟别的用户、进程有一些共享甚至这个东西就是内核的一个什么设备这种就比较难迁移走。所以简单来说就是自包含程度越高越容易迁移。这个是一个比较详细的图跟我刚才讲的过程其实是差不多我们还是会在原端发起热迁移的请求请求之后会发起两端两边 Agent 的 sick然后最后中间会切流。等到 Sync 状态完成我们会通知 K8s 说我这边可以了那么 K8s 会把流从旧的 Pod 切到新 Pod 来然后最后把所有的标识与我的 SN 或 IP 都改造完了最后通知一下 K8s 就结束了。新运行时带来的机会最后,我想分享的是新的运行时。我们在社区里面会看到容器现在来说在私有云上它的主要的形态还是 RunC就是普通的 Linux 标准容器。我们在公有云上为了能混布或者说跟内外客户在线离线都在一起我们一般会选虚拟机类型的容器引擎比如像 kata 这样的东西。早先选择就只有两种讲效率不讲安全就是纯容器讲安全不讲效率就跑一个虚拟机式的容器。从去年开始谷歌、亚马逊等头部玩家开始做一些新的事情叫做进程级虚拟化。就像我们在中学物理讲过比如说光的波粒二象性它在一些维度上看起来是波一些维度上是粒子。其实这个与进程级虚拟化是很相像的从资源管理这个角度看它是一个普通进程。 但是在内部为了加强隔离性会做一个自己的内核。从这个角度看它是一个虚机但是从外部资源角度来看因为这个内核是隐形生效的并没有动用其他虚拟机工具去启动容器也不会去实现完整的设备级模拟管理系统认为它就是一个普通进程。这是一种新的潮流也就是说我们判断这个东西当然我们还需要在里面做很多工作有可能会成为将来容器运行时的一个有益补充或者是主流。简单的说这种新的运行时会有自己的私有内核而且这个内核一般现在都不会再用 C 语言去再写一遍因为底层语言比较繁琐也很容易出错。用过 C语言的人都知道带指针管理很危险Linux 社区 bug 比比皆是现代的做法都会用 Go 语言或其他一些高级语言重写有自己的垃圾回收的机制指针就不要自己去管理了。一般不会提供很丰富的虚拟设备管理。因为这部分对一个应用来说是冗余的普通应用跑起来其实很少去关心用什么设备需要什么特殊 proc 配置简单的说就是把虚拟机的冗余部分全部砍掉只留下普通应用 Linux 的 APP 跑起来。这个是我们对运行史的一个简单的比较是从自包含的角度来讲因为自包含的程度越高它的热迁移越容易实现或一般来说安全性也越高。亚马逊现在在做 Firecracker它也是用现代语言重写了内核。微软的云也在做一个事情。大家的思路是比较一致的。因为硅谷技术交流是很频繁的他们的技术人员之间都是比较知根知底的谷歌做了 gVisor。大家可能听说过谷歌的 gVisorgVisor 是这样一个机制就是说我会在一个 APP就是普通的未经任何修改的 跑在容器里的 Linux 应用那么我们怎么去让他用我们的内核而不用 Linux 内核核心就是要捕获他的系统调用或者说劫持都可以。系统调用的劫持有软硬两种方法软件来说我们在 Linux 内核里面利用 pTrace 的机制强迫就设完之后你设置的进程的所有系统调用他不会让内核去而是先到你进程来。这个叫做软件实现。另一种方法我们叫硬件实现就是说我们会强迫这个 APP 跑在虚拟机的状态。我们知道在虚拟机里面虚拟机会有自己的中断向量表它通过这种方式来获取执行时。然后我们的 Guest kernel 是这样的我们会看到现在的类似内核是无比庞大的截止到现在应该有 2000 万行代码这里面绝大部分其实跟容器运行时没有太大关系。所以我们现在想法就是只需要把 Syscall 服务作好也就是说 APP 看到的无非就是这 300 多个 Syscall。这 300 多个系统调用你能够服务好就是不管你的 Syscall 服务用 Go 写还是用 Python 写的不讲究效率的话你都可以认为你有自己的内核然后跟主机的内核是隔离的因为我没有让 APP 直接接触主机内核的东西。为了安全我们也不允许用户直接去操作主机文件。 大家看到 RunC 上面像这样的你去操作的文件事实上在主机上或者在宿主机上都是有一个代表不管你 Overlay 出来还是快照 DeviceMapper你可以在磁盘上找到这个真实的存储。其实这是一个很大的威胁就是说用户可以直接去操作文件系统。他们操作文件系统之后其实文件系统是有很多 bug 的。它代码量那么大总是有可能突破的。所以我们加了防护层叫 Gofer。Gofer 是一个文件的代理进程用户发出的所有 file 的 read 和 right 都会被我们截获截获完会经过 Gofer 的审查。如果你确实有权限去碰这个文件他才会去给你这个操作这个是大概的一个架构。然后简单讲一下 gVisor 里面是怎么跑的。APP 在 RunC 里面它直接 call 到主机的内核。就是这条红线下图Call 这个内核他该获得哪些 syscall 就会获得 syscall如果假设内核是有什么故障或者 bug这个时候它就可以突破一些限制。在 gvisor 的里面他的执行方是这样的我的 APP 第一步会被 PTrace 或者我的 KVM-Guest 内核捕获捕获之后在我们叫 Sentry为什么这个红线画的划到 kernel 上面因为捕获的过程要么是经过 KVM-Guest-ring0 的要么是经过 PTrace 系统调用所以我认为还是要内核帮忙。然后 sEntry 拿到这个系统调用之后它会去做力所能及的事情比如说你要去读一些 PROC 文件你要去申请文件句柄本地就可以完成服务返回这是非常高效的。然后有一些事情假如你要去读写主机上的一个网卡sEentry 自己确实做不了它就会把这个需求转发到主机内核上去等得到服务之后再原路返回。文件操作就是这样的如果读写任何的主机文件都会去 Call 到 Gofer 的进程审查请求然后代理访问服务去读写真正的文件把结果返回。这个是大家可以看到APP 就是被关在两重牢笼里一个是 Guest Kernelsentry)一个是 Host Kernel。因为它本身又是一个进程所以从安全性上来讲 APP 和 sEntry 不共页表其实可以说比虚拟机还要安全。因为虚拟机里面 Guest Kernel 与 APP 共页表Guest Kernel 躲在这个列表的上端。而在 gVisor 里面 Guest Kernel 跟 APP 是完全不同的两套页表诸如此类有很多方面大家会发现 gVisor 比虚拟机更加安全。当然我们做了这么多隔离也会有副作用就是运行效率会有问题尤其是网络我们都会持续改进把虚拟机已有的一些经验用到 Go 的内核上去我们的理想是虚拟损耗低于 5%。 未来,大家可以去比较各种运行时。当我们选型一个容器的引擎会去综合地看它的运行效率它的安全性尤其是代码复杂度代码越多基本上你可以认为这个东西出 bug 的几率就越高代码越少其实越好。我们跟业界还有一个合作另外我们还在想做对容器运行时做一个测评最后综合打一个分。完成后会开源给大家使用。怎么去评价一个 Runtime 是好的是高效的它的安全性到多少分就跟汽车的这种评分一样的。我今天介绍大概就是这些。推荐阅读5G大规模商用来临之前你必须知道的几个知识点“离开360时它只给了我一块钱”AI找Bug一键快速预测原子互换一统公链江湖的神来之笔春晚鬼畜 B 站日排行最高赵本山我的时代还没有结束PDF翻译神器再也不担心读不懂英文Paper了新闻联播也可以拿来做数据分析高晓松侃5G2019开年大讲揭示运营商的秘密1.微信群添加小编微信color_ld备注“进群姓名公司职位”即可加入【云计算学习交流群】和志同道合的朋友们共同打卡学习2.征稿投稿邮箱liudancsdn.net微信号color_ld。请备注投稿姓名公司职位。喜欢就点击“好看”吧