Docker 使用簡單的 “Dockerfile” 檔案格式來指定映象的“層”。在您的 Spring Boot 專案中建立以下 Dockerfile:
示例 1. Dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
如果您使用 Gradle,您可以使用以下命令執行它
docker build --build-arg JAR_FILE=build/libs/\*.jar -t springio/gs-spring-boot-docker .
docker build -t springio/gs-spring-boot-docker .
此命令將構建一個映象,並將其標記為 springio/gs-spring-boot-docker。
這個 Dockerfile 非常簡單,但它是執行一個簡陋的 Spring Boot 應用程式所需的一切:只有 Java 和一個 JAR 檔案。構建會建立一個 spring 使用者和 spring 組來執行應用程式。然後,它將專案 JAR 檔案(透過 COPY 命令)複製到容器中作為 app.jar,並在 ENTRYPOINT 中執行。使用 Dockerfile ENTRYPOINT 的陣列形式是為了避免 shell 包裝 Java 程序。關於 Docker 的 專題指南 更詳細地介紹了這個主題。
示例 2. Dockerfile
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
構建和執行應用程式時,您可以在應用程式啟動日誌中看到使用者名稱。
docker build -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker
注意第一個 INFO 日誌條目中的 started by。
:: Spring Boot :: (v2.2.1.RELEASE)
2020-04-23 07:29:41.729 INFO 1 --- [ main] hello.Application : Starting Application on b94c86e91cf9 with PID 1 (/app started by spring in /)
...
此外,Spring Boot 的 fat JAR 檔案在依賴項和應用程式資源之間有清晰的分離,我們可以利用這一點來提高效能。關鍵是在容器檔案系統上建立分層。這些層在構建時和執行時(在大多數執行時環境中)都會被快取,因此我們希望最常更改的資源(通常是應用程式本身的類和靜態資源)在更慢更改的資源 **之後** 進行分層。因此,我們使用了稍有不同的 Dockerfile 實現。
示例 3. Dockerfile
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
這個 Dockerfile 有一個 DEPENDENCY 引數,指向一個我們解壓了 fat JAR 的目錄。要使用 Gradle 的 DEPENDENCY 引數,請執行以下命令:
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
要使用 Maven 的 DEPENDENCY 引數,請執行以下命令:
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
如果我們做得正確,它已經包含了一個 BOOT-INF/lib 目錄,其中包含依賴項 JAR,以及一個 BOOT-INF/classes 目錄,其中包含應用程式類。請注意,我們使用了應用程式自己的主類:hello.Application。(這比使用 fat JAR 啟動器的間接方式更快。)
|
解壓 JAR 檔案可能導致類路徑順序在 執行時發生變化。一個行為良好、編寫良好的應用程式應該不關心這一點,但如果依賴項管理不當,您可能會看到行為上的變化。 |
|
如果您使用 boot2docker,您需要 **首先** 執行它,然後再進行任何 Docker 命令列或構建工具的操作(它會執行一個守護程序,在虛擬機器中為您處理工作)。 |
從 Gradle 構建中,您需要在 Docker 命令列中新增顯式的構建引數:
docker build --build-arg DEPENDENCY=build/dependency -t springio/gs-spring-boot-docker .
要在 Maven 中構建映象,您可以使用更簡單的 Docker 命令列:
docker build -t springio/gs-spring-boot-docker .
|
如果您只使用 Gradle,您可以更改 Dockerfile,使 DEPENDENCY 的預設值與解壓後歸檔的位置匹配。 |
與其使用 Docker 命令列構建,不如使用構建外掛。Spring Boot 支援使用其自身的構建外掛透過 Maven 或 Gradle 構建容器。Google 還有一個名為 Jib 的開源工具,它提供了 Maven 和 Gradle 外掛。這種方法的優點可能在於您不需要 Dockerfile。您可以使用與 docker build 相同的標準容器格式來構建映象。此外,它可以在未安裝 Docker 的環境(在構建伺服器中很常見)中工作。
|
預設情況下,由預設構建包生成的映象不會以 root 使用者身份執行您的應用程式。請查閱 Gradle 或 Maven 的配置指南,瞭解如何更改預設設定。 |
使用 Gradle 構建 Docker 映象
您可以使用一個命令透過 Gradle 構建一個已標記的 Docker 映象:
./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker
使用 Maven 構建 Docker 映象
為了快速入門,您可以直接執行 Spring Boot 映象生成器,而無需更改 pom.xml(請記住,如果存在 Dockerfile,它將被忽略)。
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=springio/gs-spring-boot-docker
要推送到 Docker 倉庫,您需要有推送許可權,而預設情況下您是沒有的。將映象字首更改為您自己的 Dockerhub ID,並執行 docker login 以確保在執行 Docker 命令前已進行身份驗證。
推送之後
示例中的 docker push 會失敗(除非您是 Dockerhub 上的“springio”組織成員)。但是,如果您將配置更改為匹配您自己的 Docker ID,它應該會成功。然後您就擁有了一個新標記、已部署的映象。
您 **不必** 註冊 Docker 或釋出任何內容即可執行本地構建的 Docker 映象。如果您使用 Docker(從命令列或 Spring Boot)構建,您仍然擁有一個本地標記的映象,並且可以像這樣執行它:
$ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
Container memory limit unset. Configuring JVM for 1G container.
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=86381K -XX:ReservedCodeCacheSize=240M -Xss1M -Xmx450194K (Head Room: 0%, Loaded Class Count: 12837, Thread Count: 250, Total Memory: 1073741824)
....
2015-03-31 13:25:48.035 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-03-31 13:25:48.037 INFO 1 --- [ main] hello.Application : Started Application in 5.613 seconds (JVM running for 7.293)
|
構建包在執行時使用記憶體計算器來調整 JVM 的大小以適應容器。 |
|
在使用帶有 boot2docker 的 Mac 時,您通常會在啟動時看到類似以下內容:
Docker client to the Docker daemon, please set:
export DOCKER_CERT_PATH=/Users/gturnquist/.boot2docker/certs/boot2docker-vm
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://192.168.59.103:2376
|
當它執行時,您可以在容器列表中看到類似如下的示例:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81c723d22865 springio/gs-spring-boot-docker:latest "java -Djava.secur..." 34 seconds ago Up 33 seconds 0.0.0.0:8080->8080/tcp goofy_brown
要再次關閉它,您可以使用上一個列表中容器的 ID 執行 docker stop(您的 ID 將不同)。
docker stop goofy_brown
81c723d22865
如果您願意,完成使用後也可以刪除該容器(它會持久化儲存在您的檔案系統中,位於 /var/lib/docker 下)。
使用 Spring Profiles
使用 Spring Profiles 執行您新建立的 Docker 映象,就像將環境變數傳遞給 Docker 執行命令一樣簡單(用於 prod profile):
docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker
docker run -e "SPRING_PROFILES_ACTIVE=dev" -p 8080:8080 -t springio/gs-spring-boot-docker
在 Docker 容器中除錯應用程式
要除錯應用程式,您可以使用 JPDA 傳輸。我們將容器視為遠端伺服器。要啟用此功能,請在 JAVA_OPTS 變數中傳遞 Java 代理設定,並在容器執行時將代理的埠對映到 localhost。對於 Docker for Mac,存在一個限制,因為我們無法在沒有 一些技巧 的情況下透過 IP 訪問容器。
docker run -e "JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n" -p 8080:8080 -p 5005:5005 -t springio/gs-spring-boot-docker