控制Docker Compose的啟動順序的一個思路
- 起源
- 守護進程daemon
- 從守護進程的角度看Docker Compose
- Docker的解決方案
- 思路
- 代碼
- 結果
起源
Docker Compose
提供了一個depends_on
參數。
https://docs.docker.com/compose/compose-file/#depends_on
depends_on
參數用於描述服務之間的依賴關系,服務依賴將導致如下行為:
docker-compose up
按照依賴關系的順序啟動服務。docker-compose up SERVICE
自動包含SERVICE
的依賴關系。
看起來功能強大,不過接下來又說了使用depends_on
的註意事項:
depends_on
在啟動服務時並不會等待相關所依賴的服務完成。
也就是說,depends_on
的主要功能是自動包含SERVICE
的依賴關系,
而所謂的按照依賴關系的順序啟動服務在實踐中幾乎毫無意義,
因為很多被依賴的服務往往啟動緩慢,例如數據庫。
分析Docker
的工作原理會發現,
管理依賴關系的挑戰在於Docker
本身並不管理服務的啟動的過程,
一個Docker
服務在啟動entrypoint
或者command
進程的時候就開始了,
一直到這個進程退出後才能被Docker
識別服務結束。
Docker
服務的生命周期不同於使用服務角度的聲明周期,
從使用服務的角度看,有這麽幾個關鍵點
- 服務的主進程啟動
- 服務開始向外提供服務
- 服務向外提供服務
- 服務終止向外提供服務
- 服務的主進程結束
然後結合Docker
服務的生命周期的關鍵點一起看
entrypoint
或者command
進程啟動- 若幹啟動輔助進程啟動到結束
- 服務的主進程啟動
- 服務開始向外提供服務
- 服務向外提供服務
- 服務終止向外提供服務
- 服務的主進程結束
- 若幹結束輔助進程啟動到結束
entrypoint
或者command
Docker
可以識別entrypoint
或者command
進程的啟動和結束,
但是裏面的細節就很難識別了。
守護進程daemon
在啟動服務的過程中還有一個容易混淆的地方————守護進程daemon
, Linux
中的服務往往以守護進程daemon
的形式出現。
所有守護進程的啟動腳本,都放在/etc/init.d
目錄下面。 init
進程的主要任務,就是逐一運行這些腳本。
在Linux
中一個守護進程的父進程是init
進程,
因為守護進程真正的父進程在fork
出子進程後就先於子進程exit
退出了,
所以守護進程是一個由init
繼承的孤兒進程。
這種工作方式有兩個基本的用途。
- 用於啟動的父進程的運行權限較高,
fork
子進程的同時可以設置子進程運行於不同的環境,例如不同的用戶。 - 用於啟動的父進程在
init
中是串行的,而且可以依循一定的順序,
在fork
具體提供服務的子進程後,父進程退出,init
運行下一個守護進程的啟動的父進程,
下一個守護進程的啟動的父進程就可以使用前面的守護進程的子進程提供的服務了。
這是和本文所討論的相同的場景。
免責聲明:本段並非專門的Linux
技術分析,僅用於介紹相關知識。
從守護進程的角度看Docker Compose
既然Docker Compose
中按照順序啟動服務的情況和守護進程類似,
那麽能不能使用相同的機制呢?
從技術上沒問題,如果所有的服務都采用和守護進程類似的機制,例如fork
出子進程,
或者使用&
運行後臺進程。總之,只要保證主進程退出,
就可以被Docker Compose
識別了。
這種方式看上去很美,不過就嚴格限制了服務的靈活性。
猜測,僅僅是猜測,Docker
沒有找到盡善盡美的解決方案,所以沒有提供這個功能。
Docker
的解決方案
Docker
沒有提供直接的解決方案,不過也並且給出了一篇參考文章:
https://docs.docker.com/compose/startup-order/
這篇文章很神奇,神奇之處不是提出了什麽奇思妙想,
神奇之處在於點踩的人數遠遠多於點贊的人數,
今天(2018年4月3日)的數據是172踩53贊。竟然達到了3倍之多!
這種情況在官方文檔中是非常罕見的。
不過我也要贊一下,不是贊這篇文章,
而是為Docker
公司把這麽一個數字暴露出來的勇氣點贊。
文章內容就不討論了,思路類似。
思路
既然官方給出的思路是服務之間檢測,那麽檢測端口是一個方法,而且是非侵入式的。
不過當另一個服務並沒有提供端口,例如僅僅是若幹操作環節中的一個步驟,
就不能檢查端口了,此時就需要借助其他的方式。
前置的服務設置一個標誌,後續的服務檢查這個標誌。
從這個角度思考,借助文件系統檢查時間戳可能是最簡單的方法之一。
- 借助
volume
在不同的服務之間共享卷 - 前置服務完成啟動的時候
touch
一個文件作為標記 - 後置服務不停的檢查這個文件
具體涉及到兩個技術細節。
- 由於服務會重用之前的卷,因此不能僅檢查文件是否存在,
還需要比對文件的時間戳是否晚於當前服務的啟動時間 - 當前服務的啟動時間不能使用
uptime
,這個是主機的啟動時間,
而是比對 /proc/1/cmdline 這個文件的時間戳
代碼
https://github.com/huzhenghui/Docker-Compose-Startup-Order
version: "3"
services:
service_1:
image: ubuntu
volumes:
- Docker-Compose-Startup-Order:/Startup-Order
command: |
bash -c ‘
echo Service 1 Start;
sleep 10;
echo Service 1 Up;
touch /Startup-Order/service_1;‘
service_2:
image: ubuntu
volumes:
- Docker-Compose-Startup-Order:/Startup-Order
command: |
bash -c ‘
while [[ ! -f /Startup-Order/service_1 || /Startup-Order/service_1 -ot /proc/1/cmdline ]]; do sleep 1; done;
echo Service 2 Start;
sleep 10;
echo Service 2 Up;
touch /Startup-Order/service_2;‘
service_3:
image: ubuntu
volumes:
- Docker-Compose-Startup-Order:/Startup-Order
command: |
bash -c ‘
while [[ ! -f /Startup-Order/service_2 || /Startup-Order/service_2 -ot /proc/1/cmdline ]]; do sleep 1; done;
echo Service 3 Start;
sleep 10;
echo Service 3 Up;‘
volumes:
Docker-Compose-Startup-Order:
結果
PS C:\Users\huzh\OneDrive\Docker\Docker-Compose-Startup-Order> docker-compose up
Starting dockercomposestartuporder_service_1_1 ...
Starting dockercomposestartuporder_service_3_1 ...
Starting dockercomposestartuporder_service_3_www.feihuanyule.com1 ... done
Attaching to dockercomposestartuporder_service_1_1, dockercomposestartuporder_service_2_1, dockercomposestartuporder_service_3_1
service_1_1 | Service 1 Start
service_1_1 | Service 1 Up
dockercomposestartuporder_service_1_1 www.feihuanyule.com exited with code 0
service_2_1 | Service 2 Start
service_2_1 | Service 2 Up
dockercomposestartuporder_service_2_1 www.feihuanyule.com exited with code 0
service_3_1 | Service 3 Start
service_3_1 | Service 3 Up
dockercomposestartuporder_service_www.gouyiflb.cn 3_1 exited with code 0
PS C:\Users\huzh\OneDrive\Docker\Docker-Compose-Startup-Order>
控制Docker Compose的啟動順序的一個思路