关于容器化以及 k8s 的一点个人思考

Posted 霍思通

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于容器化以及 k8s 的一点个人思考相关的知识,希望对你有一定的参考价值。

本文均为个人工作中的一些理解,可能存在纰漏、疏忽、认知错误的地方,敬请谅解,欢迎评论区讨论。

1、序

一般来说,如果说要做容器化,那就是两个方向,docker swarm或者k8s,前者复杂度低一些,易于从docker-compose转换。后者功能完善,但是学习和维护难度很高。

个人认为,如果计划做容器化,目前几乎没有什么理由去选择swarm了。除了ConfigMap、Secret,或者Ingress,k8s好用的功能还有不少,swarm几乎没有什么优势。
不过如果是计划将零散的docker-compose部署的服务快速聚合起来,统一管理,那swarm 还是可以一用的。

但是我这里为什么要单独把 容器化 和 k8s 分开说呢?因为目前所在的公司,采取的策略就是只做“容器化”:
将服务环境,代码,配置文件等所有依赖项打包为基础容器和Dockerfile,使用docker-compose的方式控制。除此之外,其余所有因素不变。
从服务部署的角度来说,服务确实被打包了,如果需要扩容/迁移,docker容器自然是比直接在宿主机上部署方便了许多。但是停留在这个阶段,我觉得不合理。

2、现在的“容器化”

简单来说,我们目前的“容器化”是这么做:

  • 将原来的部署文档,整理转化成一个dockerfile+docker-compose.yaml
  • 代码仓库配置上CI(jenkins/gitlab-ci,均可),每次代码合并后打包一个新的镜像。
  • 使用新打包的镜像将代码和环境一起发布,而非仅发布代码。

我们也确实有了一些收益:
1、每当服务器扩容时,无需在服务器上配置服务环境,只需安装docker+部署服务,如果后续服务器转做他用,也不用担心是否会有影响。
2、服务一致性得到保证,一样的代码可以得到一样的结果,宿主机很难影响到容器,几乎不会出现同一个版本代码在不同服务器上表现不一致的情况。
但是运行一段时间后,我觉得这样做的收益并没有很高:
1、没有办法纵观全局,观测所有服务,每个服务的运行状态还是需要登录对应的服务器才能看到。这样和不做容器化没有什么区别。
2、nginx配置并不能与容器联动,一旦容器需要更换服务器还是需要手动更新(静态维护,compose写死docker内网IP)
3、虽然配置了服务日志收集,但是一旦服务部署的服务器有变动,还是需要人工修改日志收集的agent。

正如这一段的标题,为什么不继续向前走?做的更多?你上swarm/k8s不就能在master上管理整个集群了吗?
是的,无论是swarm还是k8s,都是可以选择的,但是由于现实的种种原因,目前只能停留在这一步。

3、理想中的容器化

把代码和环境打包在一起只是容器化的第一步,还需要继续往前走,才能解决残留问题。静态维护容器,其实有点把容器当虚拟机的意味,只不过每个虚拟机里只有一个服务。理想情况下,容器变动导致的路由问题和服务间互相发现/访问都应该是自动的,而非静态。
无论是直接使用docker,还是用docker swarm还是有很多问题需要人为去解决。而k8s都有方案。
基于我对于k8s浅显的理解,我觉得,推进容器化,应该有以下几个问题要关注和解决:
1、配置和日志:
配置:可以直接用ConfigMap+Secret解决。
日志:Sidecar模式比较不错,直接在Pod内启动一个agent负责这个Pod内的所有文件日志收集。Pod之间不共享。
日志服务端也有几个可选项,一般用ES,如果日志量很小也可以用loki(日志正文无索引,但是更轻便)。

虽然通过stdout/stderr收集日志也可以,但是这样没有Sidecar方式灵活。而且日志可以通过文件名进行更多区分。一般还是会选择Sidecar方式收集日志。

这两个问题解决了,容器就已经可以基础的运行:

  • 从git仓库拉取代码,打包成镜像,通过Deployment发布
  • 从ConfigMap+Secret 读取+挂载配置,而且这两者也支持动态更新。
  • 每个Pod都使用Pod内的agent收集业务日志,发送到服务端。

2、请求路由和服务发现/服务间的互相请求:
除了上述这个点,还有一些点需要去讨论:容器在调度过程中会发生变动,被杀死或者重启的。

  • 如何将外部的请求顺利导入到目标容器?
  • 容器间的发现和互相访问如何实现?

Ingress我觉得可以算是k8s的入口网关了,一个Ingress资源需要一个实际的Ingress Controller才能实现。一般来说,用Ingress-nginx就可以。
其次,Service可以将服务抽象,对外提供一个稳定的虚拟endpoint,可以用于连接,对内可以按照label转发请求到Pod上。
这样访问Service就可以将请求发送到对应的Pod,而不需要直接访问Pod。

关于服务发现和服务间的请求,coreDNS完全可以解决。每个Service创建后,都会在DNS内留下记录,其他服务可以直接使用DNS访问
一般来说,域名全程为:<service-name>.<namespace>.svc.cluster.local,后面的可以省略,指定<service-name>.<namespace>就可以确切访问到服务了,通过这样简单的方式进行服务发现,那么服务间的互相访问也自然没有太大问题。

这两个问题解决之后,请求可以顺利从外部路由到目标容器,荣期间也可以顺利互相发现/互相访问。

4、道路还有多远?

那么如果要跨越这一步,实现完整的容器化,距离还有多远?还需要解决什么问题?
虽然无法实际去操作,去将当前的项目改造成k8s,但是这并不妨碍思考一下,“现在距离完全k8s化有多远,还需要解决那些问题。”

先从单个容器来说:
代码配置方面,是在阿里云ACM上,代码直接拉取的。改为ConfigMap应该没有太大问题。
日志收集方面,目前是使用的promtail做为agent,将日志发送到loki。改为Pod内增加一个promtail容器也应该没有太大问题
promtail的配置可以存在ConfigMap上,不同的服务按照命名区分就可以了。

还有一个要注意的点,就是docker一定要配置容器日志大小限制,以免docker日志打满。
由于日志会先写服务容器内,再被promtail容器读,不需要保留太多,一般也不需要直接登录容器看文件日志。

服务路由和服务发现方面:
从nginx静态路由改为Ingress-nginx因该也不会有太大困难,把现在的nginx配置导入,后端转发到Service就可以了。
目前服务发现很简单粗暴,直接访问其他内网域名(每个服务配置了一个内网域名),从nginx绕一圈。这个改动并不大,只不过从内网域名改成Service域名就可以了。

部署发布方面:
目前使用jenkins+gitlab 来实现代码发布,测试和预发布环境都是分支触发,对应分支有代码提交就会触发部署。(特殊分支只能merge,不允许直接提交)
这部分应该不需要太大改动,只是实际的部署执行步骤需要改一下。

服务监控打点方面:
目前对于服务自己的监控没有太大需求,现在服务自身没有任何打点。
如果要用的化,还是promtheus比较合适,官方对于java、go都有客户端。自定义打metric难度不大。

目前来说,我觉得这些问题如果都能实际解决,应该就能比较好的完成迁移。当然实际过程中必然也会遇到很多问题,不过见招拆招嘛,有问题解决问题就可以了。

5、小结:

在结论部分,我觉得要先强调的一点是:不同人看待问题的角度不同,不能说谁对谁错。
从运维的角度来说,上k8s很好,部署新服务很简单,服务器扩容/缩容也方便了许多,不再强依赖宿主机等等。
但是从全局管理的角度来说,可能会有很多考虑:
1、现在的痛点是否可以接受,有没必要马上上k8s,上了之后会不会有什么问题。
2、研发都需要对k8s有一定理解,每个研发都需要再学习。否则在开发/调试/查问题上有影响。
3、相比于nginx静态配置+静态服务来说,k8s动态的地方多了很多,依赖也多了很多。维护整个系统需要关注更多的内容(比如etcd)一旦出现问题需要更多的知识才能调查清楚。

简而言之,引入k8s,对于运维角度来说,无疑是大好事,各方面都更好。但是,无论是引入k8s还是引入其他新的技术,都需要综合权衡,才能让服务越来越好。

以上是关于关于容器化以及 k8s 的一点个人思考的主要内容,如果未能解决你的问题,请参考以下文章

关于图像是如何运动的一点思考

关于样本方差以及样本协方差的一点思考

技术选型的一点个人思考

关于 Python Iterator 协议的一点思考

关于聚焦目标与排除干扰的一点思考

最近的一点思考,关于高手/大师/学霸