1.Docker镜像使用确定性的标签
2.在Java镜像中仅安装需要的内容
3.查找并修复Java镜像中的安全漏洞
4.使用多阶段构建Java镜像
5.不要以root用户身份运行Java应用程序
6.Java应用程序不要使用PID为1的进程
7.优雅下线Java应用程序
8.使用 .dockerignore文件
9.确保Java版本支持容器
10.谨慎使用容器自动化生成工具
构建一个简单的Java容器镜像
让我们从简单的Dockerfile开始,在构建Java容器时,我们经常会有如下类似的内容:
FROM maven
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN mvn clean install
CMD "mvn" "exec:java"
Copy that to a file named Dockerfile, then build and run it.
$ docker build . -t java-application
$ docker run -p 8080:8080 java-application
这很简单,而且有效。但是,此镜像充满错误。
我们不仅应该了解如何正确使用Maven,而且还应避免像上述示例那样构建Java容器。
下面,让我们开始逐步改进这个Dockerfile,使你的Java应用程序生成高效,安全的Docker镜像。
Docker镜像使用确定性的标签
当使用Maven构建Java容器镜像时,我们首先需要基于Maven镜像。但是,你知道使用Maven基本镜像时实际上引入了哪些内容吗?
当你使用下面的代码行构建镜像时,你将获得该Maven镜像的最新版本:
FROM maven
这似乎是一个有趣的功能,但是这种采用Maven默认镜像的策略可能存在一些潜在问题:
你的Docker构建不是幂等的。这意味着每次构建的结果可能会完全不同,今天的最新镜像可能不同于明天或下周的最新镜像,导致你的应用程序的字节码也是不同的,并且可能发生意外。因此,构建镜像时,我们希望具有可复制的确定性行为。
Maven Docker镜像是基于完整的操作系统镜像。这样会导致许多其他二进制文件出现在最终的生产镜像中,但是运行你的Java应用程序不需要很多这些二进制文件。因此,将它们作为Java容器镜像的一部分存在一些缺点:
1.镜像体积变大,导致更长的下载和构建时间。
2.额外的二进制文件可能会引入安全漏洞。
如何解决呐?
使用适合你需求的最小基础镜像 考虑一下-你是否需要一个完整的操作系统(包括所有额外的二进制文件)来运行你的程序?如果没有,也许基于alpine镜像或Debian的镜像会更好。
使用特定的镜像 如果使用特定的镜像,则已经可以控制和预测某些行为。如果我使用maven:3.6.3-jdk-11-slim镜像,则已经确定我正在使用JDK 11和Maven 3.6.3。JDK和Maven的更新,将不再影响Java容器的行为。为了更加精确,你也可以使用镜像的SHA256哈希值。使用哈希将确保你每次构建镜像时都使用完全相同的基础镜像。