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 大小以符合容器。 |
|
當在 Mac 上使用 boot2docker 時,您通常會在啟動時看到類似這樣的內容
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 Profile
使用 Spring Profile 執行您新建立的 Docker 映像檔就像將環境變數傳遞至 Docker run 命令一樣簡單(適用於 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 Transport。我們將容器視為遠端伺服器。若要啟用此功能,請在 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