docker-compose下的java應用啟動順序兩部曲之二:實戰

      網友投稿 897 2025-04-03

      歡迎訪問我的GitHub

      這里分類和匯總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

      上篇回顧

      本文是《docker-compose下的java應用啟動順序兩部曲》的終篇,在上一篇《docker-compose下的java應用啟動順序兩部曲之一:問題分析》中,我們以SpringCloud環境下的注冊中心和業務服務為例,展示了docker-compose.yml中

      depends_on

      參數的不足:即只能控制容器創建順序,但我們想要的是eureka服務就緒之后再啟動業務服務,并且docker官方也認為

      docker-compose下的java應用啟動順序兩部曲之二:實戰

      depends_on

      參數是達不到這個要求的,如下圖所示:

      針對上述問題,docker給出的解決辦法是使用

      wait-for-it.sh

      腳本來解決問題,地址:https://docs.docker.com/compose/startup-order/ ,如下圖:

      什么是wait-for-it.sh

      wait-for-it.sh

      腳本用來訪問指定的地址和端口,如果收不到響應就等待一段時間再去重試,直到收到響應后,再去做前面指定好的命令,如上圖紅框所示

      ./wait-for-it.sh db:5432 – python app.py

      的意思是:等到

      db:5432

      這個遠程訪問能夠響應的時候,就去執行

      python app.py

      命令

      wait-for-it.sh文件的鏈接:

      https://raw.githubusercontent.com/zq2599/blog_demos/master/wait-for-it-demo/docker/wait-for-it.sh

      環境信息

      本次實戰的環境如下:

      操作系統:CentOS Linux release 7.7.1908

      docker:1.13.1

      docker-compose:1.24.1

      spring cloud:Finchley.RELEASE

      maven:3.6.0

      jib:1.7.0

      實戰簡介

      上一篇的例子中,我們用到了eureka和service兩個容器,eureka是注冊中心,service是普通業務應用,service容器向eureka容器注冊時,eureka還沒有初始化完成,因此service注冊失敗,在稍后的自動重試時由于eureka進入ready狀態,因而service注冊成功。

      今天我們來改造上一篇的例子,讓service用上docker官方推薦的

      wait-for-it.sh

      腳本,等待eureka服務就緒再啟動java進程,確保service可以一次性注冊eureka成功;

      為了達到上述目標,總共需要做以下幾步:

      簡單介紹eureka和service容器的鏡像是怎么制作的;

      制作基礎鏡像,包含

      wait-for-it.sh

      腳本;

      使用新的基礎鏡像構建service鏡像;

      改造docker-compose.yml;

      啟動容器,驗證順序控制是否成功;

      wait-for-it.sh方案的缺陷;

      接下來進入實戰環節;

      源碼下載

      如果您不想編碼,也可以在GitHub上獲取文中所有源碼和腳本,地址和鏈接信息如下表所示:

      這個git項目中有多個文件夾,本章的應用在

      wait-for-it-demo

      文件夾下,如下圖紅框所示:

      源碼的結構如下圖所示:

      接下來開始編碼了;

      簡單介紹eureka和service容器

      上一篇和本篇,我們都在用eureka和service這兩個容器做實驗,現在就來看看他們是怎么做出來的:

      eureka是個maven工程,和SpringCloud環境中的eureka服務一樣,唯一不同的是它的pom.xml中使用了jib插件,用來將工程構建成docker鏡像:

      4.0.0 com.bolingcavalry eureka 0.0.1-SNAPSHOT jar eureka eureka com.bolingcavalry wait-for-it-demo 0.0.1-SNAPSHOT ../pom.xml UTF-8 UTF-8 1.8 Finchley.RELEASE org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.boot spring-boot-maven-plugin com.google.cloud.tools jib-maven-plugin 1.7.0 openjdk:8-jdk-stretch bolingcavalry/${project.artifactId}:${project.version} -Xms1g -Xmx1g 8080 true compile dockerBuild

      上述pom.xml中多了個jib插件,這樣在執行

      mvn compile

      的時候,插件就會用構建結果制作好docker鏡像并放入本地倉庫;

      service是個普通的SpringCloud應用,除了在pom.xml中也用到了jib插件來構建鏡像,它的配置文件中,訪問eureka的地址要寫成eureka容器的名稱:

      spring: application: name: service eureka: client: serviceUrl: defaultZone: http://eureka:8080/eureka/

      制作基礎鏡像

      從上面的pom.xml可見,我們將Java應用制作成docker鏡像時,使用的基礎鏡像是

      openjdk:8-jdk-stretch

      ,這樣做出的應用鏡像是不含wait-for-it.sh腳本的,自然就無法實現啟動順序控制了,因此我們要做一個帶有wait-for-it.sh的基礎鏡像給業務鏡像用:

      把wait-for-it.sh文件準備好,-:https://raw.githubusercontent.com/zq2599/blog_demos/master/wait-for-it-demo/docker/wait-for-it.sh

      在wait-for-it.sh文件所在目錄新建Dockerfile文件,內容如下:

      FROM openjdk:8-jdk-stretch ADD wait-for-it.sh /wait-for-it.sh RUN sh -c 'chmod 777 /wait-for-it.sh'

      注意:

      我這里用的是openjdk:8-jdk-stretch,您可以根據自己的實際需要選擇不同的openjdk版本,可以參考:《openjdk鏡像的tag說明》

      執行命令

      docker build -t bolingcavalry/jkd8-wait-for-it:0.0.2 .

      就能構建出名為

      bolingcavalry/jkd8-wait-for-it:0.0.2

      的鏡像了,請您根據自己的情況設置鏡像名稱和tag,注意命令的末尾有個小數點,不要漏了;

      如果您有hub.docker.com賬號,建請使用

      docker push

      命令將新建的鏡像推送到鏡像倉庫上去,或者推送到私有倉庫,因為后面使用jib插件構建鏡像是,jib插件要去倉庫獲取基礎鏡像的元數據信息,取不到會導致構建失敗;

      使用新的基礎鏡像構建service鏡像

      我們的目標是讓service服務等待eureka服務就緒,所以應該改造service服務,讓它用docker官方推薦的

      wait-for-it.sh

      方案來實現等待:

      修改service工程的pom.xml,有關jib插件的配置改為以下內容:

      com.google.cloud.tools jib-maven-plugin 1.7.0 bolingcavalry/jkd8-wait-for-it:0.0.2 bolingcavalry/${project.artifactId}:${project.version} INHERIT 8080 true compile dockerBuild

      上述配置有幾點需要注意:

      a. 基礎鏡像改為剛剛構建好的

      bolingcavalry/jkd8-wait-for-it:0.0.2

      b. 增加

      entrypoint

      節點,內容是

      INHERIT

      ,按照官方的說法,entrypoint的值等于INHERIT表示jib插件不構建啟動命令了,此時要使用者自己控制,可以在啟動時輸入,或者寫在基礎鏡像中,這樣我們在docker-compose.yml中用command參數來設置service容器的啟動命令,就可以把

      wait-for-it.sh

      腳本用上了

      c. 去掉

      jvmFlags

      節點,按照官方文檔的說法,entrypoint節點的值等于INHERIT時,jvmFlags和mainClass參數會被忽略,如下圖,地址是:https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin

      至此,service工程改造完畢,接下來修改docker-compose.yml,讓service容器能用上wait-for-it.sh

      改造docker-compose.yml

      完整的docker-compose.yml內容如下所示:

      version: '3' services: eureka: image: bolingcavalry/eureka:0.0.1-SNAPSHOT container_name: eureka restart: unless-stopped service: image: bolingcavalry/service:0.0.1-SNAPSHOT container_name: service restart: unless-stopped command: sh -c './wait-for-it.sh eureka:8080 -t 0 -- java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.ServiceApplication' depends_on: - eureka

      注意command參數的內容,如下,service容器創建后,會一直等待eureka:8080的響應,直到該地址有響應后,才會執行命令

      java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.ServiceApplication

      sh -c './wait-for-it.sh eureka:8080 -t 0 -- java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.ServiceApplication'

      對于命令

      java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.ServiceApplication

      ,您可能覺得太長了不好寫,這里有個小竅門,就是在不使用

      entrypoint

      節點的時候,用jib插件制作的鏡像本身是帶有啟動命令的,容器運行的時候,您可以通過

      docker ps --no-trunc

      命令看到該容器的完整啟動命令,復制過來直接用就行了;

      所有的改造工作都完成了,可以開始驗證了;

      啟動容器,驗證順序控制是否成功

      在docker-compose.yml文件所在目錄執行命令

      docker-compose up

      ,會創建兩個容器,并且日志信息會直接打印在控制臺,我們來分析這些日志信息,驗證順序控制是否成功;

      如下圖,可見service容器中并沒有啟動java進程,而是在等待eureka:8080的響應:

      繼續看日志,可見eureka服務就緒的時候,service容器的wait-for-it.sh腳本收到了響應,于是立即啟動service應用的進程:

      繼續看日志,如下圖,service在eureka上注冊成功:

      綜上所述,使用docker官方推薦的wait-for-it.sh來控制java應用的啟動順序是可行的,可以按照業務自身的需求來量身定做合適的啟動順序;

      wait-for-it.sh方案的缺陷

      使用docker官方推薦的

      wait-for-it.sh

      來控制容器啟動順序,雖然已滿足了我們的需求,但依舊留不是完美方案,留下的缺陷還是請您先知曉吧,也許這個缺陷會對您的系統產生嚴重的負面影響:

      再開啟一個SSH連接,登錄到實戰的linux電腦上,執行命令

      docker exec eureka ps -ef

      ,將eureka容器內的進程打印出來,如下所示,

      java進程的PID等于1

      [root@maven ~]# docker exec eureka ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 2 07:04 ? 00:00:48 java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.EurekaApplication root 56 0 0 07:25 ? 00:00:00 /bin/bash root 63 0 0 07:31 ? 00:00:00 ps -ef

      再來看看service的進程情況,執行命令

      docker exec service ps -ef

      ,將service容器內的進程打印出來,如下所示,

      PID等于1的進程不是java,而是啟動時的shell命令

      [root@maven ~]# docker exec service ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 07:04 ? 00:00:00 sh -c ./wait-for-it.sh eureka:8080 -t 0 -- java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.ServiceApplication root 7 1 1 07:04 ? 00:00:32 java -Xms1g -Xmx1g -cp /app/resources:/app/classes:/app/libs/* com.bolingcavalry.waitforitdemo.ServiceApplication root 107 0 0 07:33 ? 00:00:00 ps -ef

      通常情況下,在執行命令

      docker stop xxx

      停止容器時,只有PID=1的進程才會收到"SIGTERM"信號量,所以在使用

      docker stop

      停止容器時,eureka容器中的java進程收到了"SIGTERM"可以立即停止,但是service容器中的java進程收不到"SIGTERM",因此只能等到默認的10秒超時時間到達的時候,被"SIGKILL"信號量殺死,

      不但等待時間長,而且優雅停機的功能也用不上了

      您可以分別輸入

      docker stop eureka

      docker stop service

      來感受一下,前者立即完成,后者要等待10秒。

      我的shell技能過于平庸,目前還找不到好的解決辦法讓service容器中的java進程取得1號進程ID,個人覺得自定義entrypoint.sh腳本來調用wait-for-it.sh并且處理"SIGTERM"說不定可行,如果您有好的辦法請留言告知,在此感激不盡;

      目前看來,控制容器啟動順序最好的解決方案并非wait-for-it.sh,而是業務自己實現容錯,例如service注冊eureka失敗后會自動重試,但是這對業務的要求就略高了,尤其是在復雜的分布式環境中更加難以實現;

      docker官方推薦使用wait-for-it.sh腳本的文章地址是:https://docs.docker.com/compose/startup-order/ ,文章末尾顯示了頂和踩的數量,如下圖,頂的數量是145,踩的數量達到了563,一份官方文檔居然這么不受待見,也算是開了眼界,不知道和我前面提到的1號PID問題有沒有關系:

      至此,java應用的容器順序控制實戰就完成了,希望您在對自己的應用做容器化的時候,此文能給您提供一些參考。

      歡迎關注華為云博客:程序員欣宸

      學習路上,你不孤單,欣宸原創一路相伴…

      Docker Java 容器 鏡像服務

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:如何計算同一單元格中的Excel數據的公式和?
      下一篇:word表格怎樣設置每一頁都顯示表頭(word表格每一頁都有表頭怎么設置)
      相關文章
      久久久无码精品亚洲日韩按摩| 伊人久久大香线蕉亚洲| 亚洲午夜国产片在线观看| 亚洲娇小性xxxx| 亚洲高清视频在线播放| 亚洲av永久无码精品漫画| 国产成人综合亚洲亚洲国产第一页| 亚洲国产成人精品无码久久久久久综合 | 亚洲日本va午夜中文字幕久久| 亚洲AV成人片无码网站| 亚洲乱色伦图片区小说 | 亚洲AV一二三区成人影片| 亚洲国产高清视频在线观看| 亚洲视频网站在线观看| 亚洲精品视频在线观看免费| 综合自拍亚洲综合图不卡区| 亚洲三级电影网址| 91亚洲精品第一综合不卡播放| 亚洲国产精品国自产电影| 亚洲AV无码成人精品区天堂 | 77777亚洲午夜久久多喷| 亚洲av片不卡无码久久| 亚洲制服丝袜中文字幕| 国产成人精品日本亚洲网址| 日本亚洲色大成网站www久久| 亚洲人成网站看在线播放| 亚洲AV成人一区二区三区在线看| 亚洲av乱码一区二区三区香蕉| 亚洲中文字幕久在线| 亚洲va久久久久| 精品久久久久久亚洲综合网| 日韩亚洲人成网站| 中文字幕亚洲综合久久男男| 亚洲国产精品va在线播放| 亚洲AV人无码激艳猛片| 亚洲欧洲日产v特级毛片| 学生妹亚洲一区二区| 国产精品亚洲lv粉色| 亚洲一区二区三区乱码A| 亚洲色婷婷综合久久| 亚洲成人免费在线|