Docker 的優勢非常明顯,尤其是對於開發者來說,它提供了一種全新的軟件發布機制。也就是說使用 docker 鏡像作為軟件產品的載體,使用 docker 容器提供獨立的軟件運行上下文環境,使用 docker hub 等提供鏡像的集中管理,這其中最重要的是使用 Dockerfile 定義容器的內部行為和關鍵屬性來支持軟件運行。但是實際的生產環境往往需要定義數量龐大的 docker 容器,並且容器之間具有錯綜復雜的聯系。手動的記錄和配置這些復雜的容器關系,不僅效率低下而且容易出錯。所以,我們迫切需要一種像 Dockerfile 定義 docker 容器一樣能夠定義容器集群的編排和部署工具。於是,Docker Compose 出現了(其實應該說 Fig 出現了,docker 收購了 Fig 並改名為 compose)。
Dockerfile 重現一個容器,compose 則用來重現容器的集群。
說明:本文的演示環境為 ubuntu 16.04。
編排和部署
編排(orchestration)
編排指根據被部署的對象之間的耦合關系,以及被部署對象對環境的依賴,制定部署流程中各個動作的執行順序,部署過程所需要的依賴文件和被部署文件的存儲位置和獲取方式,以及如何驗證部署成功。這些信息都會在編排工具中以指定的格式(比如配置文件或特定的代碼)來要求運維人員定義並保存起來,從而保證這個流程能夠隨時在全新的環境中可靠有序地重現出來。
部署(deployment)
部署是指按照編排所指定的內容和流程,在目標機器上執行環境初始化,存放指定的依賴文件,運行指定的部署動作,最終按照編排中的規則來確認部署成功。
所以說,編排是一個指揮家,他的大腦里存儲了整個樂曲此起彼伏的演奏流程,對於每一個小節每一段音樂的演奏方式都了然於胸。而部署就是整個樂隊,他們嚴格按照指揮家的意圖用樂器來完成樂譜的執行。最終,兩者通過協作就能把每一位演奏者獨立的演奏通過組合、重疊、銜接來形成高品位的交響樂。這也是 docker compose 要完成的使命。
Compose 原理
筆者在前文《Docker Compose 簡介》中演示了官方的示例,本文不再贅述,接下來我們去探索 compose 的工作原理。先來了解兩個 compose 中常常提及的概念:
project
通過 docker compose 管理的一個項目被抽象稱為一個 project,它是由一組關聯的應用容器組成的一個完整的業務單元。簡單點說就是一個 docker-compose.yml 文件定義一個 project。
我們可以在執行 docker-compose 命令時通過 -p 選項指定 project 的名稱,如果不指定,則默認是 docker-compose.yml 文件所在的目錄名稱。
service
運行一個應用的容器,實際上可以是一個或多個運行相同鏡像的容器。可以通過 docker-compose up 命令的 --scale 選項指定某個 service 運行的容器個數,比如:
$ docker-compose up -d --scale redis=2
了解了上面的基本概念之后,讓我們一起看看 compose 的一次調用流程:
右上角的 docker-compose 定義了一組 service 來組成一個 project,通過 docker-compose.yml 中 service 的定義與 container 建立關系(service 與容器的對應關系),最后使用 container 來完成對 docker-py(Python 版的 docker client) 的調用,向 docker daemon 發起 http 請求。注意,這里的 project, service 和 container 對應的都是 docker-compose 實現中的數據結構。下面讓我們結合上圖來介紹 docker-compose 工作的大致流程。
首先,用戶執行的 docker-compose up 命令調用了命令行中的啟動方法,功能非常簡單。一個 docker-compose.yml 文件定義了一個 project,docker-compose up 提供的命令行參數則作為這個 project 的啟動參數交由 project 模塊處理。
然后,如果當前宿主機已經存在與該應用對應的容器,docker-compose 則進行行為邏輯判斷。如果用戶指定可以重新啟動已有服務,docker-compose 就會執行 service 模塊的容器重啟方法,否則就直接啟動已有容器。這兩種操作的區別在於前者會停止舊的容器,創建並啟動新的容器,並把舊容器移除掉。在這個過程中創建容器的各項自定義參數都是從 docker-compose up 命令和 docker-compose.yml 中傳入的。
接下來,啟動容器的方法也很簡潔,這個方法中完成了一個 docker 容器啟動所需的主要參數的封裝,並在 container 模塊執行啟動。
最后,contaier 模塊會調用 docker-py 客戶端來執行向 docker daemon 發起創建容器的 POST 請求。
由此可見 docker-compose 工作的整體流程非常清晰、簡潔!
重新啟動 services
前面我們提到當前宿主機已經存在與該應用對應的容器,docker-compose 會進行判斷並決定是否重新啟動已有服務。下面我們就通過 demo 來演示幾個常見的場景(我們依然使用前文中提到的官方 demo)。
強制 recreate
Recreate 就是刪除現有的容器並且重新創建新的容器,為 docker-compose up 命令指定 --force-recreate 選項可以強制 recreate 容器:
創建個別容器
如果應用中的個別 service 對應的容器被刪除了,docker-compose up 命令會新建相關的容器:
啟動個別容器
與上面類似,如果應用中的個別 service 對應的容器被停止(stop)了,docker-compose up 命令會重新啟動相關的容器:
總結
Docker-compose 總體上給人的感覺是並不復雜。本文只是從概覽的角度梳理了一遍 docker-compose 的整體執行流程,主要目的是理解它的工作原理。至於相關的使用技巧等細節,筆者會在接下來的文章中進行介紹。
參考:
《Docker 容器和容器雲》