最近发现公司持续集成的构建方式(可视化配置出dockerfile)实在太不可控,于是自己写了一个dockerfile两步构建spring boot。
Dockerfile
废话少说,先上代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29# 基础镜像
FROM maven:3.5-jdk-8 AS build-env
# 镜像维护人员
MAINTAINER [email protected]
# 编译操作
COPY ./ /
WORKDIR /
RUN ./download.sh test && mvn clean package -DskipTests=true
# 运行镜像
FROM openjdk:8-jre
# 拷贝编译好的jar包放进运行镜像
COPY --from=build-env /target/wap-article-0.0.1-SNAPSHOT.jar /opt/app.jar
# 创建日志文件夹
WORKDIR /opt/logs
RUN mkdir -p /opt/logs/gc
# 安装dumb-init
RUN wget --no-check-certificate -O /usr/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64
RUN chmod +x /usr/bin/dumb-init
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# 读取配置文件,运行
EXPOSE 10020
CMD java -Dfile.encoding=utf-8 -Duser.timezone=GMT+08 -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} -Djson.view.enabled=${JSON_VIEW_ENABLED} -Denv=${APOLLO_ENV} ${OPTIONS} -jar /opt/app.jar $ARGS
两步构建
多步构建(Multi-stage build),这样就可以通过单个Dockerfile使用多个中间镜像用于构建、测试、发布,并且有效减小最终镜像的大小。一般在这上面能找到相关镜像hub.docker、github。
我们选取maven:3.5-jdk-8
作为编译镜像,编译完之后把打包好的jar包放到运行镜像openjdk:8-jre
中,这个运行的镜像包含了能够跑起来springboot jar包的最小环境(一般来说现在流行使用alpine的镜像)。1
2# docker images
wap-article latest a77312d39b1d 10 hours ago 295MB
现在的运行镜像和原来487MiB的镜像相比,压缩了将近一半。
优雅退出
进程的优雅退出(Gracefully Exiting) 对服务来说是一件很重要的事情,但是只要正确捕捉SIGTERM 等信号一般都不会有什么大问题,但是在容器中会出现一些不可预料的结果。
在一个容器启动的时候,CMD 或者 ENTRYPOINT 里定义的命令会作为容器的主进程(main process)启动,pid 为 1,一旦主进程退出了,容器也会被销毁,容器内其他进程会被 kernel 直接 kill。shell 来启动程序,有时候会是后台进程,如果 shell 退出会导致子进程全部退出,应该会是个大麻烦。
我们在docker中有时候会用shell来启动程序,但是shell不转发signals同时还不响应退出信号。因为 kernel 会为每个进程加上默认的 signal handler,例外的是 pid=1 的进程,被 kernel 当作一个 init 角色,不会给他加上默认的 handler,可如果在容器中启动 shell,占据了 pid=1 的位置,这个容器就无法正常退出了,只能等 docker 引擎在超时后强行杀死进程。信号并没有被很好的处理和传递,孤儿僵尸进程没有被正确的收割。
假如我们使用dumb-init作为预启动钩子,启动java程序,那作为容器中的主进程,当它在收到退出信号的时候,会将退出信号转发给进程组所有进程。1
2
3
4# 安装dumb-init
RUN wget --no-check-certificate -O /usr/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64
RUN chmod +x /usr/bin/dumb-init
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
我们可以看一下有没有使用dumb-init的区别:
1 | # ps -ef |
1 | # ps -ef |
可以发现上面的pid=1变成了dumb-init