首先说下为什么要写这篇文章,网上基于jenkins自动话部署的文章很多,但是大多基于官方镜像做的,官方的latest版本的镜像部分插件不可用,笔者结合工作实际情况和本着学习的态度打算从零开始搭建一套自动化部署环境,记录这个过程中遇到的问题和如何解决问题。
本文重点介绍jenkins,自建jenkins镜像,以及jenkins如何在docker容器中运行,jenkins和docker私有仓库又是怎么玩的。docker说明、安装和git说明、安装,maven说明、安装,gitlab说明,安装。在本文中不会特别详细的介绍。
本文部分引用 网上51CTO图片 。
背景说明
Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。
先来了解一下比较典型的java项目发布工作流程:
1.java项目开发 >> 2.提交项目代码到(git或svn) >> 3. 拉取项目代码(jenkins或手动) >> 4.编译项目代码(jenkins或手动) >> 5.发布java项目,并运行java项目 >> 6.测试
在来看看用docker+jenkins+git发布java项目流程又是怎样的呢:
1.java项目开发 >> 2.提交项目代码git容器 >> 3.jenkins容器拉取项目代码 >> 4.maven编译构建项目 >> 5.jenkins发布项目到tomcat容器 >> 6.测试
看到上面的流程后,在没有jenkins的情况下这些步骤都是手工完成的,有了Jenkins的帮助,在这6步中,除了第1步,后续的5步都是自动化完成的。当你完成了提交,Jenkins会自动运行你的编译脚本,编译成功后,再运行你的测试脚本,这一步成功后,接着它会帮你把新程序发布出去,特别的,在最后一步,你可以选择手动发布,或自动发布。
1、 环境描述
1、服务器部署信息
服务器主机名IP运行服务
jenkins服务器:jenkins192.168.154.128 安装docker、 运行jenkins容器、gitlab、jdk、maven,docker私有库
app 服务器docker 192.168.154.128 安装docker、创建镜像运行java项目 gateway
说明:
本文中完全是模拟生产环境中服务器的规划:git单独部署、jenkins单独部署。如果你没有这么多服务器,可以把git服务器和jenkins服务器放在一起来测试。前提是不要搞晕了就可以。
gateway是 springcloud 的gateway,基于springboot和maven
2、版本信息
名称版本软件包说明
服务器 Centos 7 及内核信息
docker 版本:
JDK环境 1.8.211
maven 版本信息: apache-maven-3.6.1-bin.tar.gz
jenkins 采用2.150 版本 的war包,下载地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/2.150/jenkins.war
registry最新版本docker hub下载最新registry镜像docker私有仓库
3、 准备工作
3.1 JDK的安装
这里就不介绍了。通过rpm包的免配置自动安装或者tar.gz包配置安装网上有很多文章 。
3.2 docker 的安装
安装下一些必要的系统工具:
```
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
```
添加阿里的yum的源:
```
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
```
更新 yum缓存 :
```
sudo yum makecache fast
```
安装docker :
```
sudo yum -y install docker-ce
```
docker 启动:
```
sudo systemctl start docker
```
docker 设置开机自启动:
```
sudo systemctl enable docker.service
```
3.3 docker 私有仓库安装(不使用私库可以无视)
sudo docker search register
选第一个镜像就好
拉取镜像:
sudo docker pull register
这里就可以通过 docker images 查询本地镜像
然后通过该镜像启动一个容器:
```
sudo docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry --name register register
```
说明这里是将本地的 /opt/data/registry 挂载到 容器的 /tmp/registry ,防止容器删除的时候, 容器里面的镜像会丢失。
将本地镜像push到私库:
```
sudo docker tag nginx 192.168.154.128:5000/nginx
```
这里注意。因为Docker从1.3.X之后,与docker registry交互默认使用的是https,然而此处搭建的私有仓库只提供http服务,所以当与私有仓库交互时就会报上面的错误。
这个问题解决方法:
首先找到 docker.service 文件
```
find / -name docker.service
```
然后修改docker.service 文件
vim /usr/lib/systemd/system/docker.service
加一条这句话:--insecure-registry 192.168.154.128:5000
sudo docker push 192.168.154.128:5000/nginx
如果我们要查询私库中的所有镜像可以用如下:curl -XGET http://registry:5000/v2/_catalog
3.4 jenkins 安装
所有准备jenkins 需要的tar包和war包:
apache-ant-1.9.14-bin.tar.gz
apache-maven-3.6.1-bin.tar.gz
apache-tomcat-9.0.21.tar.gz
git-2.14.2.tar.gz
jdk-8u211-linux-x64.tar.gz
jenkins.war
然后编写Dockerfile
代码如下:
```
FROM centos:7
USER root
# author info
MAINTAINER wupeng 513943544@qq.como
#安装一些必要的系统工具:
# install git
ADD git-2.14.2.tar.gz /opt/
RUN yum install -y gcc gcc-c++ automake autoconf libtool make curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker
# 切换上下文
WORKDIR /opt/git-2.14.2
RUN ./configure --prefix=/usr/local/git \
&& make && make install \
&& ln -s /usr/local/git/bin/git /usr/sbin/git
# install jdk
ADD jdk-8u211-linux-x64.tar.gz /opt/
ENV JAVA_HOME /opt/jdk1.8.0_211
ENV CLASSPATH .:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $JAVA_HOME/bin:$PATH
# install maven
ADD apache-maven-3.6.1-bin.tar.gz /opt/
ENV MAVEN_HOME /opt/apache-maven-3.6.1
ENV PATH $MAVEN_HOME/bin:$PATH
RUN mkdir -p /var/jenkins_home
ENV JENKINS_HOME /var/jenkins_home
ADD apache-tomcat-9.0.21.tar.gz /opt/
# startup tomcat
ADD jenkins.war /opt/apache-tomcat-9.0.21/webapps
CMD /opt/apache-tomcat-9.0.21/bin/catalina.sh run
CMD systemctl start docker
# expose memcached port
EXPOSE 8080
```
ps:想在dockerfile里面加上安装docker,结果测试发现容器是不能用systemctl 的,导致docker 启动不了,不过没关系,不装docker,可以用宿主机的docker 完成push到私库的操作。
然后通过docker build 创建镜像
dcoker build -t jenkins_2.150 -f /home/Dockerfile .
注意 . 别漏掉了。
创建成功。
将镜像运行成容器:
```
docker run -d -p 8080:8080 -p 50000:50000 -v /soft/jenkins/jenkins_home:/var/jenkins_home -v /etc/localtime:/etc/localtime --name jenkins_2.150 jenkins:2.150
```
查询容器 :
docker ps
验证docker jenkins 环境是否正常。
进入容器 :
```
docker exec -it jenkins_2.150 /bin/bash
```
验证 jdk 和maven git docker是否正常。
其他都没问题。 然后查看 jenkins 是否正常,退出容器,然后docker logs -f jenkins_2.150
可以看到jenkins的 初始化密码:
这里需要注意,jenkins 官方的源下载插件的时候,各种被墙,所以这里修改成清华大学的源。
修改文件在:
/soft/jenkins/jenkins_home/hudson.model.UpdateCenter.xml
这个目录就是之前启动容器的时候挂载的目录,然后找到这个文件。
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
然后 重启docker 容器
docker restart jenkins_2.150
然后登陆到jenkins
http://192.168.154.128:8080/jenkins
密码就是之前 打印 的密码,如果找不到 ,直接查看下面路径的初始密码
cat /var/jenkins_home/secrets/initialAdminPassword
此路径是jenkins容器内部的路径,可以直接看挂载的jenkins_home
登录之后,选择第一个,安装建议的插件。
部分插件装不成功的插件, 直接过也没事, 进去之后还能针对性安装,接下来基本操作, 不细说了。安装好了 重启下容器。
4、gitlab 配置
4.1创建测试项目
创建gateway 项目,然后把本地测试项目push到gitlab仓库,具体操作省略,gitlab有详细步骤。
4.2 配置ssh key
进入jenkins 容器。 然后配置git user.name 和 user.email 。然后生成ssh key 配置上即可。
生成ED25519 SSH key
ssh-keygen-ted25519-C"email@example.com" 这里邮箱填配置的邮箱 配置 git config --global user.email "email@example.com"
生成之后默认在是 ~/.ssh/目录下面。 然后cat 下, 把内容复制gitlab里面即可。记得是公钥 !
5、Jenkins 配置
首先这里说下jenkins 做了写什么。
Jenkins 主要是在git仓库拉取代码,然后在本地 mvn package 成jar 包 然后用已经写好的Dockerfile docker build 成 镜像,然后用 push 到本地的私库。之后再调用ssh 到appService 去从 私库里面pull 镜像, 然后再运行成容器。目的明确之后, 在来做这套就很好解决。
5.1、安装插件
这里说下,Jenkins需要安装下面这个插件才可以看到 创建maven项目
5.2、jenkins 配置工具信息
在这里配置 maven JDK的配置信息。git因为已经做了软连接,不用。
例如 配置maven
这里需要注意的安装的maven 默认是官方的源镜像仓库,国内下载的话,各种被墙,所以这里需要修改maven settings.xml文件,我这里改成阿里的仓库。
```
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
```
至于settings.xml的位置,可以用find 查询下, 在/opt/maven/conf 下面.
5.3、创建一个maven工程
然后配置
根据自己情况自己配置。
配置git 仓库信息:
这里说明下:已经配置了sshkey 结果还报错,这么解决
然后再配置 git repo信息,就不报错了。这里222端口是宿主机 映射gitlab 容器的 22端口
配置maven 打包指令
然后 可以尝试跑一下,【立即构建】
这里看到 已经build成功了。
接下来,要做的事是把jar build成镜像,然后启动,
目前有两种解决办法:
一个简单点的 直接用scp 指令将需要的jar直接传输到对应的服务器,
然后在目标服务器(app服务器)上做build images 和 启动操作。
一个复杂点的方案就是使用私库,利用tag,构建将镜像在jenkins宿主机上build成image然后push到私库,
然后目标服务器(app服务器) 上pull images 然后启动。(私库没有删除镜像的操作,目前没找到)
不管那个方案,先编写jar build成images 的dockerfile,代码如下:
首先准备下Dockerfile文件 。
```
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD ./target/gateway.jar gateway.jar
CMD java -Xms2G -Xmx2G -Xss256k -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+UseCompressedOops -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/dump -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/dump/heap_trace.txt -XX:NewSize=512m -XX:MaxNewSize=512m -Djava.security.egd=file:/dev/./urandom -jar gateway.jar
EXPOSE 9988
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
```
这里解释下,
FROM openjdk:8-jdk-alpine 基于 openjdk:8-jdk-alpine 构建
VOLUME /tmp 将 /tmp 挂载的宿主机/tmp 目录,
ADD ./target/gateway.jar gateway.jar 添加gateway.jar (注意路径)
CMD java 这句 就是java -jar xxx.jar 启动jar包了。
EXPOSE 9988 声明端口。
最后两句 就是定义时区和做了个软连接。
第一种解决方案:使用scp指令 直接在appServer上拷贝jar
scp 可以写个shell脚本, 也可以通过ssh加授信。
这里就说下加ssh授信怎么操作:
在appServer 生成rsa 非对称密钥对,
ssh-keygen -t rsa
遇到提示直接回车就好。
然后生成的密钥在~/.ssh下
然后在将id_rsa.pub这个公钥的内容copy到目标服务器(jenkins宿主机)的~/.ssh/ 目录下的 authorized_keys 里面去 (没有就创建一个authorized_keys 文件)。
然后可以测试下 ssh ip ,直接进去表示成功了,
然后在jenkins 里面写shell指令。
如图:
```
docker stop gateway
docker rm gateway
docker rmi gateway
```
```
cd /soft/jenkinsdata/gateway
rm -rf *.jar
scp root@192.168.154.128:/soft/jenkins/jenkins_home/workspace/gateway/target/gateway.jar .
docker build -t gateway -f /soft/jenkinsdata/gateway/Dockerfile .
docker run -it -d -p 9988:9988 -v /etc/localtime:/etc/localtime --name gateway gateway
```
在build 之前先停止docker 容器,然后删除镜像。
然后再build成功之后。
重新scp jar 过来,build成镜像,然后启动成容器。
然后启动试一试:
Error response from daemon: No such container: gateway
Error: No such container: gateway
Error: No such image: gateway
这里因为找不到容器,是因为 第一次还没启动过容器,但是这样写会导致报错,所以这里需要处理下。
当有这个容器的时候就关掉 然后删除容器及其image 如果没有,无事发生。这样处理就OK了。
用一个shell脚本来做这个操作:
```
#!/bin/sh
if [ $# == 0 ];then
echo "请输入需要关闭服务名称"
exit 0
else
cid=$(docker ps -a | awk '$2~/'$1'/{print $1,$2}')
arr=($cid)
len=${#arr[@]}
if [[ len -eq 0 ]];then
echo "没有与$1相关的服务"
elif [[ len -eq 2 ]];then
echo "匹配到服务${arr[1]}"
id=${arr[0]}
echo "关闭containerId 为$id的服务"
docker stop $id
docker rm $id
docker rmi ${arr[1]}
else
echo "匹配到多个与$1相关的服务"
for i in "${!arr[@]}"
do
t=`expr $i % 2`
if [ $t -eq 0 ];then
let ii=i+1
echo "${arr[$ii]}"
fi
done
fi
fi
```
处理之后,修改下 jenkins shell配置,然后再次执行构建项目,经过一段时间等待,到appServer 看下 gateway已经启动了。
到这里就OK了,简版测试环境的自动化部署就大功告成。
最后总结:
Docker+Maven+Jenkins +Git 自动化部署解决方案,核心是利用jenkins的git和maven插件来完成项目的pull 和build操作,然后通过build的pre或者post的执行shell的机制来执行自己想要执行的任何shell脚本,这里就是docker的创建镜像和容器的指令。
需要注意的是shell 脚本执行错误导致整个构建失败的情况。