- 為什麼後端一定會碰到 Docker
- 什麼是 Dockerfile
- Dockerfile Format
- Docker 常用指令
- 用 .dockerignore 排除檔案
- Dockerfile 範例
- 什麼是 Docker Compose ?
- Docker Compose 與單純 Docker 指令
- 結語
為什麼後端一定會碰到 Docker
近年來容器化的概念已經相當普及(咳咳,其實我也資歷尚淺),在軟體開發的部署階段,許多人選擇使用 Docker 在 host 他們的服務,因為 Docker 可以避免環境污染,讓各個運行的服務存在於自己獨立的小空間中,不讓 OS 對部署產生影響,image 檔的運行都是基於 Docker Engine 之上的。
扯遠了,簡單來說,Docker 會是目前部署服務的首選,關於 Docker 的基礎概念,可以參考這篇 Docker 基礎概念與實作(Basic Docker concept and hands on)。
什麼是 Dockerfile
Dockerfile 是一個文字檔案,裡頭包含了開發人員希望在 build image 時所依序執行的指令,像是用 RUN
來執行特定指令,或是用 COPY
複製檔案... 等等。
當你想要把目前的服務打包成一個 image(可以想像成光碟片)給別人用,就需要寫一份 Dockerfile,Docker 會依照 Dockerfile 裡頭的指令逐段執行,build 出一個 image。
至於什麼是 image,或是關於 Docker 的基礎概念,還是推薦參考 Docker 基礎概念與實作(Basic Docker concept and hands on)
Dockerfile Format
在 Dockerfile 中,每一個指令的形式都會是指令配上參數,類似下方範例
INSTRUCTION arguments
例如:
WORKDIR /code
或者是
COPY poetry.lock pyproject.toml /code/
而 Dockerfile 的規定中,第一個指令必須要為 FROM
, FROM
的作用類似 OOP 中的 inherit,使用 FROM
時所帶的參數就是接著要 build 的 image 的 parent image。
Docker 常用指令
以下是一些最常用的 Docker 指令
container 相關
下面都是很常用和 container 相關的指令,而 -d
代表跑在背景
docker run -d --name <container-name> <image-name> # 運行 container
docker ps # 列出運行中的 container
docker ps -a # 列出所有 container
docker stop <container-name> # 停止運行
docker rm <container-name> # 刪除
image 相關
docker build -t <image-name>:<tag> . # 建立 image
docker images # 列出所有 image
docker rmi image-name # 刪除 image
操作相關
docker exec -it
這個指令很常用,因為有時候會進去 container 裡面查東西,有些 container 是 unbutu base 的 image,下了指令後就像進去 server 裏頭操作一樣。
docker exec -it <container-name> /bin/bash # 進入 container 的 terminal
docker logs container-name # 查 log
docker cp <file-name> <container-name>:</path> # 從 local 複製檔案到 container
用 .dockerignore 排除檔案
和 git 相同, Docker 也有一個自己定義的 ignore file 叫做 .dockerignore
,如果被放入的檔案名稱會在 build image 時被排除在外。
Dockerfile 範例
開發上常用 FastAPI,所以直接用來舉例,下方的 Dockerfile 內容就像我們在 local 端跑起來一樣,安裝套件,運行服務,只是將這些過程變為寫在 Dockerfile 裡。
Docker 在 build image 時,就是按照這份檔案裡的指令一步一步去做,然後用最後得到的結果作為映像檔。
Python FastAPI Dockerfile 範例
# 使用 python image 作為 container 的基底
FROM python:3.10-slim
# 設定環境變數
ENV POETRY_VERSION=1.4.1
# 執行指令 安裝套件
RUN pip install "poetry==$POETRY_VERSION"
# 設定 /code 為工作目錄
WORKDIR /code
# 將檔案複製到工作目錄下
COPY poetry.lock pyproject.toml /code/
# 執行指令 讓 poetry 不要建立虛擬環境的資料夾
RUN poetry config virtualenvs.create false && poetry install
# 將專案複製到工作目錄
COPY . /code
# 開通 8000 port
EXPOSE 8000
# 執行指令 運行專案
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
什麼是 Docker Compose ?
Docker Compose 是一個用於定義和執行多個 container 的工具。用 YAML 檔案來配置你的服務,簡單一句指令就可以同時啟動多個 conatiner。
通常 docker compose 的 yaml 檔會命名為 docker-compose.yml
,此時如果要一個指令跑起來,只需要
docker-compose up -d
就這麼一個指令,可以同時啟動多個 container。
如果要停止的話
docker-compose down
如果說檔案名稱不是取作預設的 docker-compose.yml
,也可以用 -f
指定
docker-compose up -d -f <docker-compose-dev.yml>
使用 Docker Compose 的優點在於:
- 配置集中管理
- 服務間依賴關係明確定義
- 環境一致性
Docker Compose 與單純 Docker 指令
使用 docker compose 其實等同於下多個 docker 指令,舉例下方的指令
docker network create myapp-network
docker run -d --name db --network myapp-network -e POSTGRES_DB=myapp postgres:13
docker run -d --name backend --network myapp-network -p 8000:8000 myapp-backend
docker run -d --name frontend --network myapp-network -p 3000:80 myapp-frontend
如果寫成 docker compose 的形式, yaml 檔會長這樣
version: '3'
services:
db:
image: postgres:13
environment:
- POSTGRES_DB=myapp
backend:
build: ./backend
ports:
- "8000:8000"
depends_on:
- db
frontend:
build: ./frontend
ports:
- "3000:80"
depends_on:
- backend
結語
現在後端的工作內容高機率包含了部署,然而相較於傳統繁瑣的部署方式,Docker 大幅的簡化了這項流程,只要 image build 好之後,在有安裝 Docker 的主機上就可以一個指令將服務跑起來,幾乎不用擔心環境問題。
除了服務部署,在 local 端開發想要測試,都可以先找有沒有現成官方 image 可以 pull 下來直接用,就不用再另外安裝了,實在是很值得碰的技術,推薦一定要學起來用。