Windows Docker 部署 Spring Boot 項目

本文首發於我的個人博客,Windows Docker 部署 Spring Boot 項目 ,歡迎訪問!

使用 Docker 部署一個簡單的 Spring Boot 數據庫項目。

最近容器化技術 hin 流行啊,所以開始折騰一下唄。試用了下,有的時候的確比虛擬機要方便。其實起初是要用 Redis,但是 Windows 安裝不方便,於是就看了下 Docker,基本開箱即用,Docker Hub 上 pull 一個 Redis 官方鏡像就可以了。剛好最近在學 Spring Cloud 微服務,也就嘗試下 Docker 部署 Spring Boot 項目。

內容上,本文主要是對 Spring Boot 官方文檔的一個補充,英語不錯的可以直接瀏覽參考文獻 6-7。

Docker 的基本概念和用法,這裏就詳細展開了,可以看參考文獻 1-3。

Docker Configuration

Docker 一般是安裝在 Linux 系統中的,但是目前 Windows 也能使用,下載安裝後開箱即用。額外需要配置的是:

1.暴露 TCP 端口,可以在 IDEA 的 Docker 插件中連接使用。

2.替換官方的 Docker 倉庫,使用阿里雲鏡像加速。在 Daemon 中可進行配置。

Config IDEA Plugin

Docker 本身是沒有 GUI 界面的,所以所有的維護管理都必須使用命令行,比較麻煩,而 IDEA 給我們提供了一個簡單的 Docker 插件,用於管理 Image 和 Container。

在設置中直接搜索 docker,添加一個 Docker Engine,配置 URL,稍等下面提示連接成功後,就可以了。如果連接失敗了,可以檢查下 TCP 的 2375 端口是否暴露了。

然後在底部,就能看到 Docker 的 tab了。裏面的信息和命令行中看到的一致。基本的創建、刪除、端口映射等操作都能在上面進行。

那麼 Docker 相關的配置就完成了。

Create Spring Boot Project

這裏用來演示的是一個 Spring Boot 的數據庫項目。跟之前一樣,配置一些基本內容就可以了。

<!--pom.xml-->
<groupId>com.example</groupId>
<artifactId>spring-docker</artifactId>
<version>1.0.0</version>
<name>spring-docker</name>

<properties>
  <java.version>1.8</java.version>
  <skipTests>true</skipTests>		
  <druid-spring-boot-starter.version>1.1.10</druid-spring-boot-starter.version>
  <dockerfile-maven-plugin.version>1.4.10</dockerfile-maven-plugin.version>
  <docker.image.prefix>springboot</docker.image.prefix>
</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>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>${druid-spring-boot-starter.version}</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
  </dependency>
</dependencies>
# application.yml
# 配置數據庫信息
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://ip:port/test?serverTimezone=Hongkong&characterEncoding=utf-8&useSSL=false
    username: 
    password: 
// Spring Boot 啓動類
@SpringBootApplication
@RestController
public class SpringDockerApplication {

    private final JdbcTemplate jdbcTemplate;
    public SpringDockerApplication(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @GetMapping("/")
    public Message index() {
        RowMapper<Message> rowMapper= new BeanPropertyRowMapper<>(Message.class);
        Message msg = jdbcTemplate.queryForObject("select * from message where k = ?", rowMapper,"msg");
        return msg;
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringDockerApplication.class, args);
    }
}

本地訪問,能成功運行就說明,Spring Boot 已經配置完成了。下面就是容器化的操作了。

Containerize It

容器化其實就是創建 Docker 鏡像的過程,需要把 jar 包封裝進去。具體有兩種方式:

  1. 創建 Dockerfile,手動執行 build 和 run。
  2. 使用 dockerfile-maven-plugin 插件。

Use Dockerfile

首先,在 src/main 下創建 docker 文件夾,並創建一個 Dockerfile,內容如下:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY spring-docker-1.0.0.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

假設大家對 Dockerfile 有一定的瞭解,解釋一下上面的內容,

  • FROM...,表示使用 openjdk 作爲基本鏡像,標籤爲:8-jdk-alpine,意思 jdk 版本爲 1.8,精簡版。
  • VOLUME...,創建一個 VOLUME 指向 /tmp 的內容。因爲這是 Spring Boot 應用程序默認爲 Tomcat 創建工作目錄的地方。效果是在 / var / lib / docker 下的主機上創建一個臨時文件,並將其鏈接到 /tmp 下的容器。
  • COPY...,把項目中的 jar 文件複製過去,並重命名爲 app.jar
  • ENTRYPOINT...,執行 java -jar 啓動項目。java.security.egd=file:/dev/./urandom 的目的是爲了縮短 Tomcat 啓動的時間。

接下來,使用 maven 打包一個 jar,手動複製到 src/main/docker目錄下。

配置 IDEA 來 build 和 run docker。

需要配置的參數如下:

  1. Name,創建的配置文件名稱。
  2. Image tag,創建鏡像的 repository:tags。
  3. Container name,創建容器名。
  4. Bind posts,端口映射,這裏將默認的 8080 端口映射到外網的 8888 端口。
  5. 最後 IDEA 會將參數拼接成 docker build & run 的命令。

點擊 Run。鏡像就會被自動 build,並且在容器中運行了。

選擇我們剛剛部署的那個容器的 Log tab,就能看到 Spring Boot 啓動的日誌了,是不是有一種似曾相識的感覺 😃

假設之前我們連接的是本地的 MySQL那麼執行到這裏應該會報錯了,錯誤如下:

2019-08-27 10:37:04.197 ERROR 1 --- [eate-1939990953] com.alibaba.druid.pool.DruidDataSource   : create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Hongkong&characterEncoding=utf-8&useSSL=false, errorCode 0, state 08S01

com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
Caused by: java.net.ConnectException: Connection refused (Connection refused)

這是由於我們配置數據庫 url 出現了問題,本地數據庫,一般會寫 localhost127.0.0.0.1。但是這個地址在 Docker 容器中是不認的。所以連不上本地數據庫。幸好 Docker 在安裝的時候配置了一個虛擬網橋,通過那個地址可以和 Docker 容器內部進行通信。

我們在宿主機上的 cmd 中輸入 ipconfig ,就可以看到。

C:\Users\mqy6289>ipconfig
Windows IP 配置

以太網適配器 vEthernet (DockerNAT):
   連接特定的 DNS 後綴 . . . . . . . :
   IPv4 地址 . . . . . . . . . . . . : 10.0.75.1
   子網掩碼  . . . . . . . . . . . . : 255.255.255.240
   默認網關. . . . . . . . . . . . . :

以太網適配器 以太網:
   連接特定的 DNS 後綴 . . . . . . . : apac.arcsoft.corp
   本地鏈接 IPv6 地址. . . . . . . . : fe80::c64:6e72:8311:378c%13
   IPv4 地址 . . . . . . . . . . . . : 172.17.218.99
   子網掩碼  . . . . . . . . . . . . : 255.255.224.0
   默認網關. . . . . . . . . . . . . : 172.17.192.1

在 DockerNAT 中,宿主機的 ip 爲 10.0.75.1,將數據庫的 url ip 改成這個。將之前的容器和鏡像刪除後,重新執行之前的步驟,發現部署成功。

在瀏覽器中輸入:http://127.0.0.1:8888/ ,就能看到 hello world 了。整個 Docker 部署的過程完成。

針對之前的網絡通信問題,我們可以在 cmd 中輸入 docker inspect hello-world 來查看容器的參數。可以看到容器的 IP 和網關的確不在一個網段,無法進行通信。

{
    "Networks": {
        "bridge": {
            "Gateway": "172.17.0.1",
            "IPAddress": "172.17.0.2",
            "MacAddress": "02:42:ac:11:00:02",
        }
    }
}

Use Maven Plugin

之前我們創建完 dockerfile 之後通過 IDEA 進行鏡像的 build 和 run 工作,同樣的我們也可以通過 Maven 插件將 Docker 鏡像和容器的構建和 Maven 集成起來。閱讀了很多博客,大多數任然使用 docker-maven-plugin,但是這個插件早就已經被官方廢棄,不推薦使用,spotify 也推薦使用 dockerfile-maven-plugin 來代替。下面的 pom.xml 的配置:

<plugin>
  <groupId>com.spotify</groupId>
  <artifactId>dockerfile-maven-plugin</artifactId>
  <version>${dockerfile-maven-plugin.version}</version>
  <configuration>
    <contextDirectory>src/main/docker</contextDirectory>
    <repository>${docker.image.prefix}/${project.artifactId}</repository>
    <tag>v1</tag>
  </configuration>
</plugin>

刷新後,可在 IDEA 的 Maven tab 看到插件的功能,主要用到的就是 build、push、tag。

默認插件會找本地 tcp://localhost:2375 如果開啓了,雙擊 dockerfile:build 鏡像就會被 build 到 Docker 中去。

可以看到,整個 build 的過程被放到 Maven 中去了。需要運行的話,選擇相應的鏡像,create container 再做簡單配置就可以了。

Troubleshooting

1.數據庫連接不上。

本地數據庫需要配置支持遠程額訪問,由於是 Windows,一般不會配置,可能會忽視。

UPDATE USER SET HOST = '%' WHERE USER = 'root';
FLUSH PRIVILEGES;

2.進入終端

我們這個基礎鏡像是 Java,默認應該是不帶終端的,如果要查看的話,可再起一個容器

docker run -ti --entrypoint /bin/sh springboot/spring-docker:v1

進入後輸入 ls -al,可以看到之前複製過去的 app.jar 文件

Summary

本文主要講了如果在 Docker 中部署 Spring Boot 項目,針對於 Dockfile 的寫法和 Maven 插件的使用,可以看參考文獻 5-6,裏面有更多的細節。我個人覺得這種部署方法,直接把 jar 和基本的環境打成一個 Docker 鏡像還是過於簡單了,很難去做監控和管理。是否還是應該像服務器部署那樣,創建一個基本的 Centos 鏡像,然後安裝 Java 的環境,通過 ssh 連接,直接把 jar 包和配置文件複製到容器中通過 bash 運行,這種方式可能更符合常理。後期如果工作上有需求,可以再深度學習一下,看看實際生產中是如何操作的。

Reference

  1. Docker Get Started
  2. 30 分鐘快速入門 Docker 教程
  3. Docker(一):Docker入門教程
  4. Spring Boot 2.0(四):使用 Docker 部署 Spring Boot
  5. Spring Boot with Docker
  6. Spring Boot Docker

Source Code

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章