Docker 入门 & CI/CD实践

导语

现在很火的一个技术就是云计算服务,除了很早就开始做云计算的Amazon,阿里、腾讯、京东、百度等巨头也分分加入云计算的领域中。谈到云计算,就离不开Docker这个方便的工具啦。正如Docker的slogan所说”Build, Ship, and Run Any App, Anywhere”,它通过对应用组件的封装(Packaging)、分发(Distribution)、部署(Deployment)、运行(Runtime)等生命周期的管理,达到应用组件级别的”一次封装,到处运行”。

今天我就来学习一下什么是Docker,以及如何在阿里云服务器上用Docker上进行持续集成/持续部署(CI/CD)的,并把学习和实践的过程整理记录下来,其中某些概念性的知识可能会直接摘录官方文档或者相关教程,希望在自己之后的项目之中,可以派的上用场。

如果你已经熟悉了Docker,并会使用它了,那么请直接跳到本文的后半部分 CI/CD on Docker

Docker 简介

什么是Docker?

Docker它的英文本意是码头工人,也就是搬运工,这种搬运工搬运的是集装箱(Container),集装箱里面装的可不是商品货物,而是任意类型的App,Docker把App(叫Payload)装在Container内,通过Linux Container技术的包装将App变成一种标准化的、可移植的、自管理的组件,这种组件可以在你的latop上开发、调试、运行,最终非常方便和一致地运行在production环境下。

Docker为App提供了一种自动化构建机制(Dockerfile),包括打包,基础设施依赖管理和安装等等;相比于传统的虚拟机技术,它不需要完整的虚拟出一个操作系统,可以降低资源的消耗,提高服务器使用效率,通过其构建工具,又可以帮助我们快速的部署运行应用。

为什么是Docker?

为什么要用Docker一文介绍了Docker的优势,以及我们为什么要使用它。大概有以下几点:

  1. 更高效地利用系统资源
  2. 更快速的启动时间
  3. 一致的运行环境
  4. 持续交付和部署
  5. 更轻松的迁移
  6. 更轻松的维护和拓展

其中”持续交付和部署”是这篇博客所要重点关注的内容。

对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合持续部署(Continuous Delivery/Deployment) 系统进行自动部署。

而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

Docker 基本概念

在使用Docker之前,有几个最基本的概念我们需要了解。

Docker 镜像

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

Docker 容器

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。

Docker 仓库

镜像构建完成后,可以很容易的在当前宿主上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

PS: 由于这篇博客暂时不会涉及到在不同服务器运行同一个Docker,所以关于Docker仓库的内容就会先简略跳过。

Docker 安装

我将在阿里云服务器上完成Docker的安装和后续的实践。服务器为Ubuntu 16.04 LTS

Docker 官方为了简化安装流程,提供了安装一套安装脚本:

1
curl -sSL https://get.docker.com/ | sh

由于墙内原因,下载可能会出现错误,这里就直接使用了阿里云提供的安装脚本进行安装

PS: 该脚本仅支持阿里云服务器内网访问,非内网用户使用不了。但是可以使用中国区优化的脚本:github

1
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/intranet | sh -

运行完脚本之后,发现Docker引擎已经完成安装,并且启动了。

可以通过下面的命令,查看docker引擎运行的状态:

1
systemctl status docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
docker version # 查看docker 版本
Client:
Version: 17.05.0-ce
API version: 1.29
Go version: go1.7.5
Git commit: 89658be
Built: Thu May 4 22:10:54 2017
OS/Arch: linux/amd64
Server:
Version: 17.05.0-ce
API version: 1.29 (minimum version 1.12)
Go version: go1.7.5
Git commit: 89658be
Built: Thu May 4 22:10:54 2017
OS/Arch: linux/amd64
Experimental: false
docker run --rm hello-world # 运行hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pulling fs layer
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://cloud.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/

使用镜像

获取镜像

Docker Hub上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像并运行。

从 Docker Registry 获取镜像的命令是 docker pull。其命令格式为:

1
docker pull [选项] [Docker Registry地址]<仓库名>:<标签>

运行镜像

docker run 就是运行容器的命令

  • -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
  • --rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
  • ubuntu:14.04:这是指用 ubuntu:14.04 镜像为基础来启动容器。
  • bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash

进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 14.04.5 LTS 系统。

最后我们通过 exit 退出了这个容器。

列出镜像

要想列出已经下载下来的镜像,可以使用 docker images 命令。

1
2
3
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 48b5124b2768 4 months ago 1.84kB

CI/CD on Docker

什么是CI/CD?

持续集成(Continuous Integration)简称CI,持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。

持续部署(Continuous Delivery/Deployment)简称CD,持续部署则是在持续交付的基础上,把部署到生产环境的过程自动化。

The Product Managers’ Guide to Continuous Delivery and DevOps一文中对持续集成、持续交付、持续部署三个概念的解释,我们大概可以了解到三者是递进关系的,后者再前者的基础之上进一步自动化。本文只做一个简单的demo,以后根据业务场景的不同再进行更改。

Travis CI

在之前我所写的博文GitHub+Travis CI 持续集成 一文中,介绍了如何使用GitHub和Travis CI 进行持续集成。使用Travis CI也可以在CI过程中使用Docker来进行部署、运行、测试。只需要在.travis.yml中添加简单的配置即可

1
2
3
4
sudo: required
services:
- docker

然后在before_install标签下,添加相关的docker语句即可。由于Travis CI 使用的是在线的服务,所以不需要自己再另外安装Docker,如果是自己在服务器上私有的Travis CI才需要。给人感觉整体的配置流程是比较简单,方便的。具体可以参考官方文档Using Docker in Builds。 与配置完Jenkins之后的持续部署类似,本文不再详细叙述,有需要的话再进行学。

Jenkins

在玩过了Travis CI之后,这次再尝试学习一下另一款持续集成工具Jenkins,并学习、记录下如何使用Jenkins和Docker做持续集成。这里与Travis CI不同,是直接使用自己的服务器来进行持续集成的,可以实现更多的定制,以及闭源代码的持续集成。(Travis CI在线服务只免费提供给GitHub开源代码)。

什么是Jenkins

jenkins是一个广泛用于持续构建的可视化web工具,持续构建说得更直白点,就是各种项目的”自动化”编译、打包、分发部署。jenkins可以很好的支持各种语言(比如:java, c#, php等)的项目构建,也完全兼容ant、maven、gradle等多种第三方构建工具,同时跟svn、git能无缝集成,也支持直接与知名源代码托管网站,比如github、bitbucket直接集成。

通过Docker安装和启动Jenkins

安装

Jenkins需要Java环境,有了Docker这个利器,我们就省去了安装Java环境的麻烦,只需执行如下命令即可。通过执行docker pull 指令,我们可以很方便地获取到可以用来执行jenkins的docker镜像。

1
$ docker pull jenkins:latest

通常的做法是把jenkins文件存储地址挂载在宿主机上,这样子如果jenkins服务器重装或者迁移就可以方便地迁移项目配置了。

1
$ docker run -d --name myjenkins -p 8080:8080 -v ${pwd}/data:/var/jenkins_home jenkins

通过这种方法来启动有时候会不成功,可能是因为路径权限存在着问题。这里使用https://github.com/AliyunContainerService/docker-jenkins版本的修改版jenkins来进行构建自己的Jenkins镜像

1
2
3
$ git clone https://github.com/AliyunContainerService/docker-jenkins
$ cd docker-jenkins/jenkins
$ docker build -t myjenkins .

启动

然后基于新镜像启动Jenkins容器

1
$ docker run -d -p 0.0.0.0:8080:8080 -p 50000:50000 -v $(pwd)/data:/var/jenkins_home --name jenkins myjenkins

查看正在运行的docker 容器

1
2
3
4
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
10ea38c68ec5 myjenkins "/entrypoint.sh" 2 minutes ago Up 2 minutes 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins

PS: $(pwd)是当前路径

基于myjenkins镜像构建了jenkins容器,将docker 8080端口(冒号后)与宿主机的8080端口(冒号前)绑定在一起,可以通过宿主机ip+8080端口号来访问jenkins页面,访问50000端口可以获取到端口信息。

配置环境变量

jenkins->系统管理->管理节点->宿主机

配置所需要的环境变量,则jenkins就可以使用到宿主机上的一些环境了。注意JAVA_HOME的配置要求的是JDK的位置而非JRE的位置。

安装插件

在data/serects下查看initialAdminPassword进入Jenkins控制页面

安装建议的插件

开始界面

对大作业进行自动构建

上篇博客中,完成了对大作业一个java项目的maven自动构建,这次就通过jenkins和docker来实现。

配置maven

在系统管理->管理插件->可选插件 中安装Maven Integration plugin这个插件

在系统管理->Global Tool Configuration 选择maven路径,或者自动安装

GitHub 插件

此外,在jenkins我们要需要安装一些关于GitHub的插件,来支持Webhook

新建 Jenkins 项目

这里选用自由风格的软件项目来构建

选择源码地址,需要添加GitHub账号权限,可以使用ssh key也可以使用账号密码

可以通过脚本触发,这里使用了GitHub Plugin所提供的GitHub hook trigger for GITScm polling,当GitHub上的项目仓库发生改变之后会发出相应的请求。

其中maven版本选择刚刚所设置的,执行clean和install目标。

构建

点击立即构建,就可以完成构建了。红色表示失败,蓝色表示成功,点击进去可以查看详细。

GitHub 自动触发

在仓库的settings->services中添加jenkins配置,根据提示添加触发url即可。url地址为http://your_jenkins_url/github-webhook/ 。这样子当GitHub仓库发生更新时就会触发自动构建功能了。

自动部署

构建中,可以执行shell指令,如果在后期大作业完成前端与后端之后,就可以直接在构建中,创建一个docker镜像、容器并且运行这个容器,使得我们可以直接访问到部署完成的网页了。这个会在后续继续改进。

PS:这次是直接在宿主机上进行配置,此外Jenkins还可以配置另外的主机节点来运行maven等构建,同时也可以指定一些环境,因为每一次都重新下载maven的话其实是比较花时间的。大致的流程就可以像下图所示一样。

总结

在了解了Docker之后,发现这是一个比虚拟机更加强大的工具,速度也很快,方便我们快速部署应用。在Docker上实现CI/CD可以让我们只需要提交GitHub上的代码更改之后,便可以在网页上看到产品的新版本了。大量地减轻了这些重复性的工作。

这次也了解了Jenkins,相比于Travis CI的话,配置会更加麻烦一些,整体的流程都是相近的,但是可以定制更多自己想要的功能,在后续过程中如果与Docker结合,可以看到一个产品的完整生命周期,自动化完成的过程。因为还没有完成大作业的前后端全部功能,所以看具体的效果可以参考Gitlab+Jenkins+docker完成Maven项目的自动部署一文。

参考

Docker入门与实践

Docker —— 从入门到实践

构建微服务实验环境(一):容器与应用

The Product Managers’ Guide to Continuous Delivery and DevOps

jenkins 入门教程

利用Jenkins和Docker做持续集成

希云cSphere-最佳实战Docker持续集成图文详解

Gitlab+Jenkins+docker完成Maven项目的自动部署

您的支持将鼓励我继续创作!