目录
- 背景
- 部署分析
- 为什么采用git+maven+jdk8的centos7的docker环境去部署springboot项目?
- 部署流程简要概括
- 项目部署
- 1.linux服务器下载Git
- 2.项目根目录下编写Dockerfile文件
- 解析
- 3.项目根目录下编写sh脚本
- ①定义全局变量
- 分析
- ②初始化——构建docker镜像和项目运行容器
- 分析
- 注意事项
- 1.项目之后的访问问题?
- 容器运行问题
- ③检查程序(jar包)是否运行
- ④启动程序
- 分析
- 附
- SpringBoot程序(jar包)运行环境如何确定?
- 为什么要定义这么多的环境配置文件?
- 环境配置文件如何执行使用?
- application.yml默认执行运行环境
- 项目运行端口的优先级
- ⑤停止程序
- ⑥重启程序
- ⑦查看程序运行状态
- ⑧git拉去最新代码,maven打包,并运行程序
- 分析
- ⑨根据参数执行对应的方法(sh脚本的第一个参数)
- ⑩执行sh不输入第一个参数,则进行提示,并退出
- 4.上传项目到远程仓库
- 5.git去clone项目到linux服务器
- 6.进入clone项目后的根目录执行sh脚本,初始化docker镜像和容器
- 解析
- 判断是否构建成功
- 7.进入容器,执行相应的sh脚本的命令
- 进入项目容器,假设容器名为test
- 进入挂载目录
- sh脚本运行命令大全
- ①查看项目(程序运行状态)
- ②启动项目
- 以默认项目的环境配置文件和端口启动
- 指定环境配置文件和默认端口启动
- 指定环境配置文件和端口启动
- ③停止项目
- ④重启项目
- ⑤拉取最新代码,打包,并启动项目
- 8.配置项目可以被远程访问(可选)
- 容器配置映射端口,无需这个配置
- 配置nginx反向代理,访问容器中的程序
- 查看容器的ip
- 配置反向代理
- 小结
- 缺点
- 优点
- 附
- Dockerfile文件
- sh部署脚本文件
- sh脚本需要根据实际项目需求修改的地方
背景
之前我部署springboot项目的时候会采用docker+jenkins的持续集成部署,但是jenkins比较重,而且配置起来相对麻烦一些,而且配置与项目不一起,不方便统一管理。所以这次的项目负责人推荐我使用轻量级的docker 构建git+maven+jdk8的centos7环境,实现轻量级的springboot项目的自动化部署。
docker+jenkins的持续集成部署springboot项目(我的另一篇博客):部署SpringBoot的jar包项目让人头疼,不如使用jenkins+docker自动化部署jar包项目
部署分析
为什么采用git+maven+jdk8的centos7的docker环境去部署springboot项目?
- 选用docker是因为docker在linux上安装镜像(软件)方便快速,而且不用复杂繁琐的配置,再者容器运行相对对立,不互相影响。
- jdk8的环境就不多说了,jar包运行的基础环境。
- git环境,主要是为了可能在GitLab、GitHub或者码云上可以随时拉去最新的项目,进行合作开发,随时部署。
- maven环境,主要是为了可以将git新拉取的项目打成jar包。
- centos7环境,主要是为了可以安装以上的各种环境,方便后期可以安装其余所需要的环境(软件)。
除此之外最好可以在centos7的环境上安装vim,方便编辑文件。
部署流程简要概括
- linux上提前下载好Git,方便项目的第一次clone
- 项目根目录下编写Dockerfile文件(方便后续的sh脚本构建docker镜像)
- 项目根目录下编写sh脚本,用于初始化docker镜像和容器,以及完成自动化部署
- 将刚才编写好的项目上传到GIT仓库(GitLab/GitHub/码云)
- 在linux服务器上使用git拉取项目代码
- 执行sh脚本的init方法,初始化docker镜像、容器,并进行项目部署(后续会详细说明)
项目部署
1.linux服务器下载Git
这里使用yum快速下载安装:
sudo yum install -y git
查看git版本(判断是否安装成功)
git --version
2.项目根目录下编写Dockerfile文件
FROM centos:7
RUN yum -y update \
&& yum -y install vim \
&& yum -y install git \
&& yum -y install java-1.8.0-openjdk-devel.x86_64 \
&& yum install -y maven \
&& mkdir ~/.m2
RUN echo '<?xml version="1.0" encoding="UTF-8"?><settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"><mirrors><mirror><id>alimaven</id><mirrorOf>central</mirrorOf><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/repositories/central/</url></mirror></mirrors></settings>' > ~/.m2/settings.xml
解析
FROM centos:7
是要构建的镜像依赖于docker的centos7镜像,没有centos7的话,会直接去pull。- 之后就是在centos7的镜像上安装vim、git、jdk8和maven,并配置maven的阿里镜像。
3.项目根目录下编写sh脚本
为了分析的更加明白透彻,将sh脚本拆开分析,后续再附上完整的sh脚本文件。
sh脚本的命名,可以用"项目名.sh"
①定义全局变量
#!/bin/bash
# 镜像名字
IMAGE_NAME=centos7_mvn_git_java8
# docker 容器名字或者jar名字,这里都命名为这个
SERVER_NAME=xiaoyun-api-java
#这里的JAR_PATH为jar包所在位置
JAR_PATH=./xiaoyun_system/target/xiaoyun_system-1.0.0-SNAPSHOT.jar
profile=$2
port=$3
#Xms=$4
#Xmx=$5
分析
IMAGE_NAME
为构建后的docker镜像名SERVER_NAME
为以IMAGE_NAME
的镜像运行的容器名JAR_PATH
为maven打成jar包后的路径("./"指的是当前项目)profile=$2
为命令行输入的第二个参数,用来定义项目运行的环境(对应于application.yml的配置文件)port=$3
为命令行输入的第二个参数,用来定义项目的运行端口Xms Xmx
为定义jar包运行的内存等参数信息,暂时先注掉,如果有需要,可以自行定义(根据这篇博客,自定定义不成问题)
②初始化——构建docker镜像和项目运行容器
init()
需要在git clone项目后,在宿主机当前项目下,执行sh文件执行(挂载到容器后,容器没有docker环境,不能构建镜像和容器,再说不执行这一步骤,还没有容器)(容器能不能对docker“套娃”,我还真没了解,即使可以,一般也没人在容器安装docker吧,无限套娃)
#初始化——构建镜像和容器(在宿主机执行)
init(){
#容器id
CID=$(docker ps | grep "$SERVER_NAME" | awk '{print $1}')
#镜像id
IID=$(docker images | grep "$IMAGE_NAME" | awk '{print $3}')
# 构建docker镜像
if [ -n "$IID" ]; then
echo "Exit $SERVER_NAME image,IID=$IID"
else
echo "NOT exit $SERVER_NAME image,start build image..."
# 根据项目个路径下的Dockerfile文件,构建镜像
docker build -t $IMAGE_NAME .
echo "$SERVER_NAME image has been builded"
fi
if [ -n "$CID" ]; then
echo "Exit $SERVER_NAME container,CID=$CID. ---Remove container"
docker stop $SERVER_NAME # 停止运行中的容器
docker rm $SERVER_NAME ##删除原来的容器
fi
# 构建容器
echo "$SERVER_NAME container,start build..."
# 运行容器
# --name 容器的名字
# -d 容器后台运行
# -p 指定容器映射的端口和主机对应的端口
# -v 将主机的目录挂载到容器的目录中(不可少)
docker run -e TZ="Asia/Shanghai" -id -m 512M --memory-swap=1G --name $SERVER_NAME -v $PWD:/project/xiaoyun_api $IMAGE_NAME
echo "$SERVER_NAME container build end"
}
分析
- 判断docker有没有要构建的镜像,如果有镜像,不构建镜像;如果没有镜像,根据项目根路径下的Dockerfile文件构建镜像。这个git+maven+jdk8的centos7镜像是可以重复使用的,就相当于软件一样,所以构建后,第二个项目使用就可以不构建了。 所以这里判断有当前镜像时,就不在构建镜像了。
- 判断有没有预构建的容器,如果有,就先停止容器,删除容器;没有容器往下执行。然后构建容器(不管有没有容器,都重新构建容器)
- 构建容器时,设置时区、运行内存和挂载目录等配置(也可以根据需求自行更改)
注意事项
1.项目之后的访问问题?
我这里sh运行的容器没有配置映射端口信息,因为我后续会配置nginx反向代理到容器的IP和端口(每个docker容器都有独立的IP),用于反向代理访问项目。
如果项目端口固定,也可以配置映射端口。
容器运行问题
- 最好限制下容器运行内存等参数信息,否则如果服务器不大的话,可能会经常造成容器意外停止。
- 写容器运行配置最好可以提前配置完全,否则中途更改配置就比较麻烦了。
③检查程序(jar包)是否运行
#检查程序是否在运行
is_exist(){
pid=`ps -ef|grep $JAR_PATH|grep -v grep|awk '{print $2}' `
#如果不存在返回1,存在返回0
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}
程序运行返回0,不运行返回1,用于后续执行的条件判断
④启动程序
#启动方法
start(){
is_exist
if [ $? -eq "0" ]; then
echo "${SERVER_NAME} is already running. pid=${pid} ."
else
echo --------Starting application --------
nohup java -server -Xms512m -Xmx512m -XX:SurvivorRatio=4 -Xss256k -XX:PermSize=256m -XX:MaxPermSize=512m -XX:-DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -jar $JAR_PATH --spring.profiles.active=${profile:-dev} --server.port=${port:-8091} > start.log 2>&1 &
echo --------------Started!---------------
fi
}
分析
- 当程序没有运行,即
is_exist
返回1时在执行启动命令;若返回0则输出程序正在运行… -jar $JAR_PATH
运行jar包--spring.profiles.active=${profile:-dev}
在什么环境下运行程序,对应之后运行sh脚本的第二个参数(与全局变量的profile=$2对应)。默认dev环境运行(如果执行sh文件不输入第二个参数,则以dev运行)。--server.port=${port:-8091}
什么端口运行,对应之后运行sh脚本的第三个参数(与全局变量的port=$3对应)。默认8091运行(如果执行sh文件不输入第三个参数,则以8091端口运行)。- 其他的参数都是限制程序的运行环境,可以自定百度更改完善。
附
SpringBoot程序(jar包)运行环境如何确定?
可看下图:
- application-dev.yml对应开发环境配置文件
- application-test.yml对应测试环境配置文件
- application-prod.yml对应生产环境配置文件
- application.yml初始配置文件,指定默认以什么环境运行程序
为什么要定义这么多的环境配置文件?
比如在开发环境和测试环境swagger是可以使用的,但是在生产环境,即项目上线后swagger是不能使用的,防止随意访问api接口,保证安全性。
定义多种环境配置文件,在不同环境运行项目只需要直接指定配置文件就好,而不是不同环境下,修改application.yml的配置(这样不方便开发和维护)。
纯属个人理解
环境配置文件如何执行使用?
在sh脚本中,--spring.profiles.active=${profile:-dev}
就是指定运行不同环境的配置文件,例如生产环境就是dev,测试环境就是test,生产环境就是prod。刚好对应于编写application-
的后缀。
application.yml默认执行运行环境
application.yml内容如下,默认以dev环境运行程序(如果不指定程序运行环境)
spring:
profiles:
active: dev
项目运行端口的优先级
application.yml中配置有项目端口,sh的–server.port=${port:-8091}有默认端口,linux命令行还可以输入参数指定项目的运行端口,究竟项目会执行哪个配置的端口?
端口执行优先级:linux命令行指定的端口 > --server.port=${port:-8091}的默认端口 > application.yml中配置的端口
⑤停止程序
#停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
echo -----------Application Stopped------------
else
echo "${JAR_PATH} is not running"
fi
}
is_exist
返回0代表程序正在运行,通过kill -9
停止程序
⑥重启程序
先停止容器,再启动程序
#重启
restart(){
stop
start
}
⑦查看程序运行状态
判断程序是否在运行
#输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "${JAR_PATH} is running. Pid is ${pid}"
else
echo "${JAR_PATH} is NOT running."
fi
}
⑧git拉去最新代码,maven打包,并运行程序
#mvn
pull(){
echo "----------git:find status---------"
git status
echo "----------git:pull new coads---------"
git pull origin develop
if [ $? -ne 0 ]; then
exit
fi
echo "----------mvn clean package -Dmaven.test.skip=true---------"
mvn clean package -Dmaven.test.skip=true
if [ $? -ne 0 ]; then
exit
fi
echo "----------Preparing start application ---------"
is_exist
if [ $? -eq "0" ]; then
restart
else
start
fi
}
分析
git status
查看git状态git pull
拉取最新代码,分支可自己修改-Dmaven.test.skip=true
maven打包时跳过测试- 最后运行程序,根据状态选择启动还是重启程序
- git拉取代码和mvn打包若出现错误会返回状态为0,则
exit
退出
⑨根据参数执行对应的方法(sh脚本的第一个参数)
#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"init")
init
;;
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
"pull")
pull
;;
*)
usage
;;
esac
⑩执行sh不输入第一个参数,则进行提示,并退出
#使用说明,用来提示输入参数
usage() {
echo "Usage: sh 执行脚本.sh [init|start|stop|restart|status|pull] [profile] [port]"
exit 1
}
到此sh脚本编写完毕,文章末尾会附上完整的sh文件
4.上传项目到远程仓库
Dockerfile文件和sh脚本已经编写完毕,可以通过git上传到远程仓库,方便服务器拉取。
git上传项目的命令省略(比较简单)
5.git去clone项目到linux服务器
git clone 你的仓库的http方式的clone地址
以GitLab仓库为例:
我这里采用http的方式,这样比较简单,如果对安全性要求比较高,也可以采用ssh方式clone,但是相对麻烦一些。
6.进入clone项目后的根目录执行sh脚本,初始化docker镜像和容器
如果sh文件不再根目录,也可以进入相对应的目录
bash test.sh init
解析
- sh脚本暂时命名为test.sh
- 执行sh脚本的时候,输入第二个参数init,用于构建镜像和容器
判断是否构建成功
判断镜像是否存在
docker images
判断容器是否运行
docker ps
如果没有成功,说明构建命令没有配置对,需要修改一下。
7.进入容器,执行相应的sh脚本的命令
进入项目容器,假设容器名为test
docker exec -it test bash
进入挂载目录
即项目根目录(上文我挂载的是 /project/xiaoyun ,运行容器有挂载目录,会在容器运行时自动创建)
cd /project/xiaoyun
sh脚本运行命令大全
①查看项目(程序运行状态)
bash test.sh status
②启动项目
以默认项目的环境配置文件和端口启动
bash test.sh start
指定环境配置文件和默认端口启动
假设以生产环境启动为例
bash test.sh start prod
指定环境配置文件和端口启动
假设以生产环境,端口为9010启动为例
bash test.sh start prod 9010
注:start、restart、pull都可以输入第二个和第三个参数,以相应的配置文件和端口启动。
③停止项目
bash test.sh stop
④重启项目
bash test.sh restart
⑤拉取最新代码,打包,并启动项目
bash test.sh pull
8.配置项目可以被远程访问(可选)
容器配置映射端口,无需这个配置
如果sh文件启动docker容器时,映射了端口,那么不需要再进行额外的配置,直接根据映射端口去访问程序就好了。
配置nginx反向代理,访问容器中的程序
nginx可以使用宿主机的,也可以使用docker运行nginx容器。但是使用docker运行nginx容器,最好不要映射端口,而是共享宿主的端口,因为未来,你并不知道你配置的项目会用到那些端口。
nginx共享宿主机端口命令:--net host
nginx容器启动参考命令:
docker run -it --name nginx --net host -v /root/project:/var/www/html
-v /root/nginx:/nginx_conf nginx /bin/bash
查看容器的ip
查看容器信息
docker inspect 运行容器名/容器id
找到对应的IPAddress,即为容器的IP
配置反向代理
IP为容器IP,端口为项目的运行端口号
可以将这个nginx配置文件以“项目名.conf”命名,放到项目更目录下,方便统一管理和维护。
server {
listen 8091;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
proxy_pass http://172.18.0.2:8091;
#root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
后续关于nginx的使用,访问项目等,暂且忽略
小结
到此docker构建git+maven+jdk8的centos7环境,实现轻量级的springboot项目的自动化部署已经完成。
缺点
还是有一些美中不足
- linux服务器要预先安装Git,进行第一次clone代码(yum方式安装也只是需要执行一行代码,也比较方便)
- 相对于jenkins不是完全自动化,还是要手动输入一些命令(不过命令都比较简单)
总体来看还是很nice的。
优点
- 有了这个配置,从项目初期就可以进行服务器部署了,省去了部署时大量的繁琐操作。
- 项目部署配置与项目一起统一管理,方便后期的维护。
- 有了部署更改要求,直接更改sh脚本即可,非常方便
- …
附
Dockerfile文件
FROM centos:latest
RUN yum -y update \
&& yum -y install vim \
&& yum -y install git \
&& yum -y install java-1.8.0-openjdk-devel.x86_64 \
&& yum install -y maven \
&& mkdir ~/.m2
RUN echo '<?xml version="1.0" encoding="UTF-8"?><settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"><mirrors><mirror><id>alimaven</id><mirrorOf>central</mirrorOf><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/repositories/central/</url></mirror></mirrors></settings>' > ~/.m2/settings.xml
sh部署脚本文件
#!/bin/bash
# 镜像名字
IMAGE_NAME=centos7_mvn_git_java8
# docker 容器名字或者jar名字,这里都命名为这个
SERVER_NAME=xiaoyun-api-java
#这里的JAR_PATH为jar包所在位置
JAR_PATH=./xiaoyun_system/target/xiaoyun_system-1.0.0-SNAPSHOT.jar
profile=$2
port=$3
#Xms=$4
#Xmx=$5
#使用说明,用来提示输入参数
usage() {
echo "Usage: sh 执行脚本.sh [init|start|stop|restart|status|pull] [profile] [port]"
exit 1
}
#初始化——构建镜像和容器(在宿主机执行)
init(){
#容器id
CID=$(docker ps | grep "$SERVER_NAME" | awk '{print $1}')
#镜像id
IID=$(docker images | grep "$IMAGE_NAME" | awk '{print $3}')
# 构建docker镜像
if [ -n "$IID" ]; then
echo "Exit $SERVER_NAME image,IID=$IID"
else
echo "NOT exit $SERVER_NAME image,start build image..."
# 根据项目个路径下的Dockerfile文件,构建镜像
docker build -t $IMAGE_NAME .
echo "$SERVER_NAME image has been builded"
fi
if [ -n "$CID" ]; then
echo "Exit $SERVER_NAME container,CID=$CID. ---Remove container"
docker stop $SERVER_NAME # 停止运行中的容器
docker rm $SERVER_NAME ##删除原来的容器
fi
# 构建容器
echo "$SERVER_NAME container,start build..."
# 运行容器
# --name 容器的名字
# -d 容器后台运行
# -p 指定容器映射的端口和主机对应的端口
# -v 将主机的目录挂载到容器的目录中(不可少)
docker run -e TZ="Asia/Shanghai" -id -m 512M --memory-swap=1G --name $SERVER_NAME -v $PWD:/project/xiaoyun_api $IMAGE_NAME
echo "$SERVER_NAME container build end"
}
#检查程序是否在运行
is_exist(){
pid=`ps -ef|grep $JAR_PATH|grep -v grep|awk '{print $2}' `
#如果不存在返回1,存在返回0
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}
#启动方法
start(){
is_exist
if [ $? -eq "0" ]; then
echo "${SERVER_NAME} is already running. pid=${pid} ."
else
echo --------Starting application --------
nohup java -server -Xms512m -Xmx512m -XX:SurvivorRatio=4 -Xss256k -XX:PermSize=256m -XX:MaxPermSize=512m -XX:-DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -jar $JAR_PATH --spring.profiles.active=${profile:-dev} --server.port=${port:-8091} > start.log 2>&1 &
echo --------------Started!---------------
fi
}
#停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
echo -----------Application Stopped------------
else
echo "${JAR_PATH} is not running"
fi
}
#输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "${JAR_PATH} is running. Pid is ${pid}"
else
echo "${JAR_PATH} is NOT running."
fi
}
#重启
restart(){
stop
start
}
#mvn
pull(){
echo "----------git:find status---------"
git status
echo "----------git:pull new coads---------"
git pull origin develop
if [ $? -ne 0 ]; then
exit
fi
echo "----------mvn clean package -Dmaven.test.skip=true---------"
mvn clean package -Dmaven.test.skip=true
if [ $? -ne 0 ]; then
exit
fi
echo "----------Preparing start application ---------"
is_exist
if [ $? -eq "0" ]; then
restart
else
start
fi
}
#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"init")
init
;;
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
"pull")
pull
;;
*)
usage
;;
esac
sh脚本需要根据实际项目需求修改的地方
修改点 | 备注 |
---|---|
IMAGE_NAME | 镜像名(可以重复使用镜像) |
SERVER_NAME | 项目运行的容器名 |
JAR_PATH | mvn打包的jar路径 |
–spring.profiles.active=${profile:-dev} | -dev(默认的项目启动环境配置文件) |
–server.port=${port:-8091} | -8091(默认的项目启动端口) |
git pull origin develop | git拉取代码的默认分支 |
到此完毕!如有遇到问题,或者有更好的改进方案,方便回复讨论!!!