Contents

Docker Compose 厲害了!一次幫你蓋多個containers

前陣子去圖書館借了一本圖解Docker&Kubernetes的書,看到第七章專門討論Docker Compose,加上練習的專案也大量使用到這個技術,因此花點時間來看一下官方的Documentation。

Docker Compose 概覽

Docker Compose 這工具是用來定義以及運行多個容器之應用程式的工具,是解鎖流暢畫高效開發與部署經驗的工具。

Compose簡化了整個application stack的控制,使用一個YAML檔簡化了服務、網路、volume的管理。用單一的指令就能建立並啟動配置檔案裏面所有的服務。

Compose可以在production、staging、開發、測試、CI Workflow等各種環境使用,也有用來管理應用程式整個生命週期的指令:

  • Start, stop, rebuild 服務
  • 檢視 running service 的狀態
  • 對 running service 做日誌串流
  • 對某個服務執行一個one-off指令

Docker Compose 主要優點

  • 簡化控制性: 簡化了調度多個服務的複雜任務,管理或複製app環境更容易
  • 有效合作: docker compose 配置檔容易共享,開發、運營之間協作更順利
  • 快速應用程式開發: Compose會將創建容器的配置檔緩存起來,如果重啟了沒有做任何異動的服務,Compose會重複利用既有的容器,這樣要在environment做變更會很快
  • 不同環境的可攜性: Compose 檔支援變數,可以用這些變數給不同環境或不同user客製化你的組成
  • 廣泛的社群與支援: Docker Compost 有活躍的社群,提供豐富的資源、教學與支援

Docker Compose 常用 use cases

開發環境

Docker Compose 檔可以記錄並且配置應用程式全部的服務所需的依賴 (例如資料庫、隊列、緩存、Web service API等等)。可以用 docker compose up 一次建立並啟動每個依賴所需的容器。

Compose可以把一個多頁的「開發者新手指南」濃縮成一個單一且機器可讀的compose file外加幾個指令。

自動化測試環境

Automated test suite 是CICD程序中非常重要的一環。自動化end-to-end testing需要一個用來跑測試的環境。Compose提供一個很方便的方式,為你的test suite提供一個可以創建且用完銷毀的隔離測試環境。

docker compose up -d
./run_tests
docker compose down

Docker compose 演變史

這裡會比較 Compose V1Compose V2 之間的file formats 以及主要的差異,也會簡短介紹Docker Compose CLI的發展歷史。

  • Compose V1
    • 不再支援了 (嗯? 但我們專案還是用docker-compose)
    • 用Python寫的
    • 使用 version top-level element
    • 使用 docker-compose 命令列語法
      Compose file format 3.x
      Compose file format 2.x
      Compose file format 1
      
  • Compose V2
    • 用GO寫的 (GoLang好熱門)
    • Optional: compose build/deploy/develop spec
      Compose Specification
        ├ Compose Build Specification   (optional)
        ├ Compose Deploy Specification  (optional)
        └ Compose Develop Specification (optional)
      
    • 這個網址定義了Compose各個區塊的top-level element寫法,包括version, name, services, networks, volumes, configs, secrets

Docker Compose CLI 版本

Docker Compose command-line binary 的第一個版本首先是2014年發布,用Python寫的,用docker-compose指令調用。基本上,Compose V1 版本的compose.yml檔案裡面包括了top-level version 元素,版本的值從 2.0 ~ 3.8 (分別是2014, 2016, 2017年發布)不等。

Docker Compose command-line binary 版本二是在2020年發布,用Go寫的,調用的指令是docker compose。Compose V2在compose.yml檔案裡面忽略了version top-level element (應該就是不寫版本 version: “3.8” 的意思吧)。

Compose file 格式版本

Compose file format 1 (2014) 沒有 top-level services 標籤,這種格式的 compose 檔無法兼容於 Compose V2 的指令

Compose file format 2.x(2016) 跟 Compose file format 3.x(2017) 兩者之間很相似,但後者加入了很多針對Swarm deployments提供的新選項。

Swam deployment 是 Docker 的集群管理工具

所以為了解決Compose CLI版本混亂、compose file格式版本差異、有無使用Swarm mode的功能均等性 (feature parity),File format 2跟File format 3就被整合進了 Compose Specification (Compose V2)。

Compose V2 可以向前相容,file format 2.x跟3.x之間異動或棄用的元素應該還是可以使用。

Compose 是如何發揮作用

Docker Compose 倚賴yaml配置檔,這個檔案通常命名為compose.yaml

compose.yaml 會依照Compose Specification提供的規範來定義多容器應用程式

Services: 
- app做計算的元件,定義為services。Service是實作在平台的一個抽象概念,是同樣的container image 跟 configuration設定 run 1次~多次

Networks:

Volumes:

Configs:

Secret:

[Reference](https://docs.docker.com/compose/compose-application-model/)

Docker Compose 快速啟動

跟著這個tutorial實作,會得到

composetest
    app.py
    compose.yaml
    Dockerfile
    requirements.txt
  • Dockerfile 常用的指令:

    • FROM: set the base image for subsequent instructions
    • WORKDIR: set the working directory for any following instructions, such as RUN, CMD, COPY etc.
    • ENV: set the environment variable ‘key’ to the ‘value’
    • RUN: execute any command to create a new layer on top of the current image, and the add layer will be used in the next step
    • CMD: set the command to be executed when running a container from an image
    • COPY: copy from source to destination
    • EXPOSE: informs Docker that this container listens on the specified network ports at runtime (default is TCP)
    • ADD: 也沒有,但應該是跟掛載檔案有關吧
      • ADD --chown={} --chmod={file-permission} <src> <dest>
    • ENTRYPOINT: 配置一個當作可執行檔的container
    • LABEL: 給這個image加上metadata,也是key-value pair
    • 關於Dockerfile Instruction看一篇就好
  • 另外內文有提到,如果在 service.web 加上 develop.watch 的話,存檔的同時會同步將程式碼 Use Compose Watch

    • 但要注意的是存檔之後,後續的 docker compose 指令要加參數: docker compose watch 或者 docker compose up --watch,用以建構/啟動 app, 並開始 file watch mode

    • 異動程式碼之後存檔,可以看到 changes were detected https://hackmd.io/_uploads/SyzXDeGQ0.png

    • 刷新瀏覽器頁面就可以看到異動了

遷移到 Compose V2

從2023年7月開始,Compose V1不再接收更新,後續發布的Docker Desktop也沒支援了。

Compose V2 一開始是2020發布,Docker Desktop目前有支援的版本都有 Compose V2。這版本改善了CLI體驗,用BuildKit改善了build效能,也持續開發新的功能。

要怎麼轉換到 Compose V2

最簡單的是確保你有安裝最新版的 Docker Desktop,就綁了 Docker Engine 以及包含了Compose V2的Docker CLI platform。

Docker Desktop 的Use Compose V2設定是開啟的,所以docker compose - an alias from docker-compose

所以 Compose V1 跟 Compose V2 之間到底差在哪

docker-compose vs docker compose

Compose V2 因為整合進了 Docker CLI 平台,所以command-line syntax變成了docker compose

Docker CLI 平台提供了有一致性而且可預期的options以及flags,例如DOCKER_HOST環境變數或者--contextCLI flag

這個異動讓你能夠使用dockerroot command之下全部的共享flags。例如docker --log-level=debug --tls compose up 啟用 Docker Engine 的 debug 日誌類型,另外也確保TLS用來作為連線。

Service container names

Compose 根據 project name, service name 以及 scale/replica count 產生出容器名稱

Compose V1,單字之間用_底線分隔;而Compose V2則改用-分號隔開

底線不是DNS hostname的有效字元。所以Compose V2可以確保service container可以藉由有一致性、且可預測的hostname在網路上存取

舉例

  • -p myproject up --scale=1 svc 的docker compose指令
    • V1: myproject_svc_1
    • V2: myproject-svc-1
Command-line flags and subcommands

Compose V2支援了幾乎全部的 Compose V1 既有的 flags 以及 subcommand,所以在腳本上能夠做直接替換(drop-in replacement)

  • Compose V2 不支援:
    • 不支援docker-compose scale,要改用docker compose up --scale
    • docker-compose rm --all
  • Compose V2 有異動:
    • --compatibility
      • V1: 已棄用,根據legacy schema version遷移yaml fields
      • V2: 使用_不用-當做容器名稱的單字分詞符
    • ps --filter KEY-VALUE
      • V1: 沒有文件紀錄,允許用任意的service property做過濾
      • V2: 只能用特定的properties做過濾,例如--filter=status=running
Environments variables

Compose V1 的環境變數的行為沒有完整記載,在某些edge cases會有不一致的行為。

至於Compose V2,環境變數就包含了(1) precedence 以及(2).env file interpolation,也包含很多範例,涵蓋了像是跳脫巢狀式quotes的難題。

precedence 指的是當這個變數在很多個來源都有給值,Docker Compose 決定要先採用哪一邊定義所使用的規則 .env 檔案插值(interpolation) 是指在放環境變數的檔案插入其他值

以下要確認:

  • 專案有無多層級的環境變數覆寫,例如.env--env CLI flag同時存在
  • 任何包含跳脫sequences或者巢狀quotes的.env檔案
  • 任何包含$符號的.env檔案,這在PHP的專案尤其常見
  • 任何使用進階擴充語法的變數值,例如${VAR:?error}

豆知識 可以透過docker compose config來預覽 Compose V2之後的配置檔確實已經做參數值插入,來驗證變數的值如預期呈現

那如果我的專案用的是Compose V1會怎樣嗎

大部分的projects要遷移到Compose V2的話,並不用在compose yaml或者開發的workflow做任何的異動。

通常會建議你改用docker compose取代原本的docker-compose,這樣就可以拔掉 docker-compose外加 compatibility alias 的需求。

然而 Docker Desktop 持續有支援docker-compose alias來重新將指令導向到docker compose,以提供方便性以及更佳兼容於第三方的工具與腳本。

在切換到Compose V2之前有應注意事項嗎

  • 遷移運行中的專案
    • 在V1跟V2兩版本中,只要指令有帶up,都會比較Docker Engine的實際狀態,來重新建立服務容器,以達到預期狀態,實際狀態的對照組是compose yaml、環境變數、command-line flags。
    • 因為 Compose V1 跟 V2 兩者命名服務容器的方式不同,如果是原本由V1啟動的專案第一次改用V2的up重啟容器時,會導致重啟的container有不同的服務名稱。
    • 要注意的是,即使你用了--compatibility flag來保存原有V1的命名風格,Compose V2 首次用up指令是,還是需要重新建立由 V1啟動的服務容器,來遷移其內部狀態 (internal state)
  • 在Docker-in-Docker中使用COMPOSE V2