AdSense

網頁

2019/12/17

Spring Boot 建立docker image

本篇紀錄在Mac將Spring Boot應用程式製作成docker image檔,並在本機的docker中運行。


也可參考「Docker build Spring Boot docker image




範例環境:

  • macOS High Sierra
  • Docker
  • Java 1.8
  • Spring Boot 2.2.1.RELEASE
  • Maven
  • IntelliJ IDEA CE

首先建立一個簡單的Spring Boot的Maven專案

本範例專案名稱為demo,工作目錄在~/Documents/workspace/demo

~/Documents下新增一個mydocker資料夾,此目錄為Dockerfile執行時的工作目錄(WORKDIR)。


在Spring Boot預設配置檔src/main/resources/application.properties設定應用程式目錄及port。所以在本機的應用程式路徑為http://localhost:8080/demo

#context path
server.servlet.context-path=/demo
#port
server.port=8080

在專案中建立一個Controller類別DemoController如下。此類別僅是最後在docker運行Spring Boot的image時可以測試用。

DemoController

package com.abc.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @GetMapping("/")
    public String hello() {
        return "Hello Spring Boot with Docker";
    }

}

在專案根目錄也就是~/Documents/workspace/demo/新增一個名稱為Dockerfile的檔案(不用副檔名),其是一個文字檔而已。

Dockerfile裡面設定以下內容為docker build image時會執行的指令。

Dockerfile

FROM openjdk:8-jdk-alpine
COPY ./target/*.jar /Documents/mydocker/demo.jar
WORKDIR /Documents/mydocker
RUN sh -c 'touch demo.jar'
ENTRYPOINT ["java","-jar","demo.jar"]

Dockerfile指令簡單說明:

  1. FROM openjdk:8-jdk-alpine:使用的base image。
  2. COPY ./target/*.jar /Documents/mydocker/demo.jar:將build context,即所在目錄的target/*.jar複製到container檔案目錄的/Documents/mydocker/demo.jar。
  3. WORKDIR /Documents/mydocker:指定container的/Documents/mydocker為Docker的命令執行目錄。
  4. RUN sh -c 'touch demo.jar':修改demo.jar的時間戳記為目前時間。
  5. ENTRYPOINT ["java","-jar","demo.jar"]:在container剛指定的WORKDIR目錄執行java -jar demo.jar

然後用mvn clean install將此Spring Boot打包為jar檔,本範例直接使用IntelliJ IDEA的Maven視窗工具,點兩下[install]即可。



建構成功後在專案的target目錄下會產生jar檔,例如demo-0.0.1.SNAPSHOT.jar


接著就是把這個jar製作成docker image檔了。

把命令列所在目錄移到Dockerfile的位置也就是~/Documents/workspace/demo
執行docker build -t demo-app .指令來建立image。
-t 後接的參數為image的名稱,所以製作的image名稱為demo-app
.代表docker build時使用當下目錄的Dockerfile


~/Documents/workspace/demo$ docker build -t demo-app .
Sending build context to Docker daemon   19.5MB
Step 1/5 : FROM openjdk:8-jdk-alpine
8-jdk-alpine: Pulling from library/openjdk
e7c96db7181b: Pull complete
f910a506b6cb: Pull complete
c2274a1a0e27: Pull complete
Digest: sha256:94792824df2df33402f201713f932b58cb9de94a0cd524164a0f2283343547b3
Status: Downloaded newer image for openjdk:8-jdk-alpine
 ---> a3562aa0b991
Step 2/5 : COPY ./target/*.jar /Documents/mydocker/demo.jar
 ---> 96f62e91c5aa
Step 3/5 : WORKDIR /Documents/mydocker
 ---> Running in eaf63cde939c
Removing intermediate container eaf63cde939c
 ---> 9825b3aa91cf
Step 4/5 : RUN sh -c 'touch demo.jar'
 ---> Running in 3f23dc5f97ca
Removing intermediate container 3f23dc5f97ca
 ---> 4a271f8300a3
Step 5/5 : ENTRYPOINT ["java","-jar","demo.jar"]
 ---> Running in dfbdcbac56a5
Removing intermediate container dfbdcbac56a5
 ---> cc6a733eecd7
Successfully built cc6a733eecd7

image製作好後會存在本機的docker registry中。執行docker images指令列出現存的images,可以看到剛剛製作好的demo-app

$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
demo-app               latest              cc6a733eecd7        3 minutes ago       143MB
openjdk                8-jdk-alpine        a3562aa0b991        7 months ago        105MB
hello-world            latest              fce289e99eb9        11 months ago       1.84kB

輸入docker run -p 8080:8080 demo-appdemo-app在docker中執行。

-p 參數用來綁定host的port及container的port。前面的8080為本機的8080 port;後面的8080為container的8080 port。

$ docker run -p 8080:8080 demo-app

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.1.RELEASE)

2019-12-17 07:08:33.894  INFO 1 --- [           main] com.abc.demo.DemoApplication             : Starting DemoApplication v0.0.1-SNAPSHOT on f9bdf41d3e05 with PID 1 (/Documents/mydocker/demo.jar started by root in /Documents/mydocker)
2019-12-17 07:08:33.899  INFO 1 --- [           main] com.abc.demo.DemoApplication             : No active profile set, falling back to default profiles: default
2019-12-17 07:08:35.347  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-12-17 07:08:35.370  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-12-17 07:08:35.370  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.27]
2019-12-17 07:08:35.464  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/demo]   : Initializing Spring embedded WebApplicationContext
2019-12-17 07:08:35.464  INFO 1 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1452 ms
2019-12-17 07:08:35.809  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-12-17 07:08:36.070  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '/demo'
2019-12-17 07:08:36.075  INFO 1 --- [           main] com.abc.demo.DemoApplication             : Started DemoApplication in 2.983 seconds (JVM running for 3.66)
2019-12-17 07:08:46.341  INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/demo]   : Initializing Spring DispatcherServlet 'dispatcherServlet' 

在本機的瀏覽器url位址輸入http://localhost:8080/demo/可看到以下結果。



如要停止運行這個image先用docker psdocker container ls列出運行中的docker container。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES
f9bdf41d3e05        demo-app            "java -jar demo.jar"   About an hour ago   Up About an hour    0.0.0.0:8080->8080/tcp   bold_heisenberg

然後輸入docker stop <container_id>docker stop <container_name>停止運行。

$ docker stop f9bdf41d3e05
f9bdf41d3e05


7 則留言:

Li 提到...
作者已經移除這則留言。
匿名 提到...

是否有錯誤訊息?

匿名 提到...

您好,我按照步驟匯出,不過瀏覽網頁的時候是404,我其中有先遇到docker的db和docker image連不到的問題。後來嘗試綁定port有解決。不過瀏覽spring專案的網頁的時候還是404。我有上網查詢不過眾說紛紜。

首先我有一個疑問就是不是只有war才有辦法看到view的html/jsp的部分嗎?為什麼網上的教學都是jar的部分呢?還是其實jar也能看到view的部分。
另外,我搜尋關於404錯誤的時候,好像有看到說是因為匯出專案式war的關係,又或者是因為"targetPath必须为META-INF/resources"但這個設定在spring的properties時不是已經有設定了嗎@@!

謝謝前輩的幫忙

匿名 提到...

先回答你第二個問題,Spring Boot是否能看到view與打包成jar或war無關,兩者都可以看到view。Spring Boot的Maven預設打包為jar是因為內含embedded Tomcat容器,而會打包成war是有得時候必須部署到外部web server例如JBoss。

本範例使用maven install打包時預設是打包成jar並產出在專案下的/target目錄。
從你目前提供的資訊我仍無法判斷錯誤問題在哪,只能一步步來看:
1.請問你在執行docker build建立image時有成功嗎?
2.請問你用docker images查詢得到你建立的image檔嗎?
3.請問你執行docker run -p 8080:8080 <IMAGE> console有顯示Spring的啟動訊息嗎?

匿名 提到...

前輩您好:

您的問題這邊,我最後是用jar匯出,就是按照您的方式。

1. 有成功匯出。
2. 查詢得到image。
3. 啟動時有顯示成功啟動,但是一開始是顯示沒辦法連到資料庫。後來試了語法修改port有成功連上資料庫。

指令如下:
docker network create crm-mssql
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Sa123456" -p 1433:1433 --name mssql --network crm-mssql -d mcr.microsoft.com/mssql/server:2019-latest
docker run --network crm-mssql --name crm-container -p 8080:8080 crm-app

匿名 提到...

那看來Spring Boot的image有啟動,那就和docker沒關係了。
所以你瀏覽器輸入http://localhost:8080/<context_path> 仍出現404是嗎?你可能要檢查一下你Spring Boot的controller的設定。

匿名 提到...

仔細看一下你的指令你運行docker時有使用自訂的docker network [crm-mssql],那麽連到docker container裡面的Spring Boot應該就不是用localhost,可能要用自訂docker network的IP位址。

AdSense