本篇文章将会从无到有,构建一套持续集成环境,该篇涉及以下技术点:
一、课程目标
这里我们先简单介绍一下自动化部署和持续集成的概念:持续集成,简称CI,是一项由Grady Booch提出的技术,它鼓励开发者们持续不断地将他们的代码合并到主干源码仓库。这些'合并'或者'提交',每一次并入到仓库通常都伴随着执行一系列的自动化任务:代码的编译,单元测试和集成测试的执行,评判代码质量是否下降的静态代码质量分析等等。
在本次搭建中我们需要简单实现自动化部署,以下我们会一步步地将springBoot项目发布到服务器,并通过Jenkins自动拉取Git代码打包并发布(也可以采用Git webHook触发Jenkins的触发器实现自动构建)
二、环境准备
本次我们使用win10企业版自带的虚拟机Hyper-V
2.1开启Hyper-V
参考百度百科:https://jingyan.baidu.com/article/ac6a9a5e1f164a2b653eac33.html
2.1装载CentOS7,下载Centos7的ios镜像
参考百度资料:https://www.cnblogs.com/cxxjohnson/p/9267988.html
2.3开启CentOS7虚拟机,如图:
2.4下载Xshell
Xshell下载地址:https://xshell.en.softonic.com/
大家可以自行破解,破解过程比较麻烦,不做介绍
打开XShell链接到CentOS7,这里我们需要知道CentOS7的ip,我们在虚拟机中输入
ifconfig
获取ip,填写账户密码,以后使用XShell的session窗口进行操作,很方便
后面我们所有的操作,环境搭建都会在这一台叫做‘纯净Linux’的虚拟机上进行,至此,我们的基础环境准备OK
三、Docker安装
3.1Docker简介:http://dockone.io/article/2804
3.2在CentOS7上安装Docker
3.2.1、Docker 要求 CentOS 系统的内核版本高于 3.10 ,输入以下命令验证你的CentOS 版本是否支持 Docker 。
通过 uname -r 命令查看你当前的内核版本
$ uname -r
3.2.2、使用 root 权限登录 Centos。确保 yum 包更新到最新。
$ sudo yum update
这个过程可能会比较慢,要耐心等待
3.2.3、卸载旧版本(如果安装过旧版本的话)
$ sudo yum remove docker docker-common docker-selinux docker-engine
3.2.4、安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
3.2.5、设置yum源
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
3.2.6、可以查看所有仓库中所有docker版本,并选择特定版本安装
$ yum list docker-ce --showduplicates | sort -r
3.2.7、安装docker
$ sudo yum install docker-ce #由于repo中默认只开启stable仓库,故这里安装的是最新稳定版17.12.0
$ sudo yum install # 例如:sudo yum install docker-ce-17.12.0.ce
3.2.8、启动并加入开机启动
$ sudo systemctl start docker
$ sudo systemctl enable docker
3.2.9、验证安装是否成功(有client和service两部分表示docker安装启动都成功了)
$ docker version
3.2.10.超时解决办法
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://3056ffe7.m.daocloud.io
方案2:在/etc/docker目录下创建daemon.json文件,添加如下内容
{ "registry-mirrors": ["https://almtd3fa.mirror.aliyuncs.com"] }
systemctl daemon-reload
service docker restart
3.2.11.Docker常用命令
docker search [] //查找
docker pull [] //拉取镜像
docker images //查看宿主机镜像
docker ps //查看Docker正在运行的容器
docker ps -a //查看已经推出的容器
docker exec -it e6bcab9047a2 /bin/bash //进入容器内部
至此我们的docker环境安装完成!
四、SpringBoot项目准备
4.1打开IDEA,File->New->Project,使用spring initializr构建一个简单的springboot项目
4.2编写一个RestController,便于测试
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Controller
@RequestMapping("index")
public class IndexController {
@RequestMapping("hello")
@ResponseBody
public String hello(String name){
// 获取IP地址
try {
String ip = InetAddress.getLocalHost().getHostAddress();
String msg = String.format("Hello!you are %s,the ip is %s !",name,ip);
return msg;
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
}
4.3启动springBoot,访问 localhost:8080/index/hello?name=hqa ,正常访问则,项目构建成功,这里很简单,不在贴图
4.4安装Git,Git下载地址:https://git-scm.com/
我们将项目发布到Git的demo仓库,这里请读者自行创建
4.5现在我们回到本地项目,执行打包命令
mvn clean package
4.6在本地编写一个Dockerfile文件,用记事本打开,输入以下脚本
FROM java:8
VOLUME /tmp
COPY demo-0.0.1-SNAPSHOT.jar app.jar
RUN bash -c "touch /app.jar"
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8080", "> /log/app.log"]
解释:
java:8是指Docker Hub上官方提供的java镜像,版本号是8也就是jdk1.8,有了这个基础镜像后,Dockerfile可以通过FROM指令直接获取它的状态——也就是在容器中java是已经安装的,接下来通过自定义的命令来运行Spring Boot应用:
VOLUME指向了一个/tmp的目录,由于Spring Boot使用内置的Tomcat容器,Tomcat默认使用/tmp作为工作目录。效果就是在主机的/var/lib/docker目录下创建了一个临时文件,并连接到容器的/tmp。
将项目的jar文件作为app.jar添加到容器
RUN表示在新创建的镜像中执行一些命令,然后把执行的结果提交到当前镜像。这里使用touch命令来改变文件的修改时间,Docker创建的所有容器文件默认状态都是“未修改”。这对于简单应用来说不需要,不过对于一些静态内容(比如:index.html)的文件就需要一个“修改时间”。
EXPOSE 容器暴露端口
ENTRYPOINT 应用启动命令 参数设定
4.7在linux主机根目录新建/hqa/docker 文件夹,cd进去,XShell 使用rz命令,或者使用xftp,或打开XShell文件传输窗口,将文件拖拽进去
4.8在本目录执行构建,执行如下命令
docker build -t [images名称] . //注意最后有个.,代表当前目录,镜像名称可以自己随便取
由于之前linux主机并未安装jdk8基础镜像,此处构建Docker会自动拉取JDK镜像
4.9查看springboot镜像
docker images
4.10
启动springboot-demo容器
docker run --name spring-boot-docker -d -v /opt/jar/springBootDocker/logs:/log -p 8099:8080 springboot-demo
//-d代表后台运行,下面demo为演示启动效果未加-d
解释说明:
run: 容器启动命令
--name spring-boot-docker 自定义容器命名
-d 程序后台启动
-v /opt/jar/boot-docker/logs:/log 应用日志 数据卷的方式存储
-p 8099:8080 用主机8099端口映射容器端口
boot-docker 镜像名称
启动界面:熟悉的感觉~~
4.11大功告成!访问 http://192.168.38.35:8099/index/hello?name=hqa 可以看到页面正常显示
4.12关闭容器
docker stop spring-boot-docker
五、Docker安装Nginx
5.1Nginx的介绍这里就不做介绍了,我们直接开始动手
5.2安装步骤
5.2.1安装Nginx Docker镜像
docker pull Nginx
5.2.2创建Nginx挂载目录
mkdir -p /data/nginx/{conf,conf.d,html,logs}
关于挂在目录的含义读者可以自行百度,主要用于在宿主机和docker容器之间做一个共享映射,方便我们配置文件
5.2.3我们先来配置一下咱们的Nginx文件,再次之前我们将springboot镜像再启动一个容器,映射端口为6099,这里参考上面就行,两个springboot项目方便我们做负载均衡测试
vim /data/nginx/conf/nginx.conf
###################################################nginx.conf配置如下###############################################################
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name 102.168.38.35; //宿主机ip
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://pic; //代理设置
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
upstream pic{
server 192.168.38.35:8099 weight=5; //springboot ip 配置
server 192.168.38.35:6099 weight=5; //springboot ip 配置
}
}
5.2.4启动Nginx容器
#将容器中nginx的80端口映射到本地的81端口
docker run --name nginx81 -d -p 81:80 -v /data/nginx/html:/usr/share/nginx/html -v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /data/nginx/logs:/var/log/nginx -v /data/nginx/conf.d:/etc/nginx/conf.d -d nginx:latest
5.2.5查看启动的容器
[root@promote logs]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e77a62262f8a springboot-demo "java -jar app.jar -…" 22 hours ago Up 16 minutes 0.0.0.0:6099->8080/tcp spring-boot-docker2
9e277aba4a0f nginx:latest "nginx -g 'daemon of…" 23 hours ago Up 12 minutes 0.0.0.0:81->80/tcp nginx81
e6bcab9047a2 springboot-demo "java -jar app.jar -…" 24 hours ago Up 17 minutes 0.0.0.0:8099->8080/tcp spring-boot-docker
5.2.6打开浏览器输入Nginx 访问地址 http://192.168.38.35:81/index/hello?name=hqa
若ip交替出现,则配置成功!
5.2.7若无法访问显示404或403,则关闭Centos防火墙
firewall-cmd --state //查看防火墙
systemctl stop firewalld.service //临时关闭
systemctl disable firewalld.service //永久关闭
至此我们的Nginx负载均衡配置完成!
六、Docker安装Jenkins
6.1jenkins介绍:Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。参考地址:https://jenkins.io/zh/doc/
6.2安装步骤
6.2.1拉取官方镜像地址
docker pull jenkins/jenkins:latest
docker images | grep jenkins/jenkins:latest
这里一定要拉取最新的版本!这里一定要拉取最新的版本!这里一定要拉取最新的版本!重要的事情说三遍,要是拉了旧版本很可能会有各种蛋疼的错误,如提示升级,插件失败,或无法安装,而我们的jenkins是部署在Docker容器的中的,因此这些错误很难搞定!拉取最新的就不会有这些问题~
6.2.2创建挂载目录(不创建这个问题也不大)
mkdir /home/jenkins_home
6.2.3构建image
docker run -d --name myjenkins -p 10086:8080 -v /home/jenkins_home:/var/jenkins_home jenkins
#若失败,可能是jenkins安装路径问题,则执行下面这个
docker run -d --name myjenkins -p 10086:8080 -v /home/jenkins_home:/home/jenkins_home jenkins
6.2.4构建成功后,访问10086端口如图:等待一段时间
搭建成功!
6.2.3拷贝出上一步需要的授权密码
docker exec -it myjenkins /bin/bash //进入容器
cat /var/jenkins_home/secrets/initialAdminPassword //将密码复制出来
6.2.4点击continue我们将看到如下新界面,直接点击导入插件,等待更新,此段时间将会比较漫长
插件安装:
首先填入信息,若进入主界面后如果看到右上角有错误的提示信息,那么请注意上面那个说了三遍的问题!!或者尝试更新修复!
6.2.5配置加速器
【系统管理】-> 【插件管理】-> 【高级】-> 【升级站点】
更换地址:http://mirror.xmission.com/jenkins/updates/current/update-center.json
至此Jenkins的配置就结束了!
记住你的用户名密码,这一点不用多说!
七、使用Jenkins进行远程部署
7.1终于到了最后一步,也是比较有难度的一步,这一步完成以后整个教程到此就结束啦
7.2配置步骤
7.2.1首先,我们需要给Jekins安装三个插件,点击插件管理,搜索安装,非常的简单
7.2.2Jekins全局配置
Jenkins->全局工具配置,由于我们的jenkins跑在docker容器上面,因此以下配置我们都需要勾选自动安装
7.2.2.1JDK配置,此处需要注册Oracle账号,没有的话需要去官网注册
7.2.2.2GIT配置
7.2.2.3MAVEN配置
7.2.2.4SSH(Publish over SSH)配置
7.2.2.5配置一个邮箱,用于Jenkins给我们发邮件(非必须)
参考资料:https://www.cnblogs.com/gcgc/p/5631385.html
7.2.3新建一个工程,选择新建一个自由风格的软件项目,任务名称叫做 Test,点击确定~
7.2.4工程配置清单
7.2.4.1Git仓库
7.2.4.2构建配置,这边做的比较简单,仅仅是进行Maven打包
7.2.4.3构建完成后,发送邮件,此处仅仅在构建失败的情况下发送
7.3构建测试
7.3.1一切准备成功,现在主控制台,看到我们的测试工程Test
7.3.2点击立即构建,或者双击Test项目,进去里面
7.3.2点击控制台输出,可以看到右侧的项目构建输出信息
7.3.3此处可以看到打包位置
7.3.4打开Xshell,进入/var/jenkins_home/workspace,我们可以看到项目jar包已经打包完成!
docker exec -it myjenkins /bin/bash
cd /var/jenkins_home/workspace/Test
7.4自动化远程发布
实验进行到这里,已经完成得差不多了,接下来是难处所在,我们需要将target目录下的jar包,发布到springboot容器的宿主机,(也是本台机器,实际上到目前为止我们的jenkins,nginx,java,springboot全部运行在一台宿主机上面(ip:192.168.38.35),实际环境中,它们肯定不会只在一台机器上),然后进springboot镜像重新打包部署!这里使用阿里云镜像库会比较容易实现这个流程,但是现在我们这里不那么做,好,我们先来整理一下流程思路:
7.4.1增加构建步骤,将jekins容器jar包文件上传至宿主机
7.4.2编写Shell脚本,将内容填充至Exec command(什么!Shell脚本不会写点击此处转到菜鸟教程学习一把)
echo '===============项目已经上传至/hqa/docker====================='
echo '==当前分支:${GIT_BRANCH},当前git地址:${GIT_URL}'
#停止当前springboot镜像
echo '===============正在停止所有springboot-demo镜像容器====================='
sudo docker stop `docker ps -a|grep springboot-demo|awk '{print $1}'`
echo '===============正在删除所有springboot-demo镜像容器====================='
#删除当前springboot容器以及镜像
sudo docker rm `docker ps -a|grep springboot-demo|awk '{print $1}'`
sudo docker rmi springboot-demo
echo '===============删除springboot-demo镜像完成====================='
echo '===============正在打包并运行所有springboot-demo镜像====================='
cd /hqa/docker
docker build -t springboot-demo .
echo '===============打包新的springboot-demo镜像完成====================='
#启动springboot容器
echo '===============正在启动所有springboot-demo镜像容器====================='
docker run --name spring-boot-docker -d -v /opt/jar/springBootDocker/logs:/log -p 8099:8080 springboot-demo
docker run --name spring-boot-docker2 -d -v /opt/jar/springBootDocker2/logs:/log -p 6099:8080 springboot-demo
echo '===============启动所有springboot-demo镜像容器完成====================='
echo '===============正在重启Nginx====================='
docker restart nginx81
echo '===============Nginx重启OK====================='
echo '===============[JENKINS BUILD SUCCESS (*_*) - BY MC.HAN]====================='
注:更优雅的Shell脚本可以使用funciton,这里咱就不搞那么多花里胡哨的了,能跑就行了...这里需要说明一下,这里为了演示效果,重启了Nginx,实际生产环境决不能这么做!而且我们还必须要考虑可用性与梯度发布的问题,不能将服务器上的机器全部停掉!
7.4.3万事俱备,我们现在来测试一下最终的效果
更新一下项目的测试代码,提交至git仓库,注意我们打印字符串加上了‘jenkins updated test’
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Controller
@RequestMapping("index")
public class IndexController {
@RequestMapping("hello")
@ResponseBody
public String hello(String name){
// 获取IP地址
try {
String ip = InetAddress.getLocalHost().getHostAddress();
String msg = String.format("jenkins updated test,Hello!you are %s,the ip is %s !",name,ip);
return msg;
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
}
7.4.4使用jenkins重新构建项目,走起~
查看构建控制台
===============项目已经上传至/hqa/docker=====================
==当前分支:origin/master,当前git地址:https://github.com/hqa11/demo.git
===============正在停止所有springboot-demo镜像容器=====================
"docker stop" requires at least 1 argument.
See 'docker stop --help'.
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
Stop one or more running containers
===============正在删除所有springboot-demo镜像容器=====================
"docker rm" requires at least 1 argument.
See 'docker rm --help'.
Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
Remove one or more containers
Error: No such image: springboot-demo
===============删除springboot-demo镜像完成=====================
===============正在打包并运行所有springboot-demo镜像=====================
Sending build context to Docker daemon 16.77MB
Step 1/6 : FROM java:8
---> d23bdf5b1b1b
Step 2/6 : VOLUME /tmp
---> Using cache
---> 5813748c6b0d
Step 3/6 : COPY demo-0.0.1-SNAPSHOT.jar app.jar
---> 48db7d175520
Step 4/6 : RUN bash -c "touch /app.jar"
---> Running in 9bbcbaf73ead
Removing intermediate container 9bbcbaf73ead
---> 9a83afdc5eb5
Step 5/6 : EXPOSE 8080
---> Running in e577d678f3a8
Removing intermediate container e577d678f3a8
---> f2509de02a09
Step 6/6 : ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8080", "> /log/app.log"]
---> Running in c07c1b7863c1
Removing intermediate container c07c1b7863c1
---> 47bb8ced22b9
Successfully built 47bb8ced22b9
Successfully tagged springboot-demo:latest
===============打包新的springboot-demo镜像完成=====================
===============正在启动所有springboot-demo镜像容器=====================
fd42095d132e132b826b5c9f700ae2893306f59f854921e73c7a95a509ce2828
c29e1b62f7339e509af6224d657cfe3db723733b6825fce386a19b2d4da80c74
===============启动所有springboot-demo镜像容器完成=====================
===============正在重启Nginx=====================
nginx81
===============Nginx重启OK=====================
===============[JENKINS BUILD SUCCESS (*_*) - BY MC.HAN]=====================
SSH: EXEC: completed after 49,092 ms
SSH: Disconnecting configuration [Hyper-V纯净Linux] ...
SSH: Transferred 1 file(s)
No emails were triggered.
Finished: SUCCESS
稍等片刻,等待容器启动,现在访问一下nginx http://192.168.38.35:81/index/hello?name=hqa
我们按几下F5,ip交替变换着,说明已经完全部署成功了!
八、结束语
到此阶段,我们的部署流程还远没有达到持续集成和部署的应有要求,这里就需要老铁们继续的深入学习了~~~