Docker Dockerfile 編寫指南

Docker Dockerfile 編寫指南

什麼是 Dockerfile?

Dockerfile 是一個文字檔案,包含了一系列指令用來自動化建構 Docker 映像檔。透過 Dockerfile,我們可以將應用程式及其運行環境封裝成一個可移植的映像檔,實現「一次建構,到處運行」的目標。

Dockerfile 的優勢

  • 可重現性:確保在不同環境中建構出相同的映像檔
  • 版本控制:可以像程式碼一樣進行版本管理
  • 自動化:減少手動配置,降低人為錯誤
  • 標準化:統一的建構流程和環境配置

基本指令介紹

核心指令

指令 功能 範例
FROM 指定基礎映像檔 FROM openjdk:21-jre
WORKDIR 設定工作目錄 WORKDIR /app
COPY 複製檔案到映像檔 COPY . /app
RUN 執行命令 RUN apt-get update
EXPOSE 聲明容器端口 EXPOSE 8080
CMD 容器啟動時的預設命令 CMD ["java", "-jar", "app.jar"]
ENTRYPOINT 容器啟動點 ENTRYPOINT ["java", "-jar"]

進階指令

指令 功能 範例
ARG 建構時參數 ARG JAVA_VERSION=21
ENV 環境變數 ENV NODE_ENV=production
VOLUME 聲明卷掛載點 VOLUME ["/data"]
USER 切換使用者 USER app
HEALTHCHECK 健康檢查 HEALTHCHECK CMD curl -f http://localhost:8080/health
LABEL 添加元數據 LABEL version="1.0"

完整範例:Spring Boot 應用程式

以下是一個生產級別的 Spring Boot 應用程式 Dockerfile 範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Dockerfile for Spring Boot Application with Java 21
# Multi-stage build for optimized image size

# Global build arguments (available to all stages)
ARG MAVEN_PROFILE=dev
ARG SPRING_PROFILE=dev

# Build stage
FROM maven:3.9.9-eclipse-temurin-21 AS build

# Re-declare args to make them available in this stage
ARG MAVEN_PROFILE

# Set working directory
WORKDIR /app

# Copy Maven configuration files first for better layer caching
COPY pom.xml ./
COPY mvnw ./
COPY mvnw.cmd ./
COPY .mvn ./.mvn

# Download dependencies (this layer will be cached unless pom.xml changes)
RUN mvn dependency:go-offline -B

# Copy source code
COPY src ./src

# Build the application
# Using the specified profile and skipping tests for faster build
RUN mvn clean package -DskipTests -P${MAVEN_PROFILE} -B

# Runtime stage
FROM eclipse-temurin:21-jre AS runtime

# Re-declare args to make them available in this stage
ARG SPRING_PROFILE

# Install necessary packages and create non-root user
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
tzdata \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd -g 1001 dockertest \
&& useradd -u 1001 -g dockertest -s /bin/bash -m dockertest

# Set timezone (optional, adjust as needed)
ENV TZ=Asia/Taipei

# Create log directories with proper permissions
RUN mkdir -p /var/log/dockertest /app/logs \
&& chown -R dockertest:dockertest /var/log/dockertest /app/logs

# Set working directory
WORKDIR /app

# Copy the built jar from build stage
COPY --from=build /app/target/dockertest-0.0.1-SNAPSHOT-boot.jar app.jar

# Change ownership to non-root user
RUN chown -R dockertest:dockertest /app

# Create volume mount points for logs
VOLUME ["/app/logs", "/var/log/dockertest"]

# Switch to non-root user
USER dockertest

# Expose port
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1

# Environment variables
ENV JAVA_OPTS="-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom" \
SPRING_PROFILES_ACTIVE=${SPRING_PROFILE}

# Run the application
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

# Labels for better container management
LABEL maintainer="vscodelife" \
version="0.0.1-SNAPSHOT" \
description="Spring Boot Docker Test Application" \
org.opencontainers.image.title="dockertest" \
org.opencontainers.image.description="Demo project for Spring Boot with Docker" \
org.opencontainers.image.version="0.0.1-SNAPSHOT" \
org.opencontainers.image.vendor="vscodelife"

範例解析

1. 多階段建構 (Multi-stage Build)

1
2
3
4
FROM maven:3.9.9-eclipse-temurin-21 AS build
# ... 建構階段
FROM eclipse-temurin:21-jre AS runtime
# ... 運行階段
  • 目的:分離建構環境和運行環境,減少最終映像檔大小
  • 優勢:建構工具不會包含在最終映像檔中

2. 建構參數 (Build Arguments)

1
2
ARG MAVEN_PROFILE=dev
ARG SPRING_PROFILE=dev
  • 用途:在建構時動態配置參數
  • 使用docker build --build-arg MAVEN_PROFILE=prod .

3. 層級快取優化

1
2
3
COPY pom.xml ./
RUN mvn dependency:go-offline -B
COPY src ./src
  • 策略:先複製依賴配置,再複製原始碼
  • 效果:依賴下載層可以被快取,加速後續建構

4. 安全性配置

1
2
3
RUN groupadd -g 1001 dockertest \
&& useradd -u 1001 -g dockertest -s /bin/bash -m dockertest
USER dockertest
  • 目的:避免使用 root 使用者運行應用程式
  • 好處:減少安全風險,符合最小權限原則

5. 健康檢查

1
2
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
  • 功能:定期檢查容器健康狀態
  • 參數說明
    • interval:檢查間隔
    • timeout:檢查超時時間
    • start-period:容器啟動後等待時間
    • retries:失敗重試次數

最佳實踐建議

1. 映像檔大小優化

使用多階段建構

1
2
3
4
5
6
7
8
# 建構階段 - 包含編譯工具
FROM node:18 AS build
COPY package*.json ./
RUN npm ci --only=production

# 運行階段 - 只包含運行時
FROM node:18-alpine
COPY --from=build /app/node_modules ./node_modules

選擇合適的基礎映像檔

1
2
3
4
5
6
7
8
# 一般用途
FROM ubuntu:22.04

# 最小化 (推薦)
FROM alpine:3.18

# 特定應用 (推薦)
FROM openjdk:21-jre-slim

2. 層級快取策略

正確的檔案複製順序

1
2
3
4
5
6
7
8
# 錯誤 - 任何檔案變更都會重新執行安裝
COPY . /app
RUN npm install

# 正確 - 依賴配置變更才重新安裝
COPY package*.json /app/
RUN npm install
COPY . /app

3. 安全性最佳實踐

使用非 root 使用者

1
2
3
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
USER appuser

減少攻擊面

1
2
3
4
5
6
7
# 清理不必要的套件
RUN apt-get update && apt-get install -y package \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# 只複製必要的檔案
COPY --chown=appuser:appgroup app.jar /app/

4. 環境配置

使用環境變數

1
2
3
ENV NODE_ENV=production \
PORT=3000 \
LOG_LEVEL=info

支援配置覆寫

1
2
3
# 允許運行時覆寫
ENV JAVA_OPTS="-Xmx512m" \
SERVER_PORT=8080

常見問題與解決方案

Q1: 映像檔太大怎麼辦?

解決方案:

  1. 使用 Alpine 基礎映像檔
  2. 實施多階段建構
  3. 清理暫存檔案和快取
  4. 使用 .dockerignore 排除不必要檔案
1
2
3
4
5
6
# .dockerignore
node_modules
*.log
.git
Dockerfile
README.md

Q2: 建構速度太慢?

解決方案:

  1. 優化層級順序,善用快取
  2. 使用 BuildKit
  3. 並行化建構步驟
1
2
3
# 啟用 BuildKit
export DOCKER_BUILDKIT=1
docker build --progress=plain .

Q3: 容器啟動失敗?

除錯步驟:

1
2
3
4
5
6
7
8
# 檢查映像檔
docker images

# 互動式執行
docker run -it --entrypoint /bin/bash your-image

# 檢查日誌
docker logs container-name

進階技巧

1. 使用 BuildKit 功能

快取掛載

1
2
3
# 使用快取掛載加速套件安裝
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y package

密鑰掛載

1
2
3
# 安全地使用密鑰
RUN --mount=type=secret,id=api_key \
curl -H "Authorization: $(cat /run/secrets/api_key)" api.example.com

2. 條件建構

1
2
3
4
5
6
7
8
ARG BUILD_ENV=development

# 根據環境安裝不同套件
RUN if [ "$BUILD_ENV" = "development" ] ; then \
npm install --include=dev ; \
else \
npm install --omit=dev ; \
fi

3. 健康檢查最佳實踐

1
2
3
4
5
6
# 自定義健康檢查腳本
COPY healthcheck.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/healthcheck.sh

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD /usr/local/bin/healthcheck.sh

建構和部署

建構映像檔

1
2
3
4
5
6
7
8
# 基本建構
docker build -t myapp:latest .

# 指定建構參數
docker build --build-arg MAVEN_PROFILE=prod -t myapp:prod .

# 使用特定 Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .

推送到 Registry

1
2
3
4
5
# 標記映像檔
docker tag myapp:latest registry.example.com/myapp:latest

# 推送
docker push registry.example.com/myapp:latest

運行容器

1
2
3
4
5
# 基本運行(背景執行)
docker run -d -p 8080:8080 myapp:latest

# 臨時運行(容器停止後自動刪除)
docker run --rm -p 8080:8080 myapp:latest

參數說明:

  • -d:在背景執行容器
  • --rm:容器停止後自動刪除,適用於測試或臨時執行
  • -p 8080:8080:將主機的 8080 端口映射到容器的 8080 端口

提示
建議使用 Docker Compose 來管理複雜的多容器應用程式

重要提示
在生產環境中,務必遵循安全最佳實踐,定期更新基礎映像檔,並進行安全掃描

📺 實戰教學影片:編寫dockerfile建立image快速部屬環境