将Docker融入进CI/CD中的实践过程
2017-07-06 18:36:51
Angela
  • 访问次数: 250
  • 注册日期: 2017-03-15
  • 最后登录: 2017-10-12
  • 当前积分: 1287

2017-07-06 运维彪哥 Docker

随着DevOps理念不断的传播,大部分IT从业者对于DevOps本身也有了一定的了解和认识,然而企业内部想根据DevOps思想实践,这并不是一件很简单的事情。一方面由于企业内部的历史环境以及组织结构问题,另外一方面因为业界并没有一套标准的开源工具集可以借鉴(关于几家基于Docker创业的服务提供商暂时除外)。


那么该篇内容主要讲解如何借助开源工具结合CI/CD的场景,将Docker融入到部署单元中去,进行持续集成、测试到最终的持续部署,开发人员最终只需要去关注业务的访问入口就可以知道业务是否正常,并可以通过一系列的监控工具去及时发现业务异常。


在整个DevOps部署流水线中需要以下几个部分:CI部分、CD部分、服务调度(治理)部分、监控部分、日志部分。本篇文章将通过一个简单的go-web应用去进行基于Docker的CI/CD流水线的测试。


基于Docker的CI/CD的优势

一个完整的流程入上图所示,用户(也就是开发人员)将包含Dockerfile的源码从本地push到Git服务器上,然后触发Jenkins进行构建源码,源码构建完成后紧接着进行Docker image的构建,一切构建完成之后,顺带将构建成功的image上传到企业内部的镜像仓库,到此刻为止,其实一个基本的CI(持续集成)已经算是结束,剩下的部分就是持续部署或者进行持续的交付开发产物了。


在以前传统的软件发布模式中,持续集成的产物是编译打包好的代码,如果想要发布程序,发布系统需要在持续集成的制品库中去获得对应的代码,然后根据一系列的环境检查来准备应用的运行时环境,而在此过程中往往会涉及到比较多的基本组件依赖,所以在整体的发布周期内来看,还是有一些问题的。


在Docker或者容器时代,我们将容器的镜像构建部分融入到持续集成(CI)环节,最终持续集成的产出物是一些已经处理好依赖关系,基本不需要人工进行二次干预的Docker image,而在CD环节,发布系统只需要设置和管理很少的信息就能够很快将image运行起来,快速地将业务发布出去。


在上面整个环节中,其实无非就是增加了Docker的那一层处理,但其实在整个软件开发的生命周期中,它是产生了极大的影响的。


首先,部署系统不需要为统一的部署框架去做更多逻辑抽象,业务研发在开发代码的过程中选择自己依赖的base image即可,最终运行起来的业务也就是你当时提供的base image的模样;其次,由于base image已经处理好了相关的依赖,所以当发布系统拿到业务的image的时候,发布操作将会变得异常迅速,这对于互联网时代可谓是非常重要的;最后一点,也是我感受最深的,就是研发构建好的image可以在任何的Docker环境中run起来,研发人员不需要再关系环境一致性的问题,他们在自己本地的测试环境能够运行起来的应用,那么到生成环境也一定可以。


为什么第三点我感触比较深呢?因为以前经常有研发兄弟跑过来跟我们讲,我们代码在本地运行一切顺利,代码给你们上到生产就各种问题。所以如果在整个流程中使用Docker image来讲所有的环境固化,从此mm就再也不用担心和研发兄弟扯皮环境不一致的问题啦。


基于Docker的CI/CD的开源方案实现

一、自助式Git管理工具Gogs的部署安装


Gogs部署


Gogs部署在10.0.0.1主机上,映射到宿主机端口为32770


$ docker run -itd -p 32770:3000 -v /export/CI-CD/mygit:/data --name jdjr-gogs gogs:17-04-25 

$ docker  ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS              PORTS                                            NAMES
e4dd6c6719c7        10.0.0.3:5001/gogs:17-04-25   "/app/gogs/docker/sta"   2 days ago          Up 2 days           0.0.0.0:10022->22/tcp, 0.0.0.0:32770->3000/tcp   jdjr-gogs


MySQL建库授权


MySQL部署在10.0.0.2上,映射到宿主机端口为32771


$ docker run -itd -p 32771:3306 --name jdjr-mysql pandora-mysql
由于该mysql只是一个中间件,并不带有业务属性,因此需要进去进行配置数据库。
$ docker exec -it jdjr-mysql bash
bash-4.1# mysqladmin -u root -p password 123456
Enter password:
bash-4.1# mysql -uroot -p123456

mysql> create database gogs;
mysql> create user 'root'@'10.0.0.1' IDENTIFIED BY '123456';
mysql> grant all on gogs.* to 'root'@'%'; 


配置Gogs


上面两步没有问题之后就可以直接访问:ip:32770 (也就是Gogs暴露的端口)进行相关的配置。


配置数据库相关:

           配置Git地址:

配置完成后进行初始化,并创建管理员用户后就可正常使用。


如图,现在正在使用的本地Git。

现在就可以将源码托管在本地的Gogs仓库上了。


二、Jenkins持续集成工具部署安装


Jenkins部署


Jenkins在官方的image基础上增加了go 1.7的编译环境,部署在10.0.0.2上,映射到宿主机端口32791。


$ docker run -itd -p 32791:8080 -p 32790:50000 -v /export/jenkins/:/var/jenkine_home/ --name jdjr-jenkins  jdjr-jenkins


注意:需要将Jenkins相关数据以及编译环境映射到Docker宿主机上,因为后期编译完成后Jenkins容器需要docker build构建业务image。


Jenkins容器运行起来之后,就可以直接访问10.0.0.2:32791进行初始化安装配置了。


在Web上面访问Jenkins地址进行初始化配置,需要写入ID进行解锁Jenkins(Web上会提示在哪个路径下存放,直接使用docker logs也可查看);解锁后就是正常的安装相关的Plugins了,只要网络没有问题,一般都正常通过。


Jenkine安装成功后界面如下:


创建Jenkins项目,并配置构建脚本(也可通过相应的Plugins进行配置)。


创建一个新的名为test的项目,配置相关的源码管理以及构建条件以及相关的后续操作。

配置Jenkins环境


注意:由上图可以看出来,Jenkins进行构建image和持续部署测试的过程都是通过SSH到远端去执行的,因此需要再Jenkins容器中生成SSH公私钥对,并和Jenkins的宿主机以及持续部署测试的宿主机进行免密认证。虽然Jenkins本身其实支持了很多种Plugin来支持管理Docker的,比如说Docker build step plugin、Docker Build Publish Plugin,但是由于过多的Plugin会造成实际环境中的维护成本大大增加,因此我们选择简单粗暴的脚本方式,上图中的Execute shell只是简单的示例。


$ docker exec -it myjenkins bash
jenkins@3add772e26a8:/$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/var/jenkins_home/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/jenkins_home/.ssh/id_rsa.
Your public key has been saved in /var/jenkins_home/.ssh/id_rsa.pub.
The key fingerprint is:
cd:f5:e7:67:ba:79:f2:d2:83:49:9d:bb:4c:74:fa:dc jenkins@3add772e26a8
The key's randomart image is:
+---[RSA 2048]----+
|                 |
|                 |
|            .    |
|         o . .   |
|        S o   o.+|
|             ..*.|
|            . +o=|
|             o=O=|
|              +OE|
+-----------------+ 


生成公私钥对之后,将公钥传给要远程部署的机器就OK了,目的是要让Jenkins容器能够免密登录远程服务器,并能执行sudo命令。


三、通过配置Nginx反向代理来访问Git,Jenkins以及测试实例


反向代理Nginx部署在10.0.0.4:80上。


配置Nginx


注意:centos6.8-jdjr-test-app:v2镜像默认是包含Nginx以及配置管理工具的。


$ docker run -itd --name biaoge-nginx centos6.8-jdjr-test-app:v2 


详细的nginx配置大概如下:


$ cat app.confjenkins_conf        upstream tomcat_jenkins.jd {
           server 10.0.0.2:32791  weight=10 max_fails=2 fail_timeout=30s;
           }
    server {
            server_name              jenkins.biao.com;

           location / {
                proxy_pass              http://tomcat_jenkins.jd;
            }
            }mygit(gogs)_conf        upstream tomcat_mygit.jd {
            server 10.0.0.1:32770  weight=10 max_fails=2 fail_timeout=30s;
            }
    server {
            server_name              mygit.biao.com;

            location / {
                proxy_pass              http://tomcat_mygit.jd;
            }
            }web_conf        upstream tomcat_web.jd {
            server 10.0.0.3:32768  weight=10 max_fails=2 fail_timeout=30s;
            }
    server {
            server_name              web.biao.com;

            location / {
                proxy_pass              http://tomcat_web.jd;
                }
                }   


注意:此时Git上的源码还没有编译部署,我只是暂时定义了一个端10.0.0.3:32768,等完成整个CI/CD流程后直接访问web.biao.com就可以看到源码部署的效果。


测试访问


在本地绑定如下hosts


10.0.0.4 jenkins.biao.com

10.0.0.4 web.biao.com
10.0.0.4 mygit.biao.com


访问mygit.biao.com上面的源码:

访问jenkins.biao.com上的构建任务:

注意:test项目在之前我们已经配置好了,所以可以直接触发构建部署。


手动触发构建部署:

注意:在构建过程这里可以看到详细的构建过程,构建成功后便可以访问我们的goweb服务了。


访问web.biao.com服务:

持续集成持续部署的效果


更新源码中的部分内容,进行重新构建访问。

修改web的源码
sh-4.2# vim web.go
sh-4.2# cat web.go  | grep Hello
fmt.Fprintf(w, "Hello ,I'm 逼格运维说 \n\r"+hostname+"\n\r"+time)
sh-4.2# git add *
sh-4.2# git commit -m 'Hello,BiggerOpser'
[master 85ca6d7] Hello,BiggerOpser
1 file changed, 1 insertion(+), 1 deletion(-)

sh-4.2# git push -u origin master
Counting objects: 5, done.
Delta compression using up to 24 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 322 bytes | 0 bytes/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To http://172.25.28.12:32770/jdjr-pe/go-web.git
98464c7..85ca6d7  master -> master
Branch master set up to track remote branch master from origin.  


在Jenkins上进行再次构建:

再次访问web.biao.com服务:

对比前后两个Web,发现不仅欢迎语由“biaoge”变成了“逼格运维说”,而且第二行的字符串由4e7853008397变为0ce402beclle,也就是是之前的那个Container已经被销毁,我们现在访问的web.biao.com是重新编译后运行在新的container里面的实例。


总结

至此,整个基于Docker的CI/CD流水线的流程基本完成,其实可以看到对于研发兄弟来说,每次的提交代码都会触发一次编译构建,并且最终会run起来一个新的Container,并且直接对外服务。当然在整个过程中对于集成、发布部分演示的都比较粗浅,这里只做一个引子,对于企业内部一整套的流程体系来说,光有持续集成和持续部署也是远远不够的,服务上线后的基础监控以及业务的日志监控还有整个业务调度都是需要进行协同考虑的(在容器云平台的构建中CI/CD只是最基本的流程,想要做好整个平台,还需要监控、日志以及调度方面的能力才能做到麻雀虽小,而五脏俱全)。


在Docker理念大行其道的今天,我个人认为我们需要更深层次的去考虑实际场景中它带给我们的优势和劣势,并且要思考如何与当前整体系统架构以及运维研发架构进行衔接,如何去为配合研发去做敏捷开发以及持续的交付,为企业产生更大的效能。


本篇文章为前一个月实践所得,今天重新编辑整理,如有部分章节读者有疑问可以随时勾搭作者。希望有共同爱好者一起交流关于Docker方面的东西。


Angela 最后编辑, 2017-07-06 18:39:51