# Docker 進階筆記 ## 使用 Dockerfile 開放埠口(EXPOSE) `EXPOSE` 指令是在 Dockerfile 中用來宣告容器在執行時會使用的埠口,例如: * `EXPOSE 80` 或 `EXPOSE 80/tcp`:開放 TCP 協定的 80 埠。 但光是使用 `EXPOSE` 並不會讓主機直接存取該埠口,還需在執行容器 `container` 時搭配: * `-p 主機埠:容器埠`:例如 `-p 8080:80`,將主機 8080 映射到容器內部 80。 * `-P`:自動將所有 `EXPOSE` 的埠口隨機對應主機上的可用埠。 常用指令有: * `docker run -P mycontainer`:隨機對應埠。 * `docker ps -a`:列出所有容器(包含停止中的)。 * `docker inspect <容器ID>`:查看容器詳細資訊(包含埠口對應情形)。 以下是一個完整的 Dockerfile 範例,使用 `python:3.11` 映像,搭配 `ENTRYPOINT` 與 `EXPOSE` 指令: ```dockerfile= # 使用 Python 3.11 官方映像 FROM python:3.11 # 宣告容器將會使用 5000 埠(例如 Flask 預設埠) EXPOSE 5000 # 設定容器啟動時執行的預設指令 ENTRYPOINT ["python3", "-m", "http.server", "5000"] ``` 這個範例會在容器啟動時自動執行一個簡單的 HTTP 伺服器(預設分享 / 目錄內容),並開放 5000 埠供主機連線。可以用以下指令來建立與執行這個映像: ```bash= docker build -t my-python-server . docker run -p 8080:5000 my-python-server ``` ## Docker 網路管理 Docker 支援多種網路模式(driver),例如: * `bridge`(預設) * `host` * `none` 相關指令包括: * `docker network ls`:列出所有網路。 * `docker network create <名稱>`:建立新網路。 * `docker network rm <名稱>`:移除網路。 * `docker run --network <網路名稱> ubuntu`:將容器連接到指定網路。 * `docker network inspect <網路名稱>`:查看網路詳細設定。 ## 最佳化 Docker 映像檔 目標是節省頻寬、提高安全性與可維護性。建議做法包括: * 拆分容器功能,使其保持單一職責。 * 使用最小基礎映像。 * 定期更新,移除不必要檔案。 查看映像大小: * `docker images` * `docker image inspect <映像ID或名稱>` ## 映像檔的層(layers) Docker 映像是由一層層的檔案系統所組成。這些層會被快取並重複使用,因此指令的順序會影響建置效率。 使用 `jq` 工具可以更清楚地查看層資訊,例如: * `docker image inspect <ID> | jq '.[0] | .RootFS'` * `docker image inspect <ID> | jq '.[0] | {LayerCount: (.RootFS.Layers | length)}'` ## 多階段建置(Multi-Stage Build) 可將建置與執行環境拆分,減少最終映像檔大小,例子如下: 第一階段:使用 Golang 建置應用程式 第二階段:只保留執行檔到最小映像(如 scratch) 流程: 1. 使用 `FROM golang:1.21 AS gobuild` 建立建置階段 2. 建置完成後用 `FROM scratch` 建立極簡執行映像 3. 用 `COPY --from=gobuild` 將檔案複製到最終映像 這樣能確保最終映像只含應用程式執行檔,大小極小、安全性高。 ## 多平台建置(Multi-Platform Build) 所謂「多平台」是指支援針對不同作業系統與處理器架構(CPU 架構)來建置 Docker 映像檔。 常見的作業系統與架構類型包括: * **作業系統(OS)**: * `linux` * `windows`(需特別環境支援) * 處理器架構(CPU): * `amd64`(又稱 x86_64,常見於桌機與伺服器) * `arm64`(常見於 Apple M1/M2、Raspberry Pi) * `arm/v7`(舊款 Raspberry Pi) ### 使用 `--platform` 指定建置目標平台 在 `docker buildx` 建置時,可以使用 `--platform` 參數來指定要建置的目標平台,例如: ```bash= docker buildx build --platform linux/amd64,linux/arm64 -t myapp . ``` 這會同時為 `amd64` 與 `arm64` 平台建置對應的映像檔。 > 若使用 `docker build` 而非 `buildx`,則無法支援多平台建置。請務必使用 `docker buildx`。 建置步驟說明: 1. 使用 `ARG TARGETOS TARGETARCH` 取得目標平台資訊。 2. 在程式編譯階段使用 `env GOOS=$TARGETOS GOARCH=$TARGETARCH go build` 建置。 完整範例(Golang 多平台建置) ```dockerfile= # 第一階段:針對不同平台進行交叉編譯 FROM --platform=$BUILDPLATFORM golang:1.21 AS build # 設定工作目錄 WORKDIR /src # 複製原始碼 COPY . . # 取得目標平台資訊 ARG TARGETOS ARG TARGETARCH # 使用指定平台參數進行交叉編譯 RUN env GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /final/app . # 第二階段:建立輕量執行映像 FROM alpine # 複製交叉編譯後的執行檔 COPY --from=build /final/app /bin # 預設啟動應用程式 ENTRYPOINT ["/bin/app"] ``` 這樣就能產生可支援多平台的 Docker 映像檔,適用於不同硬體環境,如 x86 與 ARM 架構。 ## Docker Compose 工具 Compose 是用來定義與管理多容器應用的工具。主要使用 `docker-compose.yaml` 或 `compose.yaml` 文件。範例: ```yaml= services: webapp: image: webapp ports: - "8000:5000" redis: image: redis:alpine ``` 常用指令: * `docker compose up`:啟動所有服務 * `docker compose down`:停止並清除 * `docker compose ls`:查看現有專案狀態 * `-d`:背景執行(detached 模式) YAML 檔結構常見區塊有: * services(服務) * volumes(資料卷) * networks(網路) * secrets(機密設定) * configs(其他組態) ## 除錯與啟動順序控制 使用 depends_on 可以定義容器之間的依賴關係。 此外可用 condition 指定依賴容器何時算啟動成功,例如: * `service_started` * `service_completed_successfully` * `service_healthy` 需要在容器中定義 healthcheck 才能使用 service_healthy。 除錯工具包括: * `docker compose logs`:查看日誌 * `docker compose top`:查看資源狀況 ## 資料共享與 Volume 掛載 在 `docker run` 中使用 `-v` 選項可將主機目錄掛載到容器中: `docker run -v /主機目錄:/容器目錄 myimage` 在 Compose 檔案中可以寫成: ```yaml= services: app: volumes: - ./data:/data ``` 此功能可讓多個容器共用資料,例如: * 容器 1:資料儲存或 API 提供者 * 容器 2:讀取或使用資料的前端或客戶端