前言

互联网时代,软件开发和发布的重要性不言而喻,目前已经形成一套标准的流程,最重要的组成部分就是持续集成(CI)及持续部署、交付(CD)。本文基于 Docker + Jenkins + Git + Registry 实现一套 CI 自动化发布流程。

  • 网路结构图 automated-deployment
    • 开发人员在 GitLab 上打了一个 Tag
    • GitLab 把 Tag 事件推送到 Jenkins
    • Jenkins 获取 Tag 源码,编译,打包,构建镜像
    • Jenkins Push 镜像到阿里云仓库(或本地镜像仓库)
    • Jenkins 执行远程脚本
      • 远程服务器 Pull 指定镜像
      • 停止老版本容器,启动新版本容器
    • 通知测试人员部署结果

阅读本文之前,需要安装好 docker、gitlab、registry 环境,本文的重点是如何配置 jenkins 实现自动化部署,其他的请自行 Google 安装配置好环境。

本文使用的版本为 CentOS 7、Jenkins 2.150、GitLab 11.4.5、Docker 17.3,Jenkins 挂载路径为 /home/jenkins

环境划分

角色 IP
Docker/Jenkins/Registry 47.101.61.194
Docker(部署项目) 115.47.124.247

Jenkins

Jenkins 获取 Tag 源码,编译、打包、构建镜像时,需要用到 Docker 命令,Jenkins 容器本身没有安装 Docker ,如何在 Docker 容器内使用 docker 命令? 为了让容器内可以构建镜像,应该使用 Docker Remote API 的客户端来直接调用宿主的 Docker Engine,也就是所谓的 Docker In Docker (DIND)

为 Jenkins 添加 Docker 命令行

以官方 jenkins/jenkins:lts-alpine 镜像为例,使用 Dockerfile 添加 docker 命令行可执行文件,并调整权限。

# 基础镜像
FROM jenkins/jenkins:lts-alpine
# 作者
MAINTAINER  ryan <me@ryana.cn>
# 安装 Docker CLI
USER root
RUN curl -O https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz \
    && tar zxvf docker-latest.tgz \
    && cp docker/docker /usr/local/bin/ \
    && rm -rf docker docker-latest.tgz
# 将 `jenkins` 用户的组 ID 改为宿主 `docker` 组的组ID,从而具有执行 `docker` 命令的权限。
ARG DOCKER_GID=994
USER jenkins:${DOCKER_GID}

在上面这个 Dockerfile 例子中,我们下载了静态编译的 docker 可执行文件,并提取命令行安装到系统目录下。然后调整了 jenkins 用户的组 ID,调整为宿主 docker 组 ID,从而使其具有执行 docker 命令的权限。

组 ID 使用了 DOCKER_GID 参数来定义,以方便进一步定制,ARG DOCKER_GID=994 只是个例子,您服务器上的 DOCKER_GID 可能不是 994,可通过下面两种方式改变(二选一):。

  • 命令 cat /etc/group|grep docker 查看 DOCKER_GID, 构建镜像时通过 --build-arg 来改变 DOCKER_GID 的默认值。
  • 运行时通过 --user jenkins:994 来改变运行用户的身份(墙裂推荐)。
# 如果需要构建时调整 docker 组 ID,可以使用 --build-arg 来覆盖参数默认值
docker build -t jenkins --build-arg DOCKER_GID=994 .
# 在启动容器的时候,将宿主的 `/var/run/docker.sock` 文件挂载到容器内的同样位置,从而让容器内可以通过 unix socket 调用宿主的 Docker 引擎
docker run --name jenkins \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
-d ryaning/jenkins

下载

该镜像按照上方的 Dockerfile 制作好的,直接下载就行了。

docker pull ryaning/jenkins

运行

# 查看 docker 组的 gid
cat /etc/group|grep docker

# 例
[root@test ~]# cat /etc/group|grep docker
docker:x:994:
[root@test ~]# 

# 替换 docker 组的 gid,并运行
docker run --name jenkins \
-p 8080:8080 \
-p 50000:50000 \
-e TZ="Asia/Shanghai" \
-v /etc/localtime:/etc/localtime:ro \
-v /home/jenkins:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
--user jenkins:994 \
-d ryaning/jenkins

查看日志

查看 jenkins 是否启动成功

docker logs jenkins

注意:在运行 jenkins 时,挂在文件夹的归属用户 id 必须是 1000 ,否则会抛出无操作权限异常。异常如下:

touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

为什么挂载文件夹的归属用户 Id 必须是 1000?在 Jenkins 的 Dockerfile 中可以看到说明要确保使用相同的 uid。

ARG user=jenkins
ARG group=jenkins
ARG uid=1000
ARG gid=1000
ARG http_port=8080
ARG agent_port=50000

ENV JENKINS_HOME /var/jenkins_home
ENV JENKINS_SLAVE_AGENT_PORT ${agent_port}

# Jenkins is run with user `jenkins`, uid = 1000
# If you bind mount a volume from the host or a data container, 
# ensure you use the same uid
RUN addgroup -g ${gid} ${group} \
    && adduser -h "$JENKINS_HOME" -u ${uid} -G ${group} -s /bin/bash -D ${user}
# 查看文件夹的归属者
ls -nd /home/jenkins/
# 修改文件夹的归属者和组
chown -R 1000:1000 /home/jenkins/

初始化

访问 http://ip:8080

# 首次进入后需要输入密码,在命令行使用如下命令获取初始密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

之后会进入 jenkins 安装插件页面,选择安装推荐插件就可以了。等待插件安装完成就可以了。

安装支持插件

为了实现自动化部署,需要额外安装一些插件支持,系统管理->管理插件 里进行安装即可。

  • Docker:提供 docker 构建和发布(验证)。
  • SSH:提供通过 ssh 在远程主机执行命令,用于部署服务。
  • Maven Integration:支持 maven。
  • Git Parameter:动态获取Git仓库Branch、Tag。

配置全局工具

配置 JDK、Maven

主页面 -> 系统管理 -> 全局工具配置,设置 JDK、Maven。

  • JDK

Jenkins容器本身已经安装了Jdk,拿来直接使用即可。

# 进入容器,命令查看 jdk 路径
docker exec -it jenkins bash
echo $JAVA_HOME

# 例:
/usr/lib/jvm/java-1.8-openjdk

jenkins-install-jdk

  • Maven

Maven 选择自动安装就可以了。

jenkins-install-maven

配置 SSH

第一步:主页面 -> 凭据 -> 系统 -> 右击全局凭据 -> 添加凭据,创建一个用于连接 Docker 主机的凭据。

jenkins-unrestricted-ssh

第二步:主页面 -> 系统管理 -> 系统设置 -> SSH remote hosts,添加 SSH 远程主机。

jenkins-ssh-remote

配置 Git SSH 凭证

一、在 Jenkins 容器内生成 ssh 的认证,执行 ssh-keygen ,然后将生成的 id_rsa.pub(cat ~/.ssh/id_rsa.pub) 添加到 GitHub 的 ssh 认证。然后将 id_rsa (cat ~/.ssh/id_rsa)添加到下图凭证 Private Key 处。

二、主页面 -> 凭据 -> 系统 -> 右击全局凭据 -> 添加凭据,创建一个类型为 SSH Username with private key 用于获取代码的凭据。

jenkins-unrestricted-private-key

创建项目并发布测试

本文以 Hello World demo 项目为例演示。

# 下载 demo
git clone http://gitlab.ryana.cn/ryan/helloworld.git
# 进入项目
cd helloworld
# 创建标签
git tag 1.0.0
# 推送
git push origin 1.0.0

主页面 -> 新建任务 -> 输入任务名称,构建一个 Maven 项目。

jenkins-new-job

配置 Git 参数化构建。

jenkins-general

动态获取Git仓库tag,与用户交互选择Tag发布。

jenkins-general-git-parameter

指定项目Git仓库地址,并修改 */master$Tag,Tag 是上面动态获取的变量名,表示根据用户选择打代码版本。

jenkins-general-source-code

设置maven构建命令选项,利用pom.xml文件构建项目。

clean package -Dmaven.test.skip=true

jenkins-build-environment

在Jenkins本机镜像构建与推送到镜像仓库,并SSH远程连接到Docker主机使用推送的镜像创建容器。

在Jenkins主机执行的Shell命令如下:

  • war
REPOSITORY=115.47.124.247:5000/helloworld:${Tag}
# 构建镜像
cat > Dockerfile << EOF
FROM ryaning/tomcat:8-jre8
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY target/*.war /usr/local/tomcat/webapps/ROOT.war
CMD ["catalina.sh", "run"]
EOF
docker build -t $REPOSITORY .
# 上传镜像
docker push $REPOSITORY
  • jar
REPOSITORY=115.47.124.247:5000/helloworld:${Tag}
# 构建镜像
cat > Dockerfile << EOF
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/*.jar helloworld.jar
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/helloworld.jar"]
EOF
docker build -t $REPOSITORY .
# 上传镜像
docker push $REPOSITORY

SSH远程Docker主机执行的Shell命令如下:

REPOSITORY=115.47.124.247:5000/helloworld:${Tag}
# 部署
docker rm -f helloworld |true
docker image rm $REPOSITORY |true
docker container run -d --name helloworld -p 8080:8080 $REPOSITORY

jenkins-post-steps

OK,到这里项目自动化部署已配置完成,开始构建。

jenkins-build-job

选择tag,开始构建。

jenkins-build-maven-project

点击左下角构建历史里,右击第一个查看控制台输出。

jenkins-build-console

访问。

Connecting to 115.47.124.247:54022...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.

Last login: Sat Mar  2 15:40:21 2019 from 180.155.249.31
[root@test ~]# docker ps
CONTAINER ID        IMAGE                                 COMMAND             CREATED             STATUS              PORTS                    NAMES
8d5c8143ce13        47.101.61.194:5000/helloworld:3.0.0   "catalina.sh run"   5 minutes ago       Up 5 minutes        0.0.0.0:8080->8080/tcp   helloworld
[root@test ~]# curl -l 127.0.0.1:8080
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
[root@test ~]#

results matching ""

    No results matching ""