如何把springcloud运行在docker中

目标(以下镜像均在生产使用)

  • 自定义Dockerfile构建一个生产可用的jre base image
  • 配置maven docker 打包发布插件完成从源码的打包fat jar-> build docker image -> push image
  • 使用docker运行打包完成的镜像

构建一个jre镜像

  • 首先我们对这个镜像有如下要求
  1. 镜像比如要足够小(必须在100M以内)
  2. 字符集必须要支持中文,不然就乱码了
  3. 时区必须要是UTC+8
  4. 必须要有字体支持,不然excel导出会抛出错误
  5. 还得支持下imagemagick。
  6. 使我们运行在容器中的java进程PID!=1,
    如果Pid=1会有很多问题(如jmap,jstat…等工具无法使用,你可以从这里了解更多 jmap not happy on alpine )
    这里我们使用Tini来完成这个工作,关于Tini你可以从这里了解更多 What is advantage of Tini?
  • 基于以上要求我们构建出一下镜像
  • 首先使用apline作为基础镜像足够小只有5M
  • 由于alpine自带支持中文的字符集,这里我们只需要将LANG设置为C.UTF-8即可完美的支持中文。
  • 国内软件源首选阿里云啦,顺道配置一下阿里云的镜像源,加速我们的镜像构建速度
  • 配置UTC+8时区需要安装tzdata,安装完成之后配置一下即可。
  • 目前alpine携带JDK版本为1.80_171
  • 使用tini 包装java进程

Jre base image

  • 镜像全部使用github+dockerhub的autobiuld自动构建
  • 完整的镜像如下图所示

  • 由于busybox的ps 导致依赖组件有点小瑕疵所以需要update一下ps的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ps: unrecognized option: p
    BusyBox v1.28.4 (2018-05-30 10:45:57 UTC) multi-call binary.

    Usage: ps [-o COL1,COL2=HEADER]

    Show list of processes

    -o COL1,COL2=HEADER Select columns for display
    The target pid (11) does not exist!
  • 执行下面命令修复

1
apk add --update procps
  • 如果需要用到telnet 则需要安装一下 busybox-extras
1
apk add busybox-extras
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 如不想自己构建 可使用我构建好的
# docker pull freemanliu/openjre
FROM alpine:3.8
MAINTAINER qingmu 247687009@qq.com
ENV LANG=C.UTF-8 \
JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre \
PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin \
TZ=Asia/Shanghai
RUN echo "" > /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/main/" >> /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories \
&& apk update && apk add --no-cache openjdk8-jre ca-certificates tzdata tini \
&& apk add --update procps \
&& rm -rf /var/cache/apk/* \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
  • 如:导出excel的服务,进行excel导出时,上面最基础的jre镜像会抛出异常,这时需要安装一下字体

Jre image include font

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 如不想自己构建 可使用我构建好的
# dokcer pull freemanliu/openjre:1.8.0_171_font
FROM alpine:3.8
MAINTAINER qingmu 247687009@qq.com
ENV LANG=C.UTF-8 \
JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre \
PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin \
TZ=Asia/Shanghai
RUN echo "" > /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/main/" >> /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories \
&& apk update && apk add --no-cache openjdk8-jre ca-certificates tzdata ttf-dejavu tini \
&& rm -rf /var/cache/apk/* \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

Jre image include font and imagemagick

  • 包含imagegick的镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
# freemanliu/openjre:1.8.0_171_im
FROM alpine:3.8
MAINTAINER qingmu 247687009@qq.com
ENV LANG=C.UTF-8 \
JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre \
PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin \
TZ=Asia/Shanghai
RUN echo "" > /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/main/" >> /etc/apk/repositories \
&& echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories \
&& apk update && apk add --no-cache openjdk8-jre ca-certificates tzdata ttf-dejavu imagemagick font-adobe-100dpi fontconfig tini \
&& rm -rf /var/cache/apk/* \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
  • 我们构建除了完全满足我们需求的镜像,是不是美滋滋啊。接下来就配置maven插件来帮助我们构建application的image啦。

配置maven插件

  • 注意我们使用tini,所以在编写entryPoint时,需要将tini写在最前面
1
["/sbin/tini","java","deme.jar"]
  • 对于maven插件我们有一下需求
  1. 插件配置足够简单,清晰明了
  2. 有了插件之后不再需要进行dockerfile的编写,dockerfile拜拜吧~
  3. 插件能帮我自动push到远程仓库
  • 修改root pom.xml

  • 新增如下镜像信息属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <properties>
    <docker.jre>freemanliu/openjre</docker.jre>
    <docker.jre.version>1.8.0_171</docker.jre.version>
    <docker.jre.im.version>1.8.0_171_im</docker.jre.im.version>
    <docker.jre.font.version>1.8.0_171_font</docker.jre.font.version>
    <docker.application.version>1.0.0</docker.application.version>
    <docker.registry.name>projectName</docker.registry.name>
    <docker.repository>hub.xxxx.com</docker.repository> <!-- 你的私服地址-->
    </properties>
  • 在插件管理中添加docker-maven-plugin 方便统一管理版本

  • 目前最新版是1.1.1
1
2
3
4
5
6
7
8
9
10
11
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.1.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
  • 配置具体application的pom.xml
  • 首先配置下私服的账号信息
1
2
3
4
5
6
7
8
9
10
11
12
13
vim ~/.m2/settings.xml
# 找到servers 标签添加如下如下信息
<servers>
<server>
# <id>hub-my</id> # id将在后面插件中用到
<username>你的私服的username</username>
<password>你私服的密码</password>
<configuration>
<email>随便填一个email</email>
</configuration>
</server>
... 你的其他server配置
</servers>
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 <!-- 配置mainClass -->
<properties>
<start-class>io.qingmu.eureka.app.EurekaServerApplication</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<!-- 激活fat jar 打包插件 -->
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 新增profile 添加docker打包插件-->
<profiles>
<profile>
<id>prod</id>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<imageName>
${docker.repository}/${docker.registry.name}/${project.artifactId}:${docker.application.version}
</imageName>
<newName>
${docker.repository}/${docker.registry.name}/${project.artifactId}:${docker.application.version}
</newName>
<registryUrl>${docker.repository}</registryUrl>
<workdir>/work</workdir>
<rm>true</rm>
<baseImage>${docker.jre}:${docker.jre.version}</baseImage>
<entryPoint>["/sbin/tini","java","-Dspring.profiles.active=prod","-Dspring.cloud.config.profile=prod","-DZIPKIN_SERVER=http://zipkin-server:9411","-DEUREKA_SERVER=http://root:YaIksCbBZBVR5nuGC9loUQ1zGaIQ1Y@eureka1:8761/eureka/,http://root:YaIksCbBZBVR5nuGC9loUQ1zGaIQ1Y@eureka2:8761/eureka/,http://root:YaIksCbBZBVR5nuGC9loUQ1zGaIQ1Y@eureka3:8761/eureka/","-XX:+UseG1GC","-XX:+AggressiveOpts","-XX:+UseFastAccessorMethods","-XX:+UseStringDeduplication","-XX:+UseCompressedOops","-XX:+OptimizeStringConcat","-XX:CICompilerCount=8", "-jar", "${project.build.finalName}.jar"]</entryPoint>
<pushImage>true</pushImage>
<resources>
<resource>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<!-- 配置于~/.m2/setting.xml 的server的id-->
<serverId>hub-my</serverId>
<!-- 你的私服地址 -->
<registryUrl>hub.xxxx.com</registryUrl>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
1
2
#执行构建 
mvn clean pacakge -Pprod