在2017年以前,不少企业宁可自行撰写脚本用以管理容器,也不想去触碰那个“大象”,一直到谷歌将家底呈现出来,所有人才觉得真香了。
为什么非要找工具管容器
我于本地运行一个Nginx容器仅需一行命令,然而到了生产环境,面对几十台服务器以及上百个容器,借助SSH登录逐一进行操作那便是给自己制造麻烦。在2015年我们团队有过教训,在业务高峰时段一台节点出现故障,无人手动迁移容器,直接致使半小时的线上故障。
针对容器管理工具而言,所解决的并非是“能不能跑”的问题,而是“跑了之后能不能存活”的问题,稳定性涵盖其中,自愈能力也在其考虑范畴,伸缩性能同样是其考量之一,监控功能亦是其关注要点,然而这些,仅仅依靠原生容器,是一个都没办法达成要求的,就好比在2016年的时候,我们使用Swarm,即便有官方的支持,可它的功能确实是比较薄弱的,当集群规模超过一百之后,调度就开始出现卡顿的情况了。
谷歌所开源的这一套方案,在二零一五年刚开始现身时,并未被众人所看好,毕竟Mesos已然运行了七年时间,而Swarm又有着Docker这一强大背景的助力。然而,谁都未曾预料到,在二零一七年,AWS、Azure以及Google云同时将其接入,云原生计算基金会也围绕着它展开行动,就这样行业标准被一个仅仅两岁的项目给确定下来了。
集群到底长什么样
最少三台机器构成一个标准集群,其中一台充任大脑,另外两台负责干活,大脑节点装有控制模块,干活节点运行代理程序,所有状态皆存入etcd这个分布式数据库。
CoreOS团队所开源的etcd,借助Raft协议来保障数据一致性。在2017年我们构建测试集群之际,etcd出现过一次故障,致使整个集群陷入瘫痪状态,之后才了解到它仅信任大多数节点处于存活状态,一旦三节点中有两个出现故障就无法进行数据写入。
节点,也就是你所说的物理机或者云服务器,其上均存在kubelet这个角色,它承担着与大脑进行通信的职责,依据指令来创建或者摧毁容器,早期版本时kubelet依赖于Docker,如今其借助CRI接口直接调用运行时,就连containerd也能够进行管理。
核心组件各管一摊
控制平面的大脑们
所有操作的入口是API Server,你所敲下的每一个kubectl命令,最终都会抵达它这里。它并不进行具体的工作,仅仅负责对请求予以验证,进而将数据写入etcd。在2018年的时候,我们遭遇了性能瓶颈,后来经过发现得知是匿名请求过多,给API Server添加了认证以及限流措施之后才稳定下来。
Scheduler仿若房产中介,一旦接到创建Pod的需求,便会前往Node市场挑选最为合适的房子。其看房标准细致入微,涵盖CPU内存是否充足、端口是否有冲突、是否绑定特殊硬盘。Controller Manager犹如一个监工头,当Node出现故障时,它会重启Pod,副本数减少时,它会补充新的副本,以此确保现实中的状态与etcd中存储的期望状态保持一致。
节点上的执行者
Node上的大总管是kubelet,它会定期向API Server汇报健康状态,同时监听本该处在它所在节点运行的Pod清单,要是发觉少运行了就调用容器运行时去创建,要是发觉多运行了就将其杀掉。kube-proxy承担着网络代理的职责,在早期是运用iptables来进行流量转发的,当集群规模达到超过五千条规则之时性能出现了剧烈下跌,直至后来更换成IPVS才把这个问题给解决掉。
在容器运行时的早期阶段,将Docker进行绑定,而如今现在是支持containerd以及CRI - O的。在2020那一年,我们的新集群直接采用containerd,其镜像从拉取的速度上比之前快了30%,并且在内存占用方面也减少至大部分。
抽象概念解决了真实痛点
Pod这个概念刚出现的时候,好多人都不明白,既然容器已经能够运行了,那为啥还要再套上一层呢。实际的情形是,业务容器需要边车容器来进行日志记录、拉取配置,而且这两个容器一定得同生共死,还要共享网络。Pod就是给这样一群容器划分了一套空间,它们共同使用IP、端口、存储卷,在调度的时候进行整体迁移。
Service将IP漂移问题给解决了,Pod重启之后IP必定会发生变化,客户端每次都重新配置是不行的,Service提供了一个固定的虚拟IP,后端挂任何一个Pod都是可以的,就算Pod全部挂掉然后重建,Service依旧能够找到新的Pod,在2019年的时候我们把数据库中间件接入到了Service,宕机迁移对于调用方来说是完全透明的。
Label以及Selector乃是用于组织资源的具有强大威力的手段。针对线上全部的MySQL Pod施加“env=prod,app=mysql”这样的标签,Service凭借Selector便能够确切无误地将它们绑定在一起,在进行扩容操作时,带有相同标签的新Pod也会自动被纳入负载池之中。
存储与配置想得挺周全
卷的设计讲究实在,并未进行重新发明轮子的行为,而是直接与各类存储后端进行对接。我们运用NFS卷来实现静态文件共享,借助hostPath使Pod能够读取宿主机日志,在云上环境则直接采用云盘卷,只需在配置yaml当中更改一下驱动名字便可以达成。
2016年的时候发布ConfigMap解决了大麻烦,此前倘若把配置打包进镜像会致使版本泛滥,要是挂载宿主机文件会造成环境不一致,如今将配置提取出去当作API对象,更新ConfigMap后,Pod滚动重启便可加载新配置,一套镜像能适用于所有环境。
Secret在存储敏感信息之际做过权衡,etcd自身并不进行加密,需要依靠运维人员手工去配置加密。在2021年审计之时发现某个测试集群中Secret存储着明文形式的数据库密码,纵然是在内网环境下也慌慌张张地赶忙补上KMS加密。
设计思想像Linux哲学
仅仅每个组件都只做一件事情,还得是做好的那种,Scheduler仅仅负责调度,而真正去执行创建相关操作的是kubelet;API Server仅仅负责接口交互这一事宜,把数据持久化那边的工作交给etcd去做。呈现如这般的解耦使得替换组件变成了具备可能性的情况,要是不想使用默认的调度器?那就自己去编写一个扩展调度器,它也照旧能够运行。
声明式 API 乃是最大的创新,运维无需发出指令“帮我创建三个 Nginx”,而是提交一份声明“我需要三个 Nginx”,由系统自行设法达成,早年之际我们运用脚本做类似的事情,一旦中间步骤出现失败,还得自己编写代码来处理回滚,声明式内置了这些容错逻辑。
分层架构具备逐步深入的特性,入门借助kubectl,进阶需查看API资源定义,再进一步深入便是能够编写自定义控制器,恰似Linux用户虽不关注内核,然而在有需求之时也能够自行编译模块。
*
你头一回碰到容器管理工具之际,是哪一项功能令你觉着这东西着实比编写脚本更具可靠性呢?欢迎于评论区去分享你的入坑之经历,点赞数超过一百我会专门撰写一篇关于各版本升级的饱含辛酸痛苦的历史。

Comments NOTHING