以Docker为表明的容器技术已经持续成为话题好几年了,本觉得在没有历史包袱的创业公司中,Docker应该会成为生产环境上部署和管理服务的标准配置,然而最近发现一些友商在得知咱们在生产上使用Docker和Kubernetes以后,竟然表现出了一些惊讶。我想对于这些团队没有采纳Docker而是继续使用传统运维方案,仍是以为即便多作一些繁琐的运维工做,也但愿对系统有更多的掌控度。javascript
Docker所依赖的LXC容器技术,早在十多年前就被Google这类大厂使用了,国内的一些大厂也在很早开始投入研发和使用了,对他们来讲,使用容器可以充分利用计算资源,节省硬件开销。然而对于小公司来讲,把10台服务器压缩到5台服务器并不能帮助公司活下去。html
容器技术真正开始普遍产生影响是从Docker的诞生开始,Docker刚出来的时候,官网上的slogan:“Build Ship Run”,准确的阐述了Docker的定位,从打包到部署,传统运维的这一套流程,因为语言,环境,平台的不一样在各个公司千差万别,几乎每一个公司都会开发一套本身的发布系统。有了Docker以后,这些竟然均可以标准化了,而且相对于笨重的虚拟机,Docker几乎就是一个进程简单的包装,只有不多的额外开销,启动速度也几乎至关于直接启动应用进程。java
对于没有专职运维的即刻团队,很天然的从项目开始就使用Docker来作服务的发布工具了:node
因为一个Docker容器只是一个(或一组)进程的封装,一个容器想要向宿主机以外的网络提供服务,就只能绑定宿主机的端口了,端口的管理也是一件大部分状况下都不但愿人去操心的麻烦事,为了不端口冲突,对于须要暴露端口的容器,Docker会随机绑定一个宿主机端口,这个时候咱们就须要服务发现机制来帮助不一样机器上的服务来进行通讯了。linux
咱们使用了一个简单的方案,一套开源的工具:docker-discover和docker-register,它包含两个组件:docker
每台worker node虚拟机上运行一个docker-register容器,用来扫描本地容器,把他们的服务名(用镜像名做为服务名)和端口(包括容器内端口和宿主机端口)注册到etcd上。json
一个docker-discover容器用来作中心代理,它内部包含了一个HAProxy,每隔几秒钟扫描Etcd上的注册服务,并生成配置文件,刷新HAProxy,生成backend配置的模版以下后端
{% for service in services %}
listen {{ service }}
bind *:{{services[service].port}}
{% for backend in services[service].backends %}
server {{ backend.name }} {{ backend.addr }} check inter 2s rise 3 fall 2{% endfor %}
{% endfor %}复制代码
这个方案给咱们提供了一套系统的几个基本功能:应用发布,服务发现,负载均衡,进程守护,其中应用发布是执行脚本去各个worker node上拉取最新镜像,进程守护则是由Docker daemon的restarts=always来提供。服务器
除了提供一致的运行环境使服务的发布和回滚比较可控,这套简单的系统在发布流程上仍是像传统运维同样须要远程执行脚本,功能比较简单,随着咱们后端系统成长起来,很快就不够用了。网络
Docker自己只是提供了一个运行环境,除了把服务跑起来以外,要让多个服务容器协同起来工做,咱们还须要一个容器编排(Orchestration)系统,通常来讲咱们指望编排系统能帮咱们实现几个目的:
基本发布自动化功能:
编排过程包含分配机器,拉取镜像,启动/中止/更新容器,存活监控,容器数量扩展和收缩
声明式定义服务栈:
提供一种机制,能够用配置文件来声明服务的网络端口,镜像及版本,在须要的时候经过配置可再现的建立出一整套服务。
服务发现:
提供DNS和负载均衡,一个容器启动以后,须要其余服务可以访问到它,一个容器终止运行以后,须要保证流量不会再导向它。
状态检查:
须要持续监控系统是否符合配置中声明的状态,好比一台宿主机挂了,须要把上面运行的容器在其余健康的节点上启动起来,若是一个容器挂了,须要把它从新启动。
从设计思路,社区活跃度等因素来看,Kubernetes无疑是编排工具最好的选择,但因为组件较多,学习成本并不低,还有墙的因素,在国内甚至安装都不是件容易的事。
这个时候咱们发现Rancher正式发布了,虽然没有kubernetes那么热门,但它提供了全部咱们须要的功能,还有一个简单容易上手的Web UI。在早期咱们的机器和服务数量都比较少,又急需一个编排工具好把有限精力都投入到开发上,因此迅速的把服务都迁移到Rancher上了。
准确的说,Rancher是一套容器管理打包方案,支持三种编排引擎:Kubernetes,Swarm,还有Rancher本身开发的Cattle(最近好像换成了Mesos)。从功能的完整性和易用性来看,Rancher甚至能够算得上一个商业软件了,部署极其简单,这也是咱们选择它做为入门级容器管理平台的缘由。
Rancher组件图,中小企业经常使用的软件功能都能找到:
虽然Rancher很是的易用,但随着咱们后端机器和项目数量的增长,它的一些问题也暴露出来了,UI卡顿,发布速度愈来愈慢,1.3以后甚至常常出现服务的预期状态(容器数量,版本)没法被保证,卡在发布中或者完成中状态,真正让咱们下定决心要迁移的是一次重大故障,疑似网络雪崩引发,集群全部机器都在重复断开链接,原本按通常编排系统的设计,worker node上的容器之间经过overlay网络通讯,node和Rancher server的链接即便断开也不会影响已经启动的容器运行。但在1.3以后的Rancher中,不知是有意设计仍是bug,worker node从新链接上Rancher server以后,节点上全部的容器会被从新schedule,这就致使集群中全部容器都不断的被销毁又从新建立。
在Rancher上碰到大大小小的问题,咱们发现大部分都很难找到社区提供的解决方案,咱们极可能是不幸比较早踩坑的人,虽然Rancher是开源的,但技术上的文档相比使用文档明显欠缺不少,想经过了解他的实现来排查问题比较困难,也不多有Rancher公司以外的contributor,这点是和Kubernetes很明显不一样的。
和Rancher提供了一整套解决方案不同,Kubernetes提供的是一个框架,对于Rancher,即便不熟悉其中各个组件,也能够直接用预设的配置安装,拿来当一个发布工具使用,Kubernetes则要求使用者对他的组件有必定程度了解,社区提供了不少帮助部署的installer,但使用前都须要很多配置工做。
DevOps同事画的Kubernetes架构图:
在Rancher上积累了一些容器编排的经验以后,咱们对使用Kubernetes作编排也有了一些信心,因而开始迁移到性能更好,社区更活跃的kubernetes上。
咱们将线上服务一点点从Rancher上把流量切换到Kubernetes集群,起初发现流量稍微增涨便丢包严重,定位到问题是DNS解析缓慢,观察kernel log发现宿主机conntrack count到达上限,出现丢包。
这里解释一下iptable里的conntrack:咱们在使用iptable作面向链接的防火墙时,要容许与一个ip地址创建特定链接,除了让该链接下的包能进来,还要让回复的包能出去,因为ip是个无状态协议,这个时候就须要一个session表来记录有状态的链接,conntrack就是用来记录这些session的。在微服务下,服务间调用频繁,会产生大量DNS查询,linux默认的conntrack_max很容易突破限制,因此在部署有DNS service的机器上设置一个较高的conntrack_max值基本上是必须的。
前面介绍了咱们使用容器来作服务编排,除了这些,相对于传统架构,使用容器咱们还得到了一整套日志和监控的解决方案,好比日志采集,部署在容器中的日志能够直接打印到标准输出中,Docker自己支持多种logging driver,能够将日志直接发到Graylog,AWS CloudWatch等日志平台,也可让Fluentd等日志采集工具来采集Docker默认输出的json-file,相比之下传统架构可能须要在应用中使用专门配置的logger输出到收集系统,或者配置专门的采集器去采集不一样的日志文件。
小公司对基础设施的投入不足,通常没有专人去熟悉Kubernetes这种大型开源项目,但即刻算是一个对技术持开放态度,愿意让工程师去踩坑、尝试的公司。相对于采用传统架构,由于容器编排系统都是以提升自己的复杂性来覆盖繁琐的配置和脚本编写工做,自己复杂了就会致使出现问题的时候会比较难排查。但作过运维工做的应该了解,通常本身编写的脚本很难具备通用性,极可能环境稍有改变就不能使用了,运维是比较枯燥的工做,有了编排系统帮助处理这些,咱们能够把更多的精力放到更有意义的工做上。
题图By:蒙天放
参考: