深入淺出 Dockerfile 與 Docker Compose
Dockerfile 與 Docker Compose 是 Docker 兩個最重要的概念,也是初學者最容易卡關的地方,本文以 使用需求
為觀點解釋這兩者的差異。
Image 與 Container
在解釋 Dockerfile 與 Docker Compose 之前,先複習兩個更基礎的概念:
Image
將 Service 打包成 image,通常會從 Docker Hub 下載官方的 image 使用,也可以根據官方的 image 再包成自己的 image,或者完全自行製作自己的 image。
Container
Image 類似 template,基於壓好的 image 產生隔離的執行環境,稱之為 container。
一般會以 Microservice 方式使用 container,也就是會同時啟動多個 container 形成 service。
Image 與 container 都會佔硬碟空間,尤其 container 若執行完畢後,雖然 container 從記憶體釋放,但在硬碟仍然會留一份 container 的
屍體
,需要自行刪除,或者在docker run
時增加--rm
參數,在執行完自行從硬碟刪除 container
Dockerfile
實務上我們會從 Docker Hub 下載官方的 Docker Image 使用,但官方的 image 功能可能過於陽春,我們可能想根據自己的需求,再安裝其他的 app,最後再打包成自己的 Docker image。
在傳統 VM 時代,要打造自己的 image,必須用 export 方式,但這樣有幾個缺點:
- Image 可能非常龐大
- 安裝步驟無法進 git 版控
Docker 以 Infrastructure as Code
概念,將 infrastructure 以 code 形式描述:
Dockerfile
1 | FROM ubuntu:latest |
以上為典型的 Dockerfile
,只需一個文字檔,就清楚描述一個 Docker image。
以前若要在 Ubuntu 安裝其他 dependency,只能透過 Bash 安裝一堆 package,最後再 export 成 image,但 image 可能很龐大,且安裝步驟也無法 git 版控。
但透過 Dockerfile 之後,整個新的 Ubuntu 都是以 code 形式描述,你只要將 Dockerfile
給其他人,對方只要使用 docker build
就能自行根據 Dockerfile
建立 Docker image,最後再根據 docker run
執行客製化過的 Ubuntu container。
Dockerfile
檔案很小,只是文字檔而已- 由於
Dockerfile
是文字檔,可以 git 版控
簡單的說,
Dockerfile 就是描述如何產生客製化的 image
,只是採用 code 形式描述
Docker Compose
實務上一個服務,一定由眾多 service 共同運作。如一個典型的 Web 服務,最少就必須有
.NET Core Runtime + Nginx + Redis + PosgreSQL
4 個 service 一起運行,若只使用 docker run
,則勢必寫 Bash 來管理 4 個 service,還必須考慮:
- 4 個 service 必須在同一個虛擬 network 下
- 4 個 service 的啟動順序
… 等問題。
Docker 為此提出 Docker Compose 概念,在 docker-compose.yml
檔描述各 service 間的參數與關係:
docker-compose.yml
1 | version: "3" services: netcore: image: microsoft/dotnet container_name: MyNETCore volumes: - ${NETCORE_HOST_DIR}:/code/ tty: true networks: - netcore-dev depends_on: - postgres postgres: image: postgres container_name: MyPostgres volumes: - ${POSTGRES_HOST_DIR}/data:/var/lib/postgresql/data expose: - "5432" ports: - "${POSTGRES_PORT}:5432" environment: - POSTGRES_DB=${POSTGRES_DB} - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} networks: - netcore-dev networks: netcore-dev: |
以上為典型的 docker-compose.yml
,只需一個文字檔,就清楚描述 .NET Core Runtime 與 PostgreSQL 兩個 service,將 docker-compose.yml
拿到任何裝有 Docker 的電腦,都成重現 .NET Core + PostreSQL 的環境,也就是 Infrastructure as Code
。
docker-compose.yml
檔案很小,只是文字檔而已- 由於
docker-compose.yml
是文字檔,可以 git 版控
間單的說,
docker-compose.yml 就是 container 的管理文件
,只是採用 code 形式描述
FAQ
Q:
Dockerfile
與docker-compose.yml
看起來很像,都是在描述 server,有什麼差別呢 ?
Dockerfile
是用來描述 image,也就是如何產生客製化的 image,通常用來安裝 package,將檔案複製進 image 用docker-compose.yml
是用來描述 container,也就是管理一個以上的 container,彼此串連,把同一組架構寫在一起,通常用來設定 container 參數,設定 container 的網路,設定 container 啟動順序或 service 的環境變數 … 等
Q:
Dockerfile
是用來描述 image,docker-compose.yml
是用來描述 container,但若我們還有客製化的邏輯
該怎麼辦 ?
若在使用 Dockerfile
或 docker-compose.yml
時,還必須搭配額外的 if else
或 for loop
邏輯,就必須再搭配 Bash。
Dockerfile
與 docker-composer.yml
只是設計用來描述 infrastructure
,並不是描述 邏輯
。
Q:單一 service 也適用
docker-compose.yml
嗎 ?
由於實務上,儘管是單一 service,如只為了使用 PostgreSQL,也必須在 docker run
搭配一堆參數,但人的腦容量有限,很難記住所有的參數,與其寫在 Bash,建議寫在 docker-compose.yml
,統一由 docker-compose
管理。
Conclusion
Dockerfile
用來描述 image;而docker-compose.yml
用來描述 container- 若
Dockerfile
與docker-compose.yml
還無法達到客製化的需求,就必須搭配 Bash Dockerfile
與docker-compose.yml
都是文字檔,因此檔案很小,也容易 git 版控,實現Infrastructur as Code
理想。- 儘管只有一個 container,也建議使用
docker-compose.yml