Spring系列學習之構建Spring Boot 的Docker容器應用
英文原文:https://spring.io/guides/gs/spring-boot-docker/
目錄
本指南將指導您構建執行Spring Boot應用程式的Docker映象的過程。
你要構建什麼
你需要什麼
- 大約15分鐘
- 最喜歡的文字編輯器或IDE
- JDK 1.8或更高版本
- Gradle 4+或Maven 3.2+
- 您還可以將程式碼直接匯入IDE:
如果您不使用Linux計算機,則需要虛擬化伺服器。 通過安裝VirtualBox,Mac的boot2docker等其他工具可以為您無縫管理。 訪問VirtualBox的下載站點,選擇適合您機器的版本。 下載並安裝。 不要擔心實際執行它。
您還需要Docker,它只能在64位計算機上執行。 有關為計算機設定Docker的詳細資訊,請參閱https://docs.docker.com/installation/#installation。 在繼續之前,請驗證您是否可以從shell執行docker命令。 如果您使用的是boot2docker,則需要先執行它。
如何完成本指南
與大多數Spring入門指南一樣,您可以從頭開始並完成每個步驟,或者您可以繞過您已熟悉的基本設定步驟。 無論哪種方式,您最終都會使用工作程式碼。
要從頭開始,請繼續使用Gradle構建。
要跳過基礎知識,請執行以下操作:
- 下載並解壓縮本指南的源儲存庫,或使用Git克隆它:
git clone https://github.com/spring-guides/gs-spring-boot-docker.git
- cd到gs-spring-boot-docker/initial
- 跳轉到設定Spring Boot應用程式。
完成後,您可以根據gs-spring-boot-docker/complete中的程式碼檢查結果。
Gradle構建
首先,設定一個基本的構建指令碼。 在使用Spring構建應用程式時,您可以使用任何您喜歡的構建系統,但此處包含了使用Gradle和Maven所需的程式碼。 如果您不熟悉這兩者,請參閱使用Gradle構建Java專案或使用Maven構建Java專案。
建立目錄結構
└── src
└── main
└── java
└── hello
在您選擇的專案目錄中,建立以下子目錄結構; 例如,在* nix系統上使用mkdir -p src / main / java / hello:
建立Gradle構建檔案
下面是最初的Gradle構建檔案。
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'gs-spring-boot-docker'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
Spring Boot gradle外掛提供了許多方便的功能:
- 它收集類路徑上的所有jar並構建一個可執行的“über-jar”,這使得執行和傳輸服務更加方便。
- 它搜尋public static void main()方法以標記為可執行的類。
- 它提供了一個內建的依賴項解析器,它設定版本號以匹配Spring Boot依賴項。 您可以覆蓋任何您希望的版本,但它將預設為Boot的所選版本集。
Maven構建
首先,設定一個基本的構建指令碼。 在使用Spring構建應用程式時,您可以使用任何您喜歡的構建系統,但此處包含了使用Maven所需的程式碼。 如果您不熟悉Maven,請參閱使用Maven構建Java專案。
建立目錄結構
└── src
└── main
└── java
└── hello
在您選擇的專案目錄中,建立以下子目錄結構; 例如,在* nix系統上使用mkdir -p src/main/java/hello:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-spring-boot-docker</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot Maven外掛提供了許多方便的功能:
- 它收集類路徑上的所有jar並構建一個可執行的“über-jar”,這使得執行和傳輸服務更加方便。
- 它搜尋public static void main()方法以標記為可執行的類。
- 它提供了一個內建的依賴項解析器,它設定版本號以匹配Spring Boot依賴項。 您可以覆蓋任何您希望的版本,但它將預設為Boot的所選版本集。
IDE構建
閱讀如何將本指南直接匯入Spring Tool Suite。
閱讀IntelliJ IDEA中如何使用本指南。
設定一個Spring Boot應用程式
現在您可以建立一個簡單的應用程式
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello Docker World";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
該類被標記為@SpringBootApplication和@RestController,這意味著Spring MVC可以使用它來處理Web請求。 @RequestMapping 對映 / 到 home()方法,它只發送一個'Hello World'響應。 main()方法使用Spring Boot的SpringApplication.run()方法來啟動應用程式。
現在我們可以在沒有Docker容器的情況下執行應用程式(即在主機OS中)。
如果您使用的是Gradle,請執行:
./gradlew build && java -jar build /libs/gs-spring-boot-docker-0.1.0.jar
如果您使用的是Maven,請執行:
./mvnw package && java -jar target/gs-spring-boot-docker-0.1.0.jar
並轉到localhost:8080以檢視您的“Hello Docker World”訊息。
應用容器化
Docker有一個簡單的Dockerfile檔案格式,用於指定影象的“圖層”。 那麼讓我們繼續在Spring Boot專案中建立一個Dockerfile:
Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
這個Dockerfile非常簡單,但是你需要執行一個沒有多餘裝飾的Spring Boot應用程式:只需要Java和一個JAR檔案。 專案JAR檔案作為“app.jar”新增到容器中,然後在ENTRYPOINT中執行。
我們添加了一個指向“/tmp”的VOLUME(對映路徑),因為這是Spring Boot應用程式預設為Tomcat建立工作目錄的地方。 效果是在主機“/var/lib/docker”下建立一個臨時檔案,並將其連結到“/tmp”下的容器。 對於我們在此處編寫的簡單應用程式,此步驟是可選的,但如果需要在檔案系統中實際編寫,則對於其他Spring Boot應用程式可能是必需的。
為了減少Tomcat的啟動時間,我們添加了一個指向“/dev/urandom”的系統屬性作為源。 如果您使用Tomcat(或任何其他Web伺服器)的“標準”版本,則對於更新版本的Spring Boot,這不是必需的。
為了充分利用Spring Boot jar檔案中依賴項和應用程式資源之間的清晰分離,我們將使用稍微不同的Dockerfile實現:
Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
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引數,指向我們已經解壓縮jar的目錄。 如果我們做對了,它已經包含一個BOOT-INF/lib目錄,其中包含依賴項jars,以及一個帶有應用程式類的BOOT-INF / classes目錄。 請注意,我們正在使用應用程式自己的主類hello.Application(這比使用jar啟動器提供的間接更快)。
如果您使用的是boot2docker,則需要在使用Docker命令列或使用構建工具執行任何操作之前先執行它(它執行一個守護程序來處理虛擬機器中的工作)。
要構建映象,您可以從社群使用Maven或Gradle的一些工具(非常感謝Palantir和Spotify提供這些工具)。
使用Maven構建Docker映象
在Maven pom.xml中,您應該新增一個這樣的新外掛(有關更多選項,請參閱外掛文件)::
pom.xml
<properties>
<docker.image.prefix>springio</docker.image.prefix>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.9</version>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
</configuration>
</plugin>
</plugins>
</build>
配置指定了1個必需的東西:具有映像名稱的儲存庫,最終將作為springio/gs-spring-boot-docker。
其他一些屬性是可選的:
- jar將要解壓縮的目錄的名稱,將Maven配置公開為docker的構建引數。 可以使用外掛配置的<buildArgs />指定它。
- 影象標記,如果未指定則最終為“最新”。 它可以使用<tag/>元素設定
在繼續執行以下步驟(使用Docker的CLI工具)之前,請通過鍵入docker ps確保Docker正常執行。 如果收到錯誤訊息,可能無法正確設定某些內容。 用Mac? 將$(boot2docker shellinit 2> /dev/null)新增到.bash_profile(或類似的env-setting配置檔案)的底部並重新整理shell以確保配置了正確的環境變數。
為確保在建立docker映象之前解壓縮jar,我們為依賴項外掛新增一些配置:
pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
您可以使用命令列構建標記的docker映象,如下所示:
$ ./mvnw install dockerfile:build
你可以用./mvnw dockerfile:push將影象推送到dockerhub。
您不必將新建立的Docker映象推送到實際執行它。 此外,如果您不是Dockerhub上“springio”組織的成員,“push”命令將失敗。 將構建配置和命令列更改為您自己的使用者名稱而不是“springio”,以使其實際工作。
您可以通過將其新增到外掛配置來使dockerfile:push在安裝或部署生命週期階段自動執行。
pom.xml
<executions>
<execution>
<id>default</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
使用Gradle構建Docker映象
如果您使用的是Gradle,則需要新增如下新外掛:
build.gradle
buildscript {
...
dependencies {
...
classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0')
}
}
group = 'springio'
...
apply plugin: 'com.palantir.docker'
task unpack(type: Copy) {
dependsOn bootJar
from(zipTree(tasks.bootJar.outputs.files.singleFile))
into("build/dependency")
}
docker {
name "${project.group}/${bootJar.baseName}"
copySpec.from(tasks.unpack.outputs).into("dependency")
buildArgs(['DEPENDENCY': "dependency"])
}
配置指定了4件事:
- 解壓jar檔案的任務
- 影象名稱(或標記)是從jar檔案屬性設定的,最終將作為springio / gs-spring-boot-docker
- 解壓縮的jar檔案的位置,我們可以在Dockerfile中對其進行硬編碼
- docker指向jar檔案的構建引數
您可以構建標記的docker映象,然後使用Gradle將其推送到遠端儲存庫:
$ ./gradlew build docker
Push推送映象之後
“docker push”將失敗(除非您是Dockerhub中“springio”組織的一部分),但是如果您更改配置以匹配您自己的docker ID,那麼它應該會成功,並且您將有一個新的標記,部署 圖片。
您不必註冊docker或釋出任何東西來執行docker映象。 您仍然擁有本地標記的映象,您可以像這樣執行它:
$ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
....
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)
然後可以在http://localhost:8080上訪問該應用程式(訪問它並顯示“Hello Docker World”)。 要確保整個過程真正有效,請將字首從“springio”更改為其他內容(例如$ {env.USER}),然後再從構建到docker執行再次檢視。
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_HOST中的IP地址而不是localhost。 在這種情況下,http://192.168.59.103:8080,VM的面向公眾的IP。
當它執行時,您可以在容器列表中看到,例如:
$ 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 ${container_id}(你的將是不同的):
$ docker stop 81c723d22865
81c723d22865
如果您願意,還可以在完成後刪除容器(它在/var/lib/docker下的檔案系統中儲存):
$ docker rm 81c723d22865
使用Spring Profiles配置
使用Spring配置檔案執行剛剛建立的Docker映象就像將環境變數傳遞給Docker run命令一樣簡單
$ 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_OPTS=-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n" -p 8080:8080 -p 5005:5005 -t springio/gs-spring-boot-docker
總結
恭喜! 您剛剛為Spring Boot應用程式建立了一個Docker容器! 預設情況下,Spring Boot應用程式在容器內的埠8080上執行,我們使用命令列上的“-p”將其對映到主機上的同一埠。
更多
以下指南也可能有所幫助:
想要撰寫新指南或為現有指南做出貢獻? 檢視我們的貢獻指南。
所有指南均附有程式碼的ASLv2許可證,以及Attribution,NoDerivatives創作公共許可證。