1. 程式人生 > >控制Docker Compose的啟動順序的一個思路

控制Docker Compose的啟動順序的一個思路

比對 Go 另一個 lB 下一個 生命 IT 官方文檔 ima

  • 起源
  • 守護進程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服務的生命周期不同於使用服務角度的聲明周期,
從使用服務的角度看,有這麽幾個關鍵點

  1. 服務的主進程啟動
  2. 服務開始向外提供服務
  3. 服務向外提供服務
  4. 服務終止向外提供服務
  5. 服務的主進程結束
服務的主進程啟動服務開始向外提供服務服務向外提供服務服務終止向外提供服務服務的主進程結束 從使用服務的角度看Docker服務的生命周期服務服務客戶端客戶端主進程啟動服務開始向外提供服務請求響應服務終止向外提供服務主進程結束

然後結合Docker服務的生命周期的關鍵點一起看

  1. entrypoint或者command進程啟動
  2. 若幹啟動輔助進程啟動到結束
  3. 服務的主進程啟動
  4. 服務開始向外提供服務
  5. 服務向外提供服務
  6. 服務終止向外提供服務
  7. 服務的主進程結束
  8. 若幹結束輔助進程啟動到結束
  9. entrypoint或者command
    進程結束
entrypoint或者command進程啟動若幹啟動輔助進程啟動到結束服務的主進程啟動服務開始向外提供服務服務向外提供服務服務終止向外提供服務服務終止向外提供服務若幹結束輔助進程啟動到結束entrypoint或者command進程結束 結合Docker的生命周期看服務的生命周期Docker容器Docker容器啟動輔助進程啟動輔助進程結束輔助進程結束輔助進程服務主進程服務主進程服務工作進程服務工作進程客戶端客戶端管理端管理端啟動啟動結束啟動啟動服務開始向外提供服務請求響應關閉關閉服務終止向外提供服務結束結束啟動結束結束

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的啟動順序的一個思路