「Spring」Boot Docker 认证指南(上)
许多人使用容器来包装他们的 Spring Boot 应用程序,而构建容器并不是一件简单的事情。这是针对 Spring Boot 应用程序开发人员的指南,容器对于开发人员来说并不总是一个好的抽象。它们迫使你去了解和思考低层次的问题。但是,有时可能会要求您创建或使用容器,因此了解构建块是值得的。在本指南中,我们旨在向您展示如果您面临需要创建自己的容器的前景,您可以做出的一些选择。
我们假设您知道如何创建和构建基本的 Spring Boot 应用程序。如果没有,请转到入门指南之一 ——例如,关于构建REST 服务的指南。从那里复制代码并练习本指南中包含的一些想法。
还有一个关于Docker的入门指南,这也是一个很好的起点,但它没有涵盖我们在此处介绍的选择范围或详细介绍它们。
一个基本的 Dockerfile
Spring Boot 应用程序很容易转换为可执行的 JAR 文件。所有的入门指南都是这样做的,你从Spring Initializr下载的每个应用程序都有一个构建步骤来创建一个可执行的 JAR。使用 Maven,你运行./mvnw install,使用 Gradle,你运行./gradlew build。运行该 JAR 的基本 Dockerfile 将如下所示,位于项目的顶层:
Dockerfile:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]复制
JAR_FILE您可以作为命令的一部分传入docker(Maven 和 Gradle 不同)。对于 Maven,以下命令有效:
docker build --build-arg JAR_FILE=target/*.jar -t myorg/myapp .复制
对于 Gradle,以下命令有效:
docker build --build-arg JAR_FILE=build/libs/*.jar -t myorg/myapp .复制
一旦你选择了一个构建系统,你就不需要ARG. 您可以对 JAR 位置进行硬编码。对于 Maven,如下所示:
Dockerfile:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]复制
然后我们可以使用以下命令构建镜像:
docker build -t myorg/myapp .复制
然后我们可以通过运行以下命令来运行它:
docker run -p 8080:8080 myorg/myapp复制
输出类似于以下示例输出:
. ____ _ __ _ _
/\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | _ | _| | _ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
Nov 06, 2018 2:45:16 PM org.springframework.boot.StartupInfoLogger logStarting
INFO: Starting Application v0.1.0 on b8469cdc9b87 with PID 1 (/app.jar started by root in /)
Nov 06, 2018 2:45:16 PM org.springframework.boot.SpringApplication logStartupProfileInfo
...复制
如果你想在镜像内部四处寻找,你可以通过运行以下命令在其中打开一个 shell(注意基础镜像没有bash):
docker run -ti --entrypoint /bin/sh myorg/myapp复制
输出类似于以下示例输出:
/ # ls
app.jar dev home media proc run srv tmp var
bin etc lib mnt root sbin sys usr
/ #
我们在示例中使用的 alpine 基础容器没有bash,所以这是一个ashshell。它具有一些但不是全部的特性bash。
如果你有一个正在运行的容器并且你想查看它,你可以通过运行docker exec:
docker run --name myapp -ti --entrypoint /bin/sh myorg/myapp
docker exec -ti myapp /bin/sh
/ #复制
传递给命令myapp的位置在哪里。如果您没有使用,docker 会分配一个助记名称,您可以从. 您还可以使用容器的 SHA 标识符而不是名称。SHA 标识符在输出中也可见。--namedocker run--namedocker psdocker ps
入口点
使用Dockerfile的exec 形式ENTRYPOINT,以便没有外壳包装 Java 进程。优点是java进程响应KILL发送到容器的信号。实际上,这意味着(例如)如果您docker run在本地使用图像,则可以使用CTRL-C. 如果命令行有点长,您可以COPY在运行之前将其提取到 shell 脚本中并放入映像中。以下示例显示了如何执行此操作:
Dockerfile:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY run.sh .
COPY target/*.jar app.jar
ENTRYPOINT ["run.sh"]复制
请记住使用exec java …启动 java 进程(以便它可以处理KILL信号):
run.sh:
#!/bin/sh
exec java -jar /app.jar复制
入口点的另一个有趣方面是您是否可以在运行时将环境变量注入 Java 进程。例如,假设您想要在运行时添加 Java 命令行选项。您可以尝试这样做:
Dockerfile:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","${JAVA_OPTS}","-jar","/app.jar"]复制
然后您可以尝试以下命令:
docker build -t myorg/myapp .
docker run -p 9000:9000 -e JAVA_OPTS=-Dserver.port=9000 myorg/myapp复制
这失败了,因为${}替换需要一个外壳。exec 表单不使用 shell 来启动进程,因此不应用选项。您可以通过将入口点移动到脚本(如run.sh前面显示的示例)或在入口点显式创建 shell 来解决此问题。以下示例显示了如何在入口点中创建 shell:
Dockerfile:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]复制
然后,您可以通过运行以下命令来启动此应用程序:
docker run -p 8080:8080 -e "JAVA_OPTS=-Ddebug -Xmx128m" myorg/myapp复制
该命令产生类似于以下的输出:
. ____ _ __ _ _
/\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | _ | _| | _ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.0.RELEASE)
...
2019-10-29 09:12:12.169 DEBUG 1 --- [ main] ConditionEvaluationReportLoggingListener :
============================
CONDITIONS EVALUATION REPORT
============================
...复制
(前面的输出显示了 Spring BootDEBUG生成的完整输出的一部分。)-Ddebug。
将 anENTRYPOINT与显式 shell 一起使用(如前面的示例所做的那样)意味着您可以将环境变量传递给 Java 命令。但是,到目前为止,您还不能为 Spring Boot 应用程序提供命令行参数。以下命令不会在端口 9000 上运行应用程序:
docker run -p 9000:9000 myorg/myapp --server.port=9000复制
该命令产生以下输出,将端口显示为 8080 而不是 9000:
. ____ _ __ _ _
/\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | _ | _| | _ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.0.RELEASE)
...
2019-10-29 09:20:19.718 INFO 1 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080复制
它不起作用,因为 docker 命令(该--server.port=9000部分)被传递到入口点 (