这一节主要介绍一款 CI 开源工具 Jenkins 在 Docker 中的应用部署。Jenkins 是一个可扩展的持续集成引擎,主要用于持续、自动构建/测试,以及监控一些定时任务等。这一节我们使用 Jenkins 工具构建一个测试流水线,使用 Docker-in-Docker 方式。先看我们的构建脚本:
$ cat Dockerfile FROM ubuntu:16.04 MAINTAINER leon@weippt.com ENV REFRESHED_AT 2017-07-15 RUN apt-get -yqq update && apt-get install -yqq curl apt-transport-https RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D RUN echo deb https://apt.dockerproject.org/repo ubuntu-xenial main > /etc/apt/sources.list.d/docker.list RUN apt-get update -yqq && apt-get install -yqq iptables ca-certificates openjdk-8-jdk git-core docker-engine ENV JENKINS_HOME /opt/jenkins/data ENV JENKINS_MIRROR http://mirrors.jenkins-ci.org RUN mkdir -p $JENKINS_HOME/plugins RUN curl -sf -o /opt/jenkins/jenkins.war -L $JENKINS_MIRROR/war-stable/latest/jenkins.war RUN for plugin in chucknorris greenballs scm-api git-client git wa-cleanp ; \ do curl -sf -o $JENKINS_HOME/plugins/${plugin}.hpi \ -L $JENKINS_MIRROR/plugins/${plugin}/latest/${plugin}.hpi; done ADD ./dockerjenkins.sh /usr/local/bin/dockerjenkins.sh RUN cmod +x /usr/local/bin/dockerjenkins.sh VOLUME /var/lib/docker EXPOSE 8080 ENTRYPOINT [ "/usr/local/bin/dockerjenkins.sh" ]
上面脚本用到的 dockerjenkins.sh 如下:
#!/bin/bash # wget https://dockerbook.com/code/5/jenkins/dockerjenkins.sh && chmod 0755 dockerjenkins.sh # First, make sure that cgroups are mounted correctly. CGROUP=/sys/fs/cgroup [ -d $CGROUP ] || mkdir $CGROUP mountpoint -q $CGROUP || mount -n -t tmpfs -o uid=0,gid=0,mode=0755 cgroup $CGROUP || { echo "Could not make a tmpfs mount. Did you use -privileged?" exit 1 } # Mount the cgroup hierarchies exactly as they are in the parent system. for SUBSYS in $(cut -d: -f2 /proc/1/cgroup) do [ -d $CGROUP/$SUBSYS ] || mkdir $CGROUP/$SUBSYS mountpoint -q $CGROUP/$SUBSYS || mount -n -t cgroup -o $SUBSYS cgroup $CGROUP/$SUBSYS done # Now, close extraneous file descriptors. pushd /proc/self/fd for FD in * do case "$FD" in # Keep stdin/stdout/stderr [012]) ;; # Nuke everything else *) eval exec "$FD>&-" ;; esac done popd docker daemon & exec java -jar /opt/jenkins/jenkins.war
构建这个镜像/容器:
$sudo docker build -t leon/dockerjenkins . $sudo docker run -p 8080:8080 --name jenkins --privileged -d leon/dockerjenkins $sudo docker logs -f jenkins #可以监控容器运行状态 # 注:--privileged 特权模式,可以以宿主(几乎)所有的能力运行容器。但同时对宿主也具有root权限风险!
现在已经可以在浏览器里访问Jenkins服务器了(Jenkins的默认语言同浏览器语言)。
点击页面左上角创建任务,我们为新创建的 Jenkins 作业命名为:Docker_test_job,并选择 Freestyle project;
接下来在高级选项里选择:使用自定义的工作空间,设置运行 Jenkins 的Directory为:/tmp/jenkins-buildenv/${JOB_NAME}/workspace;在源码管理里选 Git:https://github.com/oooline/docker-jenkins-sample.git (包含一些基于Ruby的RSpec测试);在构建里点击:增加构建步骤,然后选择执行shell脚本 Execute shell ,脚本如下:
# 构建用于此作业的镜像 IMAGE=$( docker build . | tail -1 | awk '{ print $NF }') # 构建挂载到 Docker 的目录 MNT="$WORKSPACE/.." # 在 Docker 里执行编译测试 CONTAINER=$(docker run -d -v "$MNT:/opt/project" $IMAGE /bin/bash -c 'cd /opt/project/workspace && rake spec') # 进入容器,这样可以看到输出的内容 docker attach $CONTAINER # 等待程序退出,得到返回码 RC=$(docker wait $CONTAINER) # 删除刚刚用到的容器 docker rm $CONTAINER # 使用刚才的返回码退出整个脚本 exit $RC
这个脚本首先使用Git仓库中的Dockerfile创建一个新的Docker镜像,用于测试基于Ruby且使用RSpec测试框架的应用程序,其中
ci_reporter_rspec gem会把 RSpec 的输出转换为JUnit格式的XML输出,并交给Jenkins做解析;接下来创建一个包含Jenkins工作空间(签出Git仓库的地方)的目录,会把这个目录挂载到Docker容器,并在这个目录里执行测试。然后我们从这个镜像创建了容器,并且运行了测试。在容器里,把工作空间挂载到 /opt/project 目录,执行命令切换到这个目录,并执行 rake spec 来运行 RSpec 测试。现在容器启动了并获取到容器ID,现在使用 docker attach,docker wait 会一直阻塞,直到容器里的命令执行完才会返回容器退出时的返回码($RC )。最后清理环境,删除刚刚创建的容器,退出脚本。
接下来依次点击 Add post-build action(增加构建后动作) 、Publish JUint test result report(公布JUint测试结果报告),在Test report XMLs中指定:spec/reports/*.xml (这个目录是 ci_reporter gem的XML输出的位置);
最后点击保存!
点击作业主面板的 Build Now,将会看到有个作业出现在 Build History 里。通过Console Output控制台可以查看构建过程,构建顺利结束之后可以在 Test Result里查看构建结果。
通过SCM轮询,它会在Git仓库有改动时,触发自动构建。也可以通过Git钩子,或GitHub钩子来实现。
现在我们创建一个新的项目:Docker_matrix_job,并选择:Muti-configuration project;
接下来,源码设置同样使用上一个案例中的 git 地址;然后点击 Add Axis(维度)并选择:User-defined- Axis(用户自定义维度),指定这个维度的名字为:OS,并设置3个值:centos debian ubuntu ( 当执行多配置作业时,Jenkins会查找这个维度并生成3个对应的作业 );接着在 Build Environment 中选择:Delete workspace before build starts(保证一些列作业初始化之前删除仓库清理构建环境);接着我们在增加构建步骤 Execute shell :
cd $OS && IMAGE=$( docker build . | tail -1 | awk '{ print $NF }')MNT="$WORKSPACE/.." CONTAINER=$(docker run -d -v "$MNT:/opt/project" $IMAGE /bin/bash -c "cd /opt/project/$OS && rake spec") docker attach $CONTAINER RC=$(docker wait $CONTAINER) docker rm $CONTAINER exit $RC
最后,再加入一个构建后动作 Publish JUnit test result report 并指定XML输出位置为:spec/reports/*.xml;
现在我们已经可以开始测试这个多配置作业了:Build Now!
我们还有一些其他的持续集成和持续部署工具可以选择,比如Drone、Shippable等......