一、容器云的背景
伴随着微服务的架构的普及,结合开源的Dubbo和Spring Cloud等微服务框架,宜信内部很多业务线逐渐了从原来的单体架构逐渐转移到微服务架构。应用从有状态到无状态,具体来说将业务状态数据如:会话、用户数据等存储到中间件中服务中。
微服务的拆分虽然将每个服务的复杂度降低,但服务实例的数目却呈现出爆炸式增长,这给运维增加难度,一方面是服务部署、升级,另一方面是服务的监控故障恢复等。
在2016年,容器技术尤其是Docker迅速流行起来,公司内部开始尝试将容器放到容器内运行,虽然通过容器解决了服务发布问题,但很多容器的运维仍然让运维捉襟见肘。宜信是一家金融科技公司,在引入开源组件的时候,稳定可靠是作为考量的最重要标准,在2017年初kubernetes慢慢成熟,成为容器的管理标准,并且被国内外很多公司采用,在这种背景下,宜信借鉴开源社区和商业PAAS平台产品,基于kubernetes自研一套容器管理平台。
二、整体架构
整个架构围绕kubernetes构建,分为四个层级,最底层主要是基础资源,包括网络、计算、存储,所有的容器都是部署在物理服务器上,容器挂载商业NAS存储,网络通过vxlan互连;中间层核心的是资源调度层,主要完成多集群的管理、发布部署、智能调度、自动伸缩等,这层主要是资源管理和服务编排;左侧面是提供系统安全,主要是为了系统安全和容器镜像安全,右侧面是一套代码自动编译、自动构建、自动部署系统;中间件层主要提供常用的中间件服务,Nginx配置和监控告警等;最上层的是用户接入层,主要提供用户的操作入口。整体架构如下图所示:
三、Nginx自助管理
公司大部分的服务都是通过Nginx反向代理对外提供服务,为了服务的隔离和负载均衡,总计十几套的Nginx集群,这些nginx的版本、配置方式各有不同,导致单纯靠人工去运维的成本非常高而且容易出错,并且容器的IP地址不固定,无法直接配置到nginx后端。自研了一套nginx管理系统,主要是为了解决nginx的模板化配置,如下图所示:
Nginx-mgr提供HTTP请求,负责接收nginx配置请求,并更新到etcd,每个nginx-agent通过watch Etcd批量刷新nginx的配置。在实际的生产环境里,部署的是阿里开源的Tengine而并非nginx,由于配置基本相同不做区分。每个服务都配置了健康检查,这样能够保障在后端故障中自动切换。如果有虚拟机场景需要手动切换,下图展示了手动切换nginx的页面:
由于很多业务都是虚拟机和容器混跑的情况下,如果后端是容器,我们通过kubernetes的API获取容器的IP地址动态刷新。
四、多集群管理
虽然kubernetes本身存在采用高可用的部署架构,避免单点故障,但这远远还不够,一方面是因为单个kubernetes集群部署在一个机房,如果发生机房级别的故障,将会导致服务中断,另一方面由于单个kubernetes集群本身故障,如集群的网络配置错误导致整个网络故障等,都将会影响业务的正常使用,在宜信将kubernetes部署在多个机房内,机房之间通过专线互连。那么多集群的管理将成为主要难点:第一是如何分配资源,当用户选择多集群部署后,系统根据每个集群的资源用量,决定每个集群分配的容器数量,并且保证每个集群至少有一个容器。集群自动伸缩时,也会按照此比例创建和回收容器。第二是故障迁移,如图中的集群控制器主要为了解决多集群的自动伸缩和集群故障时的容器迁移,控制器定时检测集群的多个节点,如果多次失败后将触发集群容器迁移的操作,保障服务可靠运行。
第三是网络和存储的互连,由于跨机房的网络需要互连,我们采用vxlan的网络方案实现,存储也是通过专线互连。容器的镜像仓库采用Harbor,多集群之间设置同步策略,并且在每个集群都设置各自的域名解析,分别解析到不同的镜像仓库。
五、DNS解析
由于业务人员对容器技术还存在疑虑,所以大部分应用都是虚拟机和容器的混合部署,容器通过域名访问虚拟机和虚拟机通过域名访问容器都是普遍存在的,为了统一管理域名,我们没有采用kubernetes自带的kube-dns(coreDns)而采用bind提供域名解析。通过kubernetes支持的Default DNS策略将容器的域名指向公司的DNS服务器,并配置域名管理的API动态添加。
六、网络方案
kubernetes的CNI的网络方案有很多种,主要分为二层、三层和overlay方案。一方面机房并不允许跑BGP协议,并且需要跨机房的主机互连,所以我们采用了flannel的vxlan方案,为了实现跨机房的互通,两个集群的flannel连接到同一个etcd集群,这样保障网络配置的一致性。老版本的Flannel存在很多问题,包括:路由条数过多,ARP表缓存失效等问题。建议修改成网段路由的形式,并且设置ARP规则永久有效,避免因为etcd等故障导致集群网络瘫痪。
Flannel的使用还需要注意一些配置优化,默认情况下每天都会申请Etcd的租约,如果申请失败会删除etcd网段信息。为了避免网段变化,可以将etcd数据节点的ttl置为0(永不过期);Docker默认是会masq所有离开主机的数据包,导致flannel中无法获取源容器的IP地址,通过设置Ipmasq添加例外,排除目标地址为flannel网段数据包;由于flannel使用vxlan的方式,开启网卡的vxlan offloading对性能提升很高。Flannel本身没有网络隔离,为了实现kubernetes的network policy我们采用canal,它是calico实现kubernetes的网络策略的插件。
七、CICD
为了支持Devops流程,在最初的版本我们尝试使用Jenkins的方式执行代码编译,但Jenkins对多租户的支持比较差。在第二版通过kubernetes的Job机制,每个用户的编译都会启动一个编译的Job,首先会下载用户代码,并根据编译语言选择对应的编译镜像,编译完成后生成执行程序,如果jar或者war文件。通过Dockerfile打成Docker镜像并推送到镜像仓库,通过镜像仓库的webhook触发滚动升级流程。
八、服务编排
系统设计了应用的逻辑概念,kubernetes虽然有服务的概念,但缺少服务的关联关系,一个完整的应用通常包括前端、后端API、中间件等多个服务,这些服务存在相互调用和制约的关系,通过定义应用的概念,不仅可以做到服务启动先后顺序的控制,还可以统一规划启停一组服务。
九、日志
容器的日志归集使用公司自研的watchdog日志系统,每台宿主机上通过DaemonSet方式部署日志采集Agent,Agent通过Docker API获取需要采集的容器和日志路径,采集日志并发送到日志中心,日志中心基于elasticsearch开发,提供多维度日志检索和导出。
十、监控
容器本身资源监控的性能监控通过Cadvisor + Prometheus的方式,容器内业务的监控集成开源的APM监控系统uav(https://github.com/uavorg/uavstack),完成应用的性能监控。uav的链路跟踪基于JavaAgent技术,如果用户部署应用勾选了使用uav监控,系统在构建镜像时将uav的agent植入到镜像内,并修改启动参数。
除了上述几个模块外,系统还集Harbor完成容器镜像的多租户管理和镜像扫描功能;日志审计是记录用户在管理界面的操作,webshell提供用户的web控制台接入,为了支持安全审计,后台会截获用户所有在webshell的操作命令并记录入库;存储管理主要是集成公司商业的NAS存储,为容器直接提供数据共享和持久化;应用商店主要是通过kubernetes的operator提供开发和测试使用的场景中间件服务。
十一、落地实践
11.1 docker不是虚拟机
在容器推广的初期业务开发人员对容器还不是很熟悉,会下意识认为容器就是虚拟机,其实他们不仅是使用方式的区别,更是实现方式和原理的差异,虚拟机是通过模拟硬件指令虚拟出操作系统的硬件环境,而容器是在共享内核的前提下提供资源的隔离和限制。下图展示了4.8内核中linux支持的7种namespace。
换句话说,其他的都没有差异,譬如,时钟,所有容器和操作系统都共享同一个时钟,如果修改了操作系统的时间,所有容器都时间都会变化。除此之外,容器内proc文件系统也是没有隔离,看到的都是宿主的信息,这给很多应用程序带来困扰,JVM初始的堆大小为内存总量的1/4,如果容器被限制在2G的内存上限,而宿主机通常都是200+G内存,JVM很容易触发OOM, 解决方案通常是启动时根据内存和CPU的限制设置JVM,或者借助lxcfs等。
Cgroup的资源限制目前对网络和磁盘IO的限制比较弱,v1的cgroup只支持direct IO的限制,但实际的生产环境都是些缓存的。目前我们也在测试cgroup v2关于IO的限制。当最新的CNI已经支持网络限速,结合tc可以很好的达到这个效果。
11.2 Kubernetes优化
Kubernetes自带了很多调度算法,在启动容器之前会通过调度的算法,这些算法都是需要过滤所有的节点并打分排序,大大增加了容器的部署时间,通过删除一些无用的调度算法,从而提高部署的速度。容器采用反亲和的策略,降低物理机故障对服务造成的影响。
虽然kubernetes开启了RBAC,但kubernetes token还是不建议挂载到业务容器内,通过关闭ServiceAccountToken提升系统的安全。
Docker镜像存储使用direct-lvm的方式,这样性能更优,在部署的时候划分单独的vg,避免因为Docker问题影响操作系统。通过devicemapper存储限制每个容器系统盘为10G,避免业务容器耗尽宿主机磁盘空间,容器运行时需要限制每个容器的最大进程数量,避免fork炸弹。
Etcd里面记录了kubernetes核心数据,所以etcd个高可用和定时备份是必须的,在kubernetes集群超过一百个节点以后,查询速度就会降低,通过SSD能够有效提升速度。本系统在kubernetes之外通过数据库保存服务和
关注证书的有效期,在部署kubernetes集群时候,很多都是自签的证书,在不指定的情况下,openssl默认一年的有效期,更新证书需要非常谨慎,因为整个kubernetes的API都是基于证书构建的,所有关联的服务都需要修改。
十二、总结
Docker容器加K8S编排是当前容器云的主流实践之一,宜信容器集群管理平台也采用这种方案。本文主要分享了宜信在容器云平台技术上的一些探索和实践。本文主要包含了Nginx自助管理、 多集群管理、DNS解析、网络方案、CICD服务编排、 日志监控、kubernetes 优化一些技术工作,以及宜信内部容器云平台化的一些思考,当然我们还有很多不足,欢迎各路英雄来宜信进行深入沟通和交流!
作者:陈晓宇
来源:宜信技术学院