您的位置:澳门新葡8455最新网站 > 编程教学 > 一个可供参考的公司应用容器化试行案例,创办

一个可供参考的公司应用容器化试行案例,创办

发布时间:2019-10-10 12:23编辑:编程教学浏览(161)

    以Docker为代表的容器技术已经持续成为话题好几年了,本以为在没有历史包袱的创业公司中,Docker应该会成为生产环境上部署和管理服务的标准配置,然而最近发现一些友商在得知我们在生产上使用Docker和Kubernetes之后,居然表现出了一些惊讶。我想对于这些团队没有采纳Docker而是继续使用传统运维方案,还是觉得即使多做一些繁琐的运维工作,也希望对系统有更多的掌控度。

    图片 1

    Docker所依赖的LXC容器技术,早在十多年前就被Google这类大厂使用了,国内的一些大厂也在很早开始投入研发和使用了,对他们来说,使用容器能够充分利用计算资源,节省硬件开销。然而对于小公司来说,把10台服务器压缩到5台服务器并不能帮助公司活下去。

    本文分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联合CoreDNS,让平台的使用者不用记录灵活多变的IP地址,仅需自己定义自己熟悉的hostname(或者由平台自动生成)就访问对应资源,让使用者几乎没有感知的使用容器还是物理机或者虚拟机。
    在这里跟大家分享一下容器使用方面的经验,说的不好,肯定有不对的地方,还希望大家多多批评、指正。

    容器技术真正开始广泛产生影响是从Docker的诞生开始,Docker刚出来的时候,官网上的slogan:“Build Ship Run”,准确的阐述了Docker的定位,从打包到部署,传统运维的这一套流程,由于语言,环境,平台的不同在各个公司千差万别,几乎每个公司都会开发一套自己的发布系统。有了Docker之后,这些居然都可以标准化了,并且相对于笨重的虚拟机,Docker几乎就是一个进程简单的包装,只有很少的额外开销,启动速度也几乎相当于直接启动应用进程。

    OpenShift简介

    图片 2

    我们都知道在容器编排领域,有3个最著名的编排系统,它们分别是Docker Swarm,Mesos和Kubernetes。其中Docker Swarm是Docker公司自己推出的容器管理和编排系统,由于起步较晚,目前大规模的应用尚不多见。
    Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发的,后在Twitter得到广泛使用。
    Kubernetes和Mesos同样是起源于Google的Borg,是现在应用最广,用户数和社区活跃度最高的容器编排系统。OpenShift也是出于这个考虑,最终选择Kubernetes作为底层架构。OpenShift是一个开源容器云平台,他是基于主流的容器技术Docker和Kubernetes构建的云平台,作为一个开源项目,他已经有5年的历史,其最早的定位是一个应用云平台(Platform as a Service)。在Docker时代来临之前,各个厂商和社区项目倾向构建自己的容器标准,比如Cloud Foundry的Warden、OpenShift的Gear,但是在Docker成为主流及社区的技术发展方向之后,OpenShift快速的拥抱了Docker,并推出了市场上第一个基于Docker及Kubernetes的容器PaaS解决方案。当然很多人会有疑问,OpenShift与Docker及Kubernetes的关系究竟是什么?
    OpenShift是基于容器技术构建的一个云平台。这里所指的容器技术即包含Docker及Kubernetes。如下图所示:

    图片 3

    OpenShift底层以Docker作为容器引擎驱动,以Kubernetes作为容器编排引擎组件。OpenShift提供了开发语言、中间件、自动化流程工具及界面等元素,提供了一套完整的基于容器的应用云平台。OpenShift有三个版本,分别是企业版,社区版和在线版,我们这里使用社区版(origin版)用做我们企业内部的部署。

    对于没有专职运维的即刻团队,很自然的从项目开始就使用Docker来做服务的发布工具了:

    部署介绍

    图片 4

    我们的架构比较典型,前端有2个负载均衡器(HAProxy),两个负载均衡通过Heartbeat共享一个VIP,后面连接3个master node,每个master node上分别运行了一个etcd,后面连接了N个slave node,因为集群中所有的状态都会持久化到etcd中,所以api server基本上是无状态运行,当有节点需要和master node打交道时,先访问这个VIP,然后连接到vip后面的一个HAProxy上,HAProxy再选择其中一个master node上的api server进行与etcd通信,下面是我的HAProxy的配置文件haproxy.conf:

    backend atomic-openshift-api
        balance source
        mode tcp
        server      master0 10.10.xx.xx:8443 check
        server      master1 10.10.xx.xx:8443 check
        server      master2 10.10.xx.xx:8443 check
    

    同时,其实我们如果集群里面有其他的高可用的需求,比如我们使用了Harbor做为私有镜像仓库,三个镜像仓库配置了Replication规则,我们通过VIP推送一个镜像到3个中的任意一个仓库,其他两个仓库也会存在我们的同样镜像,从而实现高可用。下面是Haproxy的配。

    backend harbor-proxy
        balance source
        mode tcp
        server      harbor1 10.10.xx.xx:80 check
        server      harbor2 10.10.xx.xx:80 check
        server      harbor3 10.10.xx.xx:80 check
    

    第一阶段:映射宿主机端口 + HAProxy转发

    运行流程

    图片 5

    第一个是每天定时的作业,这个比较简单,我们快速过一下:首先在Jenkins里做定时任务,时间定为0:30,从代码下载代码,在编译之前进行代码风格检查(snoar)和执行单元测试(junit),然后进行编译,把编译出的二进制包和事先写好的Dockerfile进行docker build,然后把输出的docker image push到Harbor的镜像仓库,同时这个编译的jenkins job会触发,测试部署job,测试部署job执行时会触发OpenShift里的test project,我们把PullPolicy设置成alway是,每次执行从新部署,都会触发新的镜像的拉去,从而达到从新部署的目的,测试项目分为两个阶段,自动化测试和压力测试,自动化测试采用selenume和robot结合的方式,在测试结束之后生成测试报告。
    压力测试使用jmeter。结束之后也会以邮件的方式发送给订阅者。如果所有测试通过之后,测试job会先打给这个image打一个tag,然后push到Harbor的ready项目中。同时发送通知邮件。
    待第二天测试人员需要测试的时候,它们会通过我们的release manager去拉去ready项目中最新的image,然后部署到它们自己的project里,进行特定功能的手工测试。整体流程大概如此。
    第二个是针对开发测试环境的,由于资源有限,我们的开发人员也用容器的方式部署每个人的开发环境,这里OpenShift/Kubernetes多租户的优势就体现出来了。在我们的容器平台上,每一个开发人员对应一个账号,每一个账号就是我们OpenShift里的一个project,由于每一个project都可以做资源限制,所以只要给大家按照需求分配好固定份额,就可以做到资源的共享和互不干涉。举个例子,比如说,我们有一个项目,它分为前端和后端。
    前端人员开发的时候,他只关心前端代码,后端代码基本仅仅是调用而不做修改,那么这个时候我们就可以把前端打成一个docker image,后端打成一个docker image(这里是举个例子,实际情况可能比较复杂,可能前后端都不止一个image),然后在前端开发人员写完前端代码后,把自己的代码通过NFS共享给Release Manager,(NFS挂在可以是实现挂载好的,Windows、Linux、MacOS均可以,代码就可以直接存在上面),然后点击Relea se Manager上面的前端发布按钮,就会触发Jenkins中的一个front-end job,它会完成代码编译,打包和镜像推送,最后在这个开发人员对应的project里从新部署前端代码的整个过程,接着这个开发人员就可以通过url(比如
    如果是后端人员也会有同样的流程,具体就不在赘述了。具体说明一点,由于OpenShift集成了Router功能,也就是类似于Kubernetes里的Ingress,但是它是用Haproxy实现的,并且能够在Yaml文件中对Router进行配置,刚才引用的url我们可以配置成
    生产和预发布环境,这个环境的配置除了要求高可用之外,就是环境的硬件配置,一般意义上来将,预发布的环境因该不高于正式生产环境。当决定对测试通过的镜像版本进行上线时,首先会用docker tag把它换成tag换成pre-release,经过压力测试和手工最后的verify就可以发布到正式环境中了。

    由于一个Docker容器只是一个进程的封装,一个容器想要向宿主机之外的网络提供服务,就只能绑定宿主机的端口了,端口的管理也是一件大部分情况下都不希望人去操心的麻烦事,为了避免端口冲突,对于需要暴露端口的容器,Docker会随机绑定一个宿主机端口,这个时候我们就需要服务发现机制来帮助不同机器上的服务来进行通信了。

    调试问题的解决

    图片 6

    下面我们着重讲一下在实际部署和使用容器过程中,遇到了哪些问题,以及是如何去解决:
    1、有一个问题也许很多刚刚使用容器的公司都会遇到,就是开发人员喜欢把容器当成虚拟机用,在遇到程序bug的时候,很多开发者都喜欢SSH到容器里,亲自看看log,或者是尝试替换一下它们程序的debug版(这里的所谓debug版,就是开发人员在代码里加入一些调试信息或者print一下log),然后重新启动应用。这里我们不推荐给容器内部安装SSHD,因为首先容器的IP是临时分配的,我们无法确定的告诉开发者它这一次的IP地址是多少,即使告诉了他也不一定能够访问的到(我们的容器系统网络层和外界不是一个网段),那么如何解决这个问题呢?
    我们开始也是尽量说服开发者,学会用log去debug,因为我们前面已经把log通过ES进行了收集,用Kabina可以去查看,但是没有办法,有些开发人员还是习惯自己去cat或者vim打开日志文件。所以这里我们就用到了OpenShift里提供的一个oc子命令oc get pod和oc exec,前者用来得到当前用户所在的项目中的Pod列表,后在类似docker exec命令可以直接跳进容器里(Kubernetes中也提供类似的命令),当开发人员需要把里面的日志文件拷贝出来,后者是拷贝一个debug版本的程序到容器里去运行时,可以用oc cp(同样这个在Kubernetes里也有类似的命令)。
    虽然这几个命令据我观察有一些bug,比如拷贝的目标目录不太准确,而且对容器里的tar命令有一些依赖。但这都是小问题,不影响使用,如果觉得几条命令结合起来使用有些麻烦,可以自己用Python脚本进行一个简单的封装,这样当开发人员使用的时候会更加简单。
    2、每一个系统几乎都或多或少的使用了一些第三方工具,比如MySQL、MyCAT、Redis、ZooKeeper,这些组件我们都把它们进行了容器化,以便实现资源的整合和方便部署。那么这就引发了另一个问题,开发人员在碰到程序bug的时候,往往需要直接去连接这些第三方组件,去改修改和查看里面的信息。比如他需要查看Redis的键值是否存在,查看数据是否写入到了数据库里。
    这个时候,由于所有组件在容器中,你不知道它的准确IP,你可能很容易想到用OpenShift提供的router功能去像刚才的url那样提供外界的访问,但是由于这些中间件是4层的协议,而现有OpenShift的Router功能仅仅支持7层协议,所以我们为了解决这个问题就必须实现OpenShift的4层代理功能。
    通过修改Openshift的源代码haproxy-templte.conf和router部分的相关代码,然后通过yaml route的annotation段,定义一个规则,把对应的端口传进router的配置文件,让后端的第三方应用程序通过router节点对应的端口(Haproxy里的mode tcp)从而实现router代理4层协议的目的,但是这会导致另一个问题,因为router的每一个端口只能映射给一个后台应用,比如3306端口,只能映射给一个MySQL,如果有两个开发人员都要调试MySQL,那第二个开发者的MySQL的映射端口肯定就得用除了3306以外的端口(比如3307、第三个人3308等)那么就会产生一个问题router的映射端口如何告诉开发人员呢?
    这个问题有两个解决办法,第一个是通过一个Web UI,去显示的告诉开发人员他所有的资源对应的router节点的端口号,但是这有一个不方便的地方,如果的资源对应的Pod被重置了,那么他的端口号也就会被改变,即使端口号不改变,记起来也比较麻烦(大型的项目可能要用到5、6个中间件产品,每一个开发人员都要记自己的那套环境的端口号,还要学会如何用自己的工具去修改默认的端口号显然给开发者添加了许多不必要的麻烦。那么基于此问题的考虑,我们使用了第二种方法,即我们内部实现了一个DNS,开发者每个人的资源的IP都可以用DNS查到,比如developer1,他有Redis、MySQL、MyCAT、ZooKeeper、PostgreSQL等资源,那他的这些资源对应的DNS域名为:developer1.redis developer1.mysql developer1.mycat developer1.zookeeper等,但是由于DNS只能返回IP地址,无法返回端口号,我们还是得不到router节点对应资源的端口号。
    为了解决这个问题,我们可以用Haproxy加别名的方式,比如运行命令ifconfig eth0:1 10.10.xx.xx意思就是给eth0的NIC,加上了一个虚拟的IP10.10.xx.xx,那么此时,这个网卡eth0就有两2个IP,并且此时都能ping通。(当然实际实现的时候,我们不可能用是ifconfig命令完成这个网卡别名的方式,那样太low,也太不可靠了,我查看了ifconfig的源代码,把其中设置IP地址的代码集成到了我们修改过的OpenShift里)。然后router的haproxy.conf做bind的时候就需要指定这个IP,端口号还用原来这个应用默认的端口号如下图所示,这样开发人员不用每次都记住不同的端口号,仅仅配置一个DNS,不管环境发生了什么样的改变,都可以用默认端口和hostname去连接自己的资源进行调试。
    3、配置Docker容器的时候,默认使用的是DeviceMapper方式,然而这种方式有众多的限制,可以参考

    我们使用了一个简单的方案,一套开源的工具:docker-discover和docker-register,它包含两个组件:

    Q&A

    图片 7

    Q:所有开发人员都是用一套OpenShift集群测试吗?CI/CD也是同一套环境吗?A:我们是按业务分的,原则上,一套业务线(一个业务部门)用一套系统,这样成本上也好分摊。 Q:OpenShift也是用Go编写的?A:是的,OpenShift用的是源码级别的和Kubernetes的集成,不是通过client-go或者REST API的方式,所以我们可以看到,Kubernetes发型的版本总是比OpenShift的快1 到2个版本。 Q:对于OpenShift比较适合多大规模的团队?A:这个怎么说呢,其实引入DevOps或者CI/CD的流程就是为了给企业减少人员成本,让有些能够自动化的东西通过计算机运行起来。所以因该是人员越少越好,但是人员如果少,就要求每个人的技术能里比较强,开源的东西往往用起来不难,但是真到出了问题的时候就需要看代码去解决了。所以如果人少的话,可能每个人就要求懂得就比较多一些。 Q:router本身是否具备HAProxy?A:OpenShift的Router就是用HAProxy实现的,当然我现在用的是3.6.1的版本,我不知道以后会不会支持Nginx或者其他别的LB,因为我看到代码里已经有关于Nginx的相关配置了,但是没有激活。OpenShift使用HAProxy的大致流程就是通过一个Yaml或者jason文件,配置一条route信息,然后通过api-server持久化到etcd中,router的代码启动一个goroutine,去通过api-server watch etcd,然后根据配置信息和环境变量,通过haproxy-template模版,去生成 haproxy.conf,然后去动态reload。 Q:OpenShift的project和Kubernetes的namespace是一对一的关系么?project可以设置资源配额么?怎么设的?A:是一对一关系,当然有一些namespace 是有一些特殊意义的,不建议在里面跑应用。project可以设置资源配额,具体怎么设置就比较复杂了,建议参考一下官方文档,简单的说就是可以根据CPU内存做资源的限定,这个和Kubernetes是一样的。 Q:OpenShift中原生性能指标监控服务的Pod总挂有没有相应的解决办法?A:解决Pod总挂的问题就得具体问题具体分析了,我记得它做性能监控的那个Pod比较吃资源,其实可以对他进行一下限定,比如:oc env rc hawkular-cassandra-1 MAX_HEAP_SIZE=1024M -n openshift-infra。 Q:OpenShift中的router默认情况下是跑在Pod里的,那么当service特别多,route规则也特别多的时候,如何解决router服务的性能问题的?A:这是一个好问题,但其实我觉得这个和HAProxy有很大的关系,跟在不在Pod中关系不大,因为router这个容器其实用的是主机网络,那么这个问题其实就转化成了如何提升HAProxy的性能,这种情况其实有很多可以参考的方案,比如前面在加一层LVS负载,或者用DNS做域名解析的时候进行一定的负载功能。
    基于Kubernetes的DevOps实践培训

    图片 8

    本次培训包含:Kubernetes核心概念;Kubernetes集群的安装配置、运维管理、架构规划;Kubernetes组件、监控、网络;针对于Kubernetes API接口的二次开发;DevOps基本理念;微服务架构;微服务的容器化等,点击识别下方二维码加微信好友了解具体培训内容

    图片 9

    2月1日正式开课,还有最后3个名额,点击阅读原文链接即可报名。

    每台worker node虚拟机上运行一个docker-register容器,用来扫描本地容器,把他们的服务名(用镜像名作为服务名)和端口(包括容器内端口和宿主机端口)注册到etcd上。

    一个docker-discover容器用来做中心代理,它内部包含了一个HAProxy,每隔几秒钟扫描Etcd上的注册服务,并生成配置文件,刷新HAProxy,生成backend配置的模版如下

    图片 10图片 11

    这个方案给我们提供了一套系统的几个基本功能:应用发布,服务发现,负载均衡,进程守护,其中应用发布是执行脚本去各个worker node上拉取最新镜像,进程守护则是由Docker daemon的restarts=always来提供。

    除了提供一致的运行环境使服务的发布和回滚比较可控,这套简单的系统在发布流程上还是像传统运维一样需要远程执行脚本,功能比较简单,随着我们后端系统成长起来,很快就不够用了。

    第二阶段:Rancher

    Docker本身只是提供了一个运行环境,除了把服务跑起来之外,要让多个服务容器协同起来工作,我们还需要一个容器编排(Orchestration)系统,一般来说我们期望编排系统能帮我们实现几个目的:

    基本发布自动化功能:

    编排过程包含分配机器,拉取镜像,启动/停止/更新容器,存活监控,容器数量扩展和收缩

    声明式定义服务栈:

    提供一种机制,可以用配置文件来声明服务的网络端口,镜像及版本,在需要的时候通过配置可再现的创建出一整套服务。

    服务发现:

    提供DNS和负载均衡,一个容器启动之后,需要其他服务能够访问到它,一个容器终止运行之后,需要保证流量不会再导向它。

    状态检查:

    需要持续监控系统是否符合配置中声明的状态,比如一台宿主机挂了,需要把上面运行的容器在其他健康的节点上启动起来,如果一个容器挂了,需要把它重新启动。

    从设计思路,社区活跃度等因素来看,Kubernetes无疑是编排工具最好的选择,但由于组件较多,学习成本并不低,还有墙的因素,在国内甚至安装都不是件容易的事。

    这个时候我们发现Rancher正式发布了,虽然没有kubernetes那么热门,但它提供了所有我们需要的功能,还有一个简单容易上手的Web UI。在早期我们的机器和服务数量都比较少,又急需一个编排工具好把有限精力都投入到开发上,所以迅速的把服务都迁移到Rancher上了。

    准确的说,Rancher是一套容器管理打包方案,支持三种编排引擎:Kubernetes,Swarm,还有Rancher自己开发的Cattle(最近好像换成了Mesos)。从功能的完整性和易用性来看,Rancher甚至可以算得上一个商业软件了,部署极其简单,这也是我们选择它作为入门级容器管理平台的原因。

    Rancher组件图,中小企业常用的软件功能都能找到:

    图片 12

    后来围绕Rancher,也使用了一些Catalog里提供的服务栈,我们逐步搭建起来了一套完整的容器运维系统,包含了日志收集,性能监控,配合AWS的Auto Scaling Group,应用扩展也是很方便的事情。

    推荐一个Java学习交流群:855355016,群内有分布式架构、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Netty、Jvm等视频资料提供学习参考

    第三阶段:Kubernetes

    虽然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架构图:

    图片 13

    在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这种大型开源项目,但即刻算是一个对技术持开放态度,愿意让工程师去踩坑、尝试的公司。相对于采用传统架构,因为容器编排系统都是以提高本身的复杂性来覆盖繁琐的配置和脚本编写工作,本身复杂了就会导致出现问题的时候会比较难排查。但做过运维工作的应该了解,一般自己编写的脚本很难具有通用性,很可能环境稍有改变就不能使用了,运维是比较枯燥的工作,有了编排系统帮助处理这些,我们可以把更多的精力放到更有意义的工作上。

    欢迎Java工程师朋友们加入Java进阶高级架构群:855355016

    本群提供免费的学习指导 架构资料 以及解答

    不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

    本文由澳门新葡8455最新网站发布于编程教学,转载请注明出处:一个可供参考的公司应用容器化试行案例,创办

    关键词:

上一篇:第二十二期

下一篇:程序员必须掌握的