应用的容器化-docker打包spring boot镜像

应用容器化

容器占用资源少、部署快,每个应用可以被打包成一个容器镜像,每个应用与容器间成一对一关系也使容器有更大优势,使用容器可以在build或release 的阶段,为应用创建容器镜像,因为每个应用不需要与其余的应用堆栈组合,也不依赖于生产环境基础结构,这使得从研发到测试、生产能提供一致环境。类似地,容器比虚机轻量、更“透明”,这更便于监控和管理。

制作自己的镜像

选择适合你的方式来生成镜像

  1. 通过 Dockerfile 来自动编译生成镜像,实现构建镜像的需求。
    多用于用户交互较少的时刻,比如软件部署时无需输入任何命令的应用。

  2. 通过容器内操作,并进行 Commit 来实现打包生成镜像。
    用于用户交互较多、安装过程中配置较多的应用。

如何拆分现有的应用,来实现打包。Docker 容器化的应用应当是功能最小化的应用。这里的拆分并不是指将每个软件拆分成为一个容器,而是强调实现功能最小化。

一般来说,你可以将应用拆分成为 2-3 个容器, 2 个容器的情况下,是数据存储和应用程序各自一个容器。对于部分应用,可能还有缓存系统,同样也要单独放置在一个容器内。这样的结构下,在你需要扩容时,就比较容易,根据你的需要,可以随时扩容缓存系统或应用程序。

Dockerfile 打包

Dockerfile 就是声明应用环境和应用本身。Docker 需要更专注于它本身作为容器的技术。完成无状态化应用和写完 Dockerfile 之后,这个程序就可以被打报成 Docker image 了,放到一个 Docker Host 上运行起来就得到了无状态的应用容器,也就完成了把应用装容器的过程。

docker打包springboot项目成镜像

  1. 相关参考
    将springboot jar应用打包成镜像并在docker运行成容器
    参考URL: https://blog.****.net/keepd/article/details/80569797
    docker打包springboot项目成镜像
    参考URL: https://blog.****.net/junmoxi/article/details/80007878
    使用 Docker 部署 Springboot 应用
    参考: https://blog.****.net/chinrui/article/details/79721366

  2. 在Spring Boot项目中创建一个Dockerfile
    文件格式,官方参考URL:
    https://docs.docker.com/engine/reference/builder/#usage

FROM openjdk:8-alpine
VOLUME /tmp
ARG JAR_FILE=target/gs-spring-boot-docker-0.1.0.jar
ADD ${JAR_FILE} app.jar
ENV JAVA_OPTS=""
ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar

应用程序的jar文件(文件名为app.jar)被添加到容器中,执行 ENTRYPOINT

我们增加了一个VOLUME指向”/tmp”,因为那是Spring Boot应用程序为Tomcat创建的默认工作目录。作用是在你的主机”/var/lib/docker”目录下创建一个临时的文件,并且链接到容器中的”/tmp”目录。对于简单程序这一步是可选的,但是对于其他想要真实写入文件系统的Spring Boot应用程序又是必选的。

我们增加了一个指向”/dev/urandom”的Tomcat系统属性来缩小Tomcat的启动时间。

可以看到在左上叫有个运行的标记,很对,这个就是用来在IDEA构建jar包到镜像,然后放到Docker中运行的按钮。
应用的容器化-docker打包spring boot镜像
如上图 我们只需要创建对应的Dockerfile文件,IDEA就会提醒上图中的选择供我们操作,我们选择编辑xxx/Dockerfile,填写image tag信息,然后run,然后就如图中下方生成了镜像。

  1. 使用docker-maven-plugin插件,在pom.xml中配置插件,完成自动化的镜像生成上线。
    TODO 未测试
    docker-maven-plugin 介绍
    在我们持续集成过程中,项目工程一般使用 Maven 编译打包,然后生成镜像,通过镜像上线,能够大大提供上线效率,同时能够快速动态扩容,快速回滚,着实很方便。docker-maven-plugin 插件就是为了帮助我们在Maven工程中,通过简单的配置,自动生成镜像并推送到仓库中。

应用的容器化-docker打包spring boot镜像
然后在ternimal中运行 mvn clean package -DskipTests=true docker:build命令,打包项目并构建镜像,命令执行完毕可以看到

IDEA使用Docker插件

【windows 10使用docker运行spring boot】
参考URL: https://www.jianshu.com/p/d7f3215df1b4
【IntelliJ IDEA安装docker插件】可直接参考
参考URL: https://blog.****.net/sealir/article/details/81200662
IDEA中配置及使用Docker
参考URL: https://blog.****.net/jacksonary/article/details/78974344

IDEA Docker插件安装

  1. 在IntelliJ IDEA中安装Docker integration插件
    This plugin lets you download and build Docker images, create and start Docker containers, and carry out other related tasks.
    这个插件可以下载构建Docker镜像,创建和启动容器以及执行其它相关任务。

  2. 安装插件后,需要配置连接docker服务端
    从File->Settings->Build,Execution,Deployment->Docker打开配置界面。
    点击+号添加一个docker配置,输入Name和Engine API URL,URL是docker服务地址,需要docker开启远程连接功能

  3. 添加Docker的菜单窗口
    IDEA顶部工具栏的View–>Tool Windows–>Docker
    完成点击左下角的小窗口图标放大即可看到Docker的菜单工具栏

插件的基本操作

应用的容器化-docker打包spring boot镜像
主要包含了containers和images,里面是一些已经存在的容器和镜像,下面是一些基本操作:

  • 拉取镜像
    如果要拉取镜像,直接在images上右击pull images然后填写Repository即可。
    例如,填写如下 地址,即下载nginx
    hub.c.163.com/library/nginx
  • 创建镜像容器运行
    在需要跑的镜像上右击创建容器,这个时候也可看到镜像的ID和Tag,如下图
    应用的容器化-docker打包spring boot镜像

配置相应的端口绑定等信息即可启动即可,在创建的容器上右击选择inspect可以查看相应容器的详细信息,也可以启动或者停止容器。

至此,我们已经可以使用docker命令,直接把我们生成的spring boot手动利用docker命令打包到docker容器中,并直接运行。

关于2375端口

为了实现集群管理,Docker提供了远程管理接口。Docker Daemon作为守护进程,运行在后台,可以执行发送到管理接口上的Docker命令。

**启动Docker Daemon时,加入-H 0.0.0.0:2375,Docker Daemon就可以接收远端的Docker Client发送的指令。**注意,Docker是把2375端口作为非加密端口暴露出来,一般是用在测试环境中。此时,没有任何加密和认证过程,只要知道Docker主机的IP,任何人都可以管理这台主机上的容器和镜像。

通过远程管理的方式,向Docker主机发送命令。

docker -H 192.168.0.10:2375 info

Linux下开启2375端口

docker 开启2375端口,提供外部访问docker
参考:https://blog.****.net/onlyshenmin/article/details/81069047
docker开启远程访问
参考URL: https://blog.****.net/z_qifa/article/details/74202785
Docker打开TCP管理端口
参考URL: https://blog.****.net/onlyshenmin/article/details/81069047
Docker本身提供了加密的远程管理端口2376,配合CA证书,就能提供TLS连接了。
参考URL: https://blog.****.net/ghostcloud2016/article/details/51539837

Windows开启2375端口

在windows下设置docker直接使用gui界面来设置就可以了
右键启动栏的docker小图标,点击settings>general
设置启用localhost的2375端口来运行docker
Expose daemon on tcp://localhost:2375 without TLS
应用的容器化-docker打包spring boot镜像

基础镜像 Commit 生成镜像

除了通过 Dockerfile 来打包生成镜像外,也可以通过 Docker Commit 来生成镜像。通过 Commit 打包的镜像多是由于应用本身在部署时有大量的交互内容,无法通过命令来指定。

容器ID 35f1c2ae1f7e 则
将容器打包成镜像执行命令 docker commit 35f1c2ae1f7e mynewimage就将容器35f1c2ae1f7e打包为新的镜像mynewimage了

Docker镜像大小问题

Docker容器镜像瘦身的三种构建方法整理
https://blog.****.net/limingcai168/article/details/82347485
三个技巧,将Docker镜像体积减小90%
https://blog.****.net/bbwangj/article/details/82178774

基本的构建方式,就是依赖各种资源的传统方式构建,分层较多,一个容器至少在600M左右,但是各方的依赖工具都很全面,对于调试,以及内部操作都 相对简单方便。

Docker 容器应该只包含一个进程以及用于运行这个进程所需的最少的文件,你不需要整个操作系统。
“distroless”镜像只包含应用程序及其运行时依赖项,不包含程序包管理器、shell 以及在标准 Linux 发行版中可以找到的任何其他程序。
这个未包含额外程序的镜像会多小呢?

$ docker images | grep node-distroless
node-distroless 7b4db3b7f1e5 76.7MB
仅仅76.76MB!

比前一个镜像少了600MB!

需要注意的是,我们不应该在生产环境中附加到容器中进行调试,而应依靠正确的日志和监控。

如果我们既希望能调试,又关心尺寸大小,又该怎么办?
使用Alpine作为更小的基础镜像

一个基于musl libc[3]和busybox[4]、面向安全的轻量级Linux发行版。

换言之,它是一个尺寸更小、更安全的Linux发行版。

Alpine基础镜像

Docker系列之(三):Docker微容器Alpine Linux
参考URL: https://www.cnblogs.com/ee900222/p/docker_3.html

Alpine基础镜像是基于muslc的,这是一个C的替代标准库。

但是,多数Linux发行版,比如Ubuntu、Debian及CentOS都是基于glibc的。这两个库照理应该实现了相同的接口。

不过,它们的目标不同:

  • glibc最常用,速度更快
  • muslc占用空间更少,以安全为核心

在编译应用程序时,多数情况下是使用某个libc来编译的。如果想在其他libc中使用,只能重新编译。

Alpine Linux是一个面向安全的轻型的Linux发行版。
Alpine Linux采用了 musl libc 和 busybox以减小系统的体积和运行时资源消耗。
在保持瘦身的同时,Alpine Linux还提供了自己的包管理工具apk。

关键的是,相比于其他Linux的Docker镜像,它的容量非常小,仅仅只有5MB

问题点:

  1. Alpine Linux使用了musl,可能和其他Linux发行版使用的glibc实现会有些不同。

  2. musl实现的DNS服务不会使用resolv.conf文件中的search和domain两个配置,通过DNS来进行服务发现时需要注意。

原生基础镜像非常适合用于测试和开发。
它的尺寸比较大,不过用起来就像你主机上安装的Ubuntu一样。并且,你能访问该操作系统里有的所有二进制程序。

Alpine制作JDK8镜像

  1. 自己制作
    Alpine 3.6 OpenJDK 8 Bug
    参考URL: https://mritd.me/2017/09/27/alpine-3.6-openjdk-8-bug/
    (未测试)
FROM alpine:edge

LABEL maintainer="mritd <[email protected]>"

ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
ENV JAVA_VERSION 8u144
ENV JAVA_ALPINE_VERSION 8.144.01-r0

RUN apk add --update bash curl tar wget ca-certificates unzip \
        openjdk8=${JAVA_ALPINE_VERSION} font-adobe-100dpi ttf-dejavu fontconfig \
    && rm -rf /var/cache/apk/* \

CMD ["bash"]
  1. 使用别人已经制作好的镜像 alpine jdk8
    进入阿里云镜像中心https://dev.aliyun.com/search.html
    找到JAVA相关,点击进去
    This image is officially deprecated in favor of the openjdk image, and will receive no further updates after 2016-12-31 (Dec 31, 2016). Please adjust your usage accordingly.

此映像被正式弃用,而支持openjdk映像,并且在2016-12-31(2016年12月31日)之后将不会接收到进一步的更新。请相应地调整您的使用情况。

说明官方新的java jdk镜像,使用的是openjdk这个关键字镜像。
继续点击进去,进入到openjdk镜像官网
https://hub.docker.com/_/openjdk/?spm=5176.1972344.1.5.cNQc3F
官方对openJdk做了比较详细的说明。
Image Variants

openjdk:<version>
openjdk:<version>-alpine
openjdk:<version>-slim
  1. 使用默认的镜像。如果你不确认你使用哪个,version后面不加,应该就满足你。它被设计为抛弃式throw away容器(挂载源代码并启动容器以启动应用程序),又可以用作构建其他映像的基础。
    你也可以加标签tags jessie, sid, or stretch 。这些是Debian发布的版本,用于声明你用哪个基础镜像。

  2. -alpine这个镜像是基于流行的 Alpine Linux项目。使用官方镜像只有5MB大小。
    当希望镜像尽可能小时,强烈推荐这个镜像。
    为了减少镜像大小,不推荐添加额外工具(如git、bash)包含进镜像。使用这个镜像作为基础,在Docckerfile中添加所需的内容。

  3. 这个映像安装了OpenJDK -headless的包,因此缺少了许多与UI相关的Java库和默认标签中包含的一些普通包。它只包含运行Java所需的最小包。百度关键字 “java headless”

version 就是你的jdk的版本,8就填8。
根据上面信息,我们使用openjdk-8-jdk-alpine的话,docker 命令应该如下:

docker pull openjdk:8-alpine

这个应该是官方 最精简的 jdk8 docker镜像。

openjdk-8-jdk-alpine容器问题整理

  1. Docker openjdk-8-jdk-alpine 容器时间与jdk时区不同修改方法
    参考URL: https://www.cnblogs.com/solooo/p/7832117.html
    未验证测试
    TODO 遇到再整理。

基于已有镜像,dockr命令直接打包jar文件到容器中并运行

官方参考URL: https://hub.docker.com/_/openjdk/?spm=5176.1972344.1.5.cNQc3F
SpringBoot | 第十四章:基于Docker的简单部署
https://blog.****.net/qq_42803327/article/details/82150809

基于有时候没有开启远程服务,也可把jar上传至服务器,然后编写脚本进行运行,这里简单示例下,正常部署时,正常是通过jenkins打包,然后编写打包后事件,运行脚本即可。

-v 你宿主机的目录/文件
windows环境下,你使用绝对路径,系统会提醒,讲你用到的盘符共享。(其实就是windows共享给Docker宿主Linux,Docker才能访问,copy它需要的文件给容器中。)
如果你运行Docker在linux主机下,应该不存在此问题。
应用的容器化-docker打包spring boot镜像
-w 你容器主机目录/文件(也可以-v中用冒号直接隔开即可)
–name 指定新生成的容器名

docker run -d -p 1234:11111 -v D:\bat\eurekaserver-0.0.1-SNAPSHOT.jar:/usr/eurekaserver-0.0.1-SNAPSHOT.jar --name SpringbootByJar3 openjdk:8-alpine java -server -jar  -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1 /usr/eurekaserver-0.0.1-SNAPSHOT.jar --spring.config.location=eurekaserver.properties

如果需要copy整个目录的文件到容器中呢?
如下 -v 填目录 冒号后面是目标目录,测试通过。

docker run -d -p 1234:11111 -v D:\bat\:/usr/test --name SpringbootByJar3 openjdk:8-alpine java -server -jar  -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1 /usr/test/eurekaserver-0.0.1-SNAPSHOT.jar --spring.config.location=eurekaserver.properties

其实就是把jar拷贝从宿主中拷贝到容器中,然后运行java命令进行启动。和原来的运行jar方式是一样的。

遇到问题整理

  1. -Dcom.sun.management.jmxremote.port=1332 java启动参数,有这个启动不起来。 提醒1332已被使用。
    暂时删除该参数。
  2. 文件传成功到容器里面了,但是java程序没有启动起来,登进容器可以手动启起来? Error: Unable to access jarfile eurekaserver-0.0.1-SNAPSHOT.jar
    IDEA插件断开连接docker demon,重新连接,发现和之前不一样,应该是成功了,spring boot启动有个过程。注意查看容器日志即可。
    应用的容器化-docker打包spring boot镜像