本文基于springboot2.3.7进行demo开发,整体结构如下:
1.springboot配置
此demo进行多环境隔离,bootstrap.xml和application.xml配置一些不变的参数包括默认的profile,此参数可以通过启动jar包进行传入,后面会讲到
本文采用的是logback,命名为logback.xml后会,springboot会自动加载此文件,并替代其默认配置。由于代码是demo,内置tomcat的日志可以自行搜索 logback.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="/home/fpf/log" />
<!--控制台日志, 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--配置后,最新的日志是不带时间戳的-->
<file>${LOG_HOME}/file.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/file.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="service" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/service.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/service.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--此处会了单独讲service,controller的日志进行分开存储-->
<logger name="com.example.dockerone.controller" level="info">
<appender-ref ref="service"/>
</logger>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE"/>
</root>
</configuration>
2. 集成Docker
在华为云买个服务低配服务器,安装docker,执行docker images就不会报错,已经有镜像如下
接下来就讲述如何采用脚本的方式优雅发布demo,可分为如下几步:
- 编写Dockerfile
- 将编译生成的可执行jar和Dockerfile一起打包,后面传到服务器
- 启动(目录挂载和端口映射)(启动这里也可以使用脚本,本demo事手动的)
2.1 编写Dockerfile
Dockerfile语法可以自行百度,下面是本demo的Docfile,每一步的作用都有说明
# Docker image for springboot file run
# VERSION 0.0.1
# Author: ****
# 基础镜像使用java
FROM java:8
# VOLUME 指定了临时文件目录为/tmp。
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
# VOLUME 此处如果挂载目录,目录名称是随机的(一大串字符串),比如将宿主机某个目录挂载到容器的日志目录;可以在启动的时候指定挂载目录,
# 创建目录,-p作用是不存在就创建,作为后面工作目录使用
RUN mkdir -p /home/fpf/dockercontainer/
#COPY指令后面可以带多参数,此处的含义是将生成的jar和同一层start.sh文件拷贝刚创建的工作目录
#此处用变量,包名或者版本修改,Dockerfile不需要改
COPY ${project.artifactId}-${project.version}.jar start.sh /home/fpf/dockercontainer/
#同步宿主机和容器时间,我们日志挂载后,不同步的话,时间有问题
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
# 工作目录
WORKDIR /home/fpf/dockercontainer
#设置环境变量,此处的环境变量映射的就是applica.yml中配置的profile变量,在docker运行是可以#传入此参数
ENV PROFILES sit
#此指令在环境执行的时候就行 sh -c ./start.sh
ENTRYPOINT ["sh","-c","./start.sh"]
注意上面${project.artifactId}引用的变量就是pom文件中,下面会继续说明,此处有坑
另外上面提到start.sh,作为demo,里面就根据环境变量(docker启动时传入,然后会映射到对应的application-*.xml文件)进行启动
#!/bin/bash
#脚本就直接启动jar包,此处可以使用nohup进行启动,在容器化的话,会不在控制台打印日志,没其他差别
java -jar ${project.artifactId}-${project.version}.jar --spring.profiles.active=${PROFILES}
2.2 打包Dockerfile和jar包
本demo中,采用assembly插件进行打包,将两者打成zip包,解压后如下:
想完成这种打包,先在pom中增加assembly插件
这里配置没啥,主要看assembly.xml这个文件,其中注意一些坑,xml文件如下 在2.1中 Dockerfile和start.sh中引用的pom变量${project.artifactId}会被替换,就依赖这个插件
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>package</id>
<formats>
<format>zip</format>
</formats>
<!--是否需要最外层目录,比如zip包名叫aaa.zip,如果置为true,那么解压出来的最上层目录是aaa/*,为false则为/*-->
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>config</directory>
<outputDirectory></outputDirectory>
<!--替换文件中的变量,**特别注意,**如果此处为false,${project.artifactId}就不会被替换-->
<filtered>true</filtered>
<includes>
<include>*.sh</include>
<include>Dockerfile</include>
</includes>
<!--sh文件要有可执行权限-->
<fileMode>0755</fileMode>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>
3. 启动Docker
先把生成的zip传到远程服务器,**注意**,本地编译代码时,需要看下start.sh文件格式,必须是unix格式,idea的右下角
上传后,解压如下:
下面才凯斯构造镜像和启动容器
a. 开始构造镜像,镜像名称demo:v1 docker build -t demo:1 .
b. 启动容器
docker run -it -p 8086:8085 -e PROFILES=online -v /home/fpf/docker/log:/home/fpf/log demo:v1
此指令含义 启动名为demo:v1的镜像,-it代表后台运行,-e代表传入到Docfile的参数(此参数不传,默认使用Dockerfile定义的,即sit),-v代表目录挂载,本demo是把宿主机的目录挂载到容器内,即在宿主机/home/fpf/docker/log目录能看到容器内/home/fpf/log的日志
启动成功
本地postman请求,