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,也就是會同時啟動多個 service,每個 service 以 container 形式啟動。
Dockerfile
實務上我們會從 Docker Hub 下載官方的 Docker image 使用,但官方的 image 功能可能過於陽春,我們可能想根據自己的需求,再安裝其他的 app / package / dependency
,最後再打包成自己的 Docker image。
在傳統 VM 時代,要打造自己的 image,必須用 export 方式,但這樣有幾個缺點:
- Image 可能非常龐大
- 安裝步驟無法進 git 版控
Docker 以 Infrastructure as Code
概念,將 infrastructure 以 code 形式描述:
dockerfile
FROM node:latest
RUN yarn global add @vue/cli
以上為典型的 dockerfile
,只需一個文字檔,就清楚描述一個 Docker image。
以前若要在 Linux 安裝其他 dependency,只能透過 Bash 安裝一堆 package,最後再 export 成 image,但 image 可能很龐大,且安裝步驟也無法 git 版控。
但透過 dockerfile 之後,整個新的 Linux 都是以 code 形式描述,你只要將 dockerfile 給其他人,對方只要使用 docker build
就能自行根據 dockerfile
建立 Docker image,最後再以 docker run
執行客製化過的 Ubuntu container。
dockerfile
檔案很小,只是文字檔而已- 由於
dockerfile
是文字檔,可以 git 版控
簡單的說,
dockerfile 就是描述如何產生客製化的 image
,只是採用 code 形式描述
Docker Compose
實務上後端服務,一定由眾多 service 共同運作。如一個典型的 Web 服務,以 .NET Core 為例,最少就必須有
.NET Core Runtime + Nginx + Redis + PosgreSQL
4 個 service 一起運行,若只使用 docker run
,則勢必寫 Bash 來管理 4 個 service,還必須考慮:
- 4 個 service 必須在同一個虛擬 network 下
- 4 個 service 哪些檔案需與 host 共用
- 4 個 service 的啟動順序
… 等問題。
Docker 為此提出 Docker Compose 概念,在 docker-compose.yml
檔描述各 service 間的參數與關係:
docker-compose.yml
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 / dependency,將檔案複製進 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 或 Makefile。
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 或 Makefile 處理邏輯部分 Dockerfile
與docker-compose.yml
都是文字檔,因此檔案很小,也容易 git 版控,實現Infrastructure as Code
理想。- 儘管只有一個 container,也建議使用
docker-compose.yml