---
# System prepended metadata

title: Docker 進階筆記

---

# 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：讀取或使用資料的前端或客戶端