UDN-企业互联网技术人气社区

板块导航

浏览  : 3369
回复  : 1

[临时] 使用Jenkins、Docker和Ansible进行持续集成和交付

[复制链接]
Docker的头像 楼主
发表于 2015-10-23 15:44:20 | 显示全部楼层 |阅读模式
【编者的话】本文介绍了使用Docker、Jenkins等技术实现应用开发,测试到部署的自动化。它是一种探索。重点在于流程中的代码检测、测试、部署。部署后要做的事情没有涉及。会在后面文章中介绍。

本文试图为您介绍一个设置持续集成、交付、部署工作流的可行方式。我会使用Jenkins、Docker、Ansible和Vagrant来设置(配置)两个服务器。一个作为Jenkins的服务器,另一个用来模拟生产环境。前者用来检查代码、测试和构建应用程序,后者用来部署应用和后期测试。

你需要先安装好Vagrant和Git。剩下的一些工具我们会边介绍
CI/CD环境我们将会使用Vagrant和Ansible构建Jenkins环境。Vagrant会新建一个Ubuntu虚拟机然后运行bootstrap.sh脚本。脚本的唯一目的是安装Ansible。一旦安装好,Ansible就可以下载Docker,并运行Jenkins进程。

Jenkins会被打包在一个Docker容器中,并通过Ansible部署。可以查看Continuous Deployment: Implementation with Ansible and Docker获取更多信息。

如果你不想自己实践,也可以克隆这个GitHub仓库jenkins-Docker-ansible。一旦下载完成,我们就可以使用Vagrant启动cd虚拟机了。
  1. git clone https://github.com/vfarcic/jenkins-Docker-ansible.git
  2. cd jenkins-Docker-ansible
  3. vagrant up cd
复制代码

第一次在电脑上运行这条命令可能会花一些时间,所以我们可以利用Vagrant创建、配置虚拟机的时间来并行看下下面的步骤。

Vagrantfile中有两行设置非常关键:
  1. cd.vm.provision "shell", path: "bootstrap.sh"
  2. cd.vm.provision :shell, inline: 'ansible-playbook /vagrant/ansible/cd.yml -c local'
复制代码

首先运行bootstrap.sh脚本安装Ansible。我们可以使用ANSIBLE PROVISIONER,但是这需要我们在自己的主机上也安装Ansible。我觉得并没有必要,尤其是Windows的用户,在Windows上安装配置Ansible一点儿也不简单。此外,我们我们需要在虚拟机中安装Ansible以完成从cd部署应用到prod


译者注:cd和prod是本文中启动的两个虚拟机的名字。顾名思义,cd指持续部署VM,prod指生产环境VM。
bootstrap.sh执行结束后,Ansible的配置文件cd.yml开始运行:
  1. - hosts: localhost
  2. remote_user: vagrant
  3. sudo: yes
  4. roles:
  5. - java
  6. - Docker
  7. - registry
  8. - jenkins
复制代码

Ansible会运行Java、Docker、Jenkins、Registry role。Jenkins需要Java来运行slaves。Docker用来构建和运行容器。剩下的应用会以Docker进程的方式来运行。这时就不需要直接下载依赖、包或者其它应用。Registry role会运行Docker Registry。

这里是Jenkins role的任务列表:
  1. - name: Directories are present
  2. file: path="{{ item }}" state=directory
  3. with_items: directories
  4. - name: Config files are present
  5. copy: src='{{ item }}' dest='{{ jenkins_directory }}/{{ item }}'
  6. with_items: configs
  7. - name: Plugins are present
  8. get_url: url='https://updates.jenkins-ci.org/{{ item }}' dest='{{ jenkins_directory }}/plugins'
  9. with_items: plugins
  10. - name: Build job directories are present
  11. file: path='{{ jenkins_directory }}/jobs/{{ item }}' state=directory
  12. with_items: jobs
  13. - name: Build jobs are present
  14. template: src=build.xml.j2 dest='{{ jenkins_directory }}/jobs/{{ item }}/config.xml' backup=yes
  15. with_items: jobs
  16. - name: Deployment job directories are present
  17. file: path='{{ jenkins_directory }}/jobs/{{ item }}-deployment' state=directory
  18. with_items: jobs
  19. - name: Deployment jobs are present
  20. template: src=deployment.xml.j2 dest='{{ jenkins_directory }}/jobs/{{ item }}-deployment/config.xml' backup=yes
  21. with_items: jobs
  22. - name: Container is running
  23. Docker: name=jenkins image=vfarcic/jenkins ports=8080:8080 volumes=/data/jenkins:/jenkins
  24. - name: Reload
  25. uri: url=http://localhost:8080/reload method=POST status_code=302
  26. ignore_errors: yes
复制代码

首先我们创建存放Jenkins插件和roles的目录。为了加快构建需要的容器,我们还在主机上创建了存放ivy文件(SBT可能需要用)的目录。这样每次构建Docker容器时不需要重复下载依赖了。

创建好目录后我们会复制Jenkins的文件和插件。

下一步是Jenkins的jobs。因为所有的jobs都会做相同的工作,所以我们根据需要使用两个模板(build.xml.j2和deployment.xml.j2)来创建job。

最后,一旦job的配置文件传到Jenkins服务器里,我们就能确认Jenkins容器启动并且正确运行了。

所有的Ansible和Jenkins源代码都可以在jenkins-Docker-ansible找到。

下面是build.xml.j2模板中的关键部分:
  1. sudo Docker build -t 192.168.50.91:5000/{{ item }}-tests Docker/tests/
  2. sudo Docker push 192.168.50.91:5000/{{ item }}-tests
  3. sudo Docker run -t --rm
  4. -v $PWD:/source
  5. -v /data/.ivy2:/root/.ivy2/cache
  6. 192.168.50.91:5000/{{ item }}-tests
  7. sudo Docker build -t 192.168.50.91:5000/{{ item }} .
  8. sudo Docker push 192.168.50.91:5000/{{ item }}  
复制代码

上面所有的 {{ item }} 都会被Ansible中的变量值代替。因为所有的构建job都执行相同的流程,对于所有的job我们可以使用相同的模板以及提供简单的变量值就够了。在这篇文章中,main.yml中的变量值如下:
  1. jobs:
  2. - books-service
复制代码

Ansible运行时,每个** {{ item }} 会被替换为books-service jobs**中的变量对应我们需要的item值。jobs中的变量不需要一次性匹配添加完但要根据需要逐步添加。

接着我们会看到下面这样:
  1. jobs:
  2. - books-service
  3. - authentication-service
  4. - shopping-cart-service
  5. - books-ui
复制代码

开始用Ansible部署时,来自模板的执行命令如下:
  1. sudo Docker build -t 192.168.50.91:5000/books-service-tests Docker/tests/
  2. sudo Docker push 192.168.50.91:5000/books-service-tests
  3. sudo Docker run -t --rm
  4. -v $PWD:/source
  5. -v /data/.ivy2:/root/.ivy2/cache
  6. localhost:5000/books-service-tests
  7. sudo Docker build -t 192.168.50.91:5000/books-service .
  8. sudo Docker push 192.168.50.91:5000/books-service
复制代码

首先我们构建测试容器并push到私有registry中,然后运行测试。如果没有错误,我们会构建books-service容器,push到私有registry中。从这里开始, books-service已经完成了测试和构建,准备部署。

Docker出现之前,我所有的Jenkins服务构建到最后留下一堆job。因为使用大量不同的框架、语言和库,所以大部分job都不一样。管理大量的job很累人而且容易出错,这就不仅仅是复杂的问题了。管理slaves和依赖同样需要大量的时间。

Docker的出现简化了问题的复杂度。如果我们能保证每个项目有它自己的测试和应用容器,那所有的job就能做同样的事情了:构建测试容器并运行,若没有错误就构建应用容器并push到私有仓库。最后,我们只需要部署它。如果每个项目有它们自己的Dockerfile,那所有项目的构建流程都类似。另一个优点是因为有了Docker我们不需要在服务器上安装任何东西,我们唯一需要的就是能运行容器的Docker。

并不像构建job那样每次基本上都差不多,应用的部署会稍微复杂一些。虽然应用不可变并且被封装在容器里,但是仍然有一些环境变量、链接(link)和数据卷需要设置。这里就是Ansible施展拳脚的地方。我们可以使Jenkins的部署job相同但是它们的Ansible playbook名称不同。(译者注:这句个人理解不是太准确,贴出原句:We can have every Jenkins deployment job the same with only name of the Ansible playbook differing)。这样执行部署的job很容易运行部署应用的Ansible role了。这在大多数情况下都很简单。如果不使用Docker部署的话,两者的差异是巨大的。在使用Docker时我们只需要考虑数据(应用和依赖都被打包在容器里里了),没有Docker我们要考虑下载什么、更新什么以及这些变化会对服务器或虚拟机里的其它应用带来哪些影响。这也是企业不愿意更新技术栈的原因之一,例如,仍然使用java 5(或者更低)。

下面是Ansible中books-service中列出的例子:
  1. - name: Directory is present
  2. file:
  3. path=/data/books-service/db
  4. state=directory
  5. - name: Latest container is pulled
  6. shell: sudo Docker pull 192.168.50.91:5000/books-service
  7. - name: Container is absent
  8. Docker:
  9. image=192.168.50.91:5000/books-service
  10. name=books-service
  11. state=absent
  12. - name: Container is running
  13. Docker:
  14. name=books-service
  15. image=192.168.50.91:5000/books-service
  16. ports=9001:8080
  17. volumes=/data/books-service/db:/data/db
  18. state=running
复制代码

我们要确保存储数据的目录存在,拉取最新的容器,移除运行中的进程启动新的。

让我们回来看文章开始时创建的cd虚拟机。如果vagrant up cd命令执行结束,那整个VM中的Jenkins、Docker和Registry都启动并运行起来了。

现在我们可以打开http://localhost:8080使用Jenkins了。Ansible的task没有创建凭证,我们需要手工创建。
  • 点击Manage Jenkins > Manage Nodes > CD > Configure
  • 点击Credentials部分的Add按钮
  • 输入vagrant作为用户名和密码,点击Add按钮
  • 选中Credentials部分新创建的key
  • 点击SaveLaunch slave agent

    这些步骤本可以自动完成,但是安全起见我更喜欢手动配置。

    现在启动了CD slave,它指向我们用Vagrant创建的cd虚拟机,并提供给所有的jobs使用(即使部署的job在另一台机器里执行)。

    现在准备运行books-servicejob。在Jenkins的主页,点击books-service job,就启动了第一次构建(也可以点击Build now手动构建),可以在Build History模块查看构建过程,Console Output可以查看日志。第一次构建Docker容器可能会花一些时间。一旦job完成就会运行books-service-deployment job,但是我们仍然没有生产环境的VM而且Jenkins job运行的Ansible playbook也可能连不上生产环境的VM。一会儿我们再来考虑这个。现在我们将要做的是检测代码、运行测试、构建容器并push到私有registry中。

    这种设置的主要优点是除了cd上的Docker外不需要再额外下载任何东西,因为所有一切都在容器里搞定了。我们就没必要为了下载提供编写和测试的各种框架、库而头疼了。也不会有不同版本应用间的依赖冲突了。最后,Jenkins的jobs也变得很简单,因为用于应用测试、构建、部署的逻辑全部放在了Docker文件里。换句话说,不管Jenkins要管理多少项目或应用,整个流程维护起来都很简单、一点儿不痛苦。

    如果我们约定命名规范(比如本文中的例子),创建新的job就更简单了。要做的就是在Ansible配置文件ansible/roles/jenkins/defaults/main.yml中添加新的变量,运行vagrant provision cd或者直接在CD VM中运行 ansible-playbook /vagrant/ansible/cd.yml -c local

    下面展示了如何将改变应用到CD服务器中(包括添加新的Jenkins Job):
    [在主机的克隆仓库目录下执行]
    1. vagrant provision cd
    复制代码

    或者
    1. vagrant ssh cd
    2. ansible-playbook /vagrant/ansible/cd.yml -c local
    3. exit
    复制代码

    books-service被安排每隔5分钟从仓库更新代码。这很耗资源且运行很慢。更好的设置是使用GitHub hook。有了DitHub hook只有每次push代码到仓库时才会触发构建。更多信息参见GitHub Plugin 。类似的设置可以应用到任何其它类型的代码仓库。
    生产环境为了更贴近与真实情形,生产环境会另起一个虚拟机。目前还不需要在上面安装任何软件。随后Jenkins会运行Ansible,Ansible要确保服务器正确启动来部署每个应用。prod VM的创建方式和cd VM的相同。

    [在克隆仓库目录执行命令]
    1. vagrant up prod
    复制代码

    cd不同,prod需要一个Ubuntu系统就够了,不需要包和额外的依赖。

    现在我们启动并运行了prod环境,唯一剩下的是生成SSH Key并把它加到cd里。

    [在克隆仓库目录执行命令]
    1. vagrant ssh prod
    2. ssh-keygen # Simply press enter to all questions
    3. exit
    4. vagrant ssh cd
    5. ssh-keygen # Simply press enter to all questions
    6. ssh-copy-id 192.168.50.92 # Password is "vagrant"
    7. exit
    复制代码

    所有要做的都在这了。我们也启动了部署应用的生产环境了。现在回到Jenkins (http://localhost:8080)运行books-service-deployment job,如果您到这一步时books-service还没执行结束,请耐心等待直到它结束,books-service-deployment会自动开始。一切job都结束时,服务会被启动运行在9001端口。

    现在我们添加一些信息到books-service

    [在克隆仓库目录执行命令]
    1. vagrant ssh prod
    2. curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 1, "title": "My First Book", "author": "John Doe", "description": "Not a very good book"}' http://localhost:9001/API/v1/books
    3. curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 2, "title": "My Second Book", "author": "John Doe", "description": "Not a bad as the first book"}' http://localhost:9001/api/v1/books
    4. curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 3, "title": "My Third Book", "author": "John Doe", "description": "Failed writers club"}' http://localhost:9001/api/v1/books
    5. exit
    复制代码

    然后看看服务是否返回了正确的数据。在浏览器中打开http://localhost:9001/api/v1/books网址。你会看到之前用crul命令添加的3本书的信息。

    我们的服务已经部署并且启动运行了。每次修改代码时,都会重复相同的流程:Jenkins会克隆代码、运行测试、构建容器、推送到registry,最后在目标服务器运行容器。

    虚拟机创建,配置,构建和部署花了很多时间。但是,从现在开始大部分事情(Docker镜像IVY依赖等)下载后,再次运行的时候会非常快(不需要再重复下载)。只要把新建的Docker镜像push到registry。从这一刻起,快速就是整个流程最重要的优势了。
    总结有了Docker我们可以探索构建、测试、部署应用新的途径。容器技术的优点之一是它很简单,因为它具有不变性和自举的特点。这就没有什么理由让服务器下载运行大量依赖的包了。也不再需要做那些该死的维护不同版本应用或者是新建一个虚拟机来测试部署应用了。

    Dokcer不仅让服务器配置变得简单。为每个配置提供Docker文件也意味着Jenkins job更易于维护。不再需要成百上千个jobs了并且每个job对应应用测试部署的文件都不同,有了Docker我们很简单就能让所有jobs都一样。用Dockerfile构建、测试,最后用Ansible部署Docker容器。(或者区其它工具比如Fig)

    我们没有涉及到项目部署后的测试(功能测试、集成测试、压测等),这一步对成功的持续交付或部署是必须的。我们也漏掉了部署零宕机应用的方式。我们会在下一篇文章中的项目给出方法。(另一篇文章中)我们会在这次结束的地方开始,并且更深入地探索应用部署后要做的事情。

    文章中涉及的源代码在这里jenkins-Docker-ansible 。

    原文链接:Continuous Integration, Delivery or Deployment with Jenkins, Docker and Ansible(翻译:adolphlwq)

    ==========================================

    译者介绍
    adolphlwq,南京信息工程大学本科大四学生,对Docker充满兴趣,喜欢运动。现在正努力充电希望快速进步。
  • 相关帖子

    发表于 2015-10-23 16:57:58 | 显示全部楼层
    编辑器对md语法不是特别兼容,好几处代码显示效果不好啊
    使用道具 举报

    回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关于我们
    联系我们
    • 电话:010-86393388
    • 邮件:udn@yonyou.com
    • 地址:北京市海淀区北清路68号
    移动客户端下载
    关注我们
    • 微信公众号:yonyouudn
    • 扫描右侧二维码关注我们
    • 专注企业互联网的技术社区
    版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
    快速回复 返回列表 返回顶部