建)
6.1 Dockerfile 是什么Dockerfile 是一個文本文件里面按順序?qū)懼幌盗兄噶?。Docker 引擎讀取這些指令逐行執(zhí)行最終生成一個鏡像。鏡像與 Dockerfile 的關(guān)系1Dockerfile配方 2 ↓ docker build 3 鏡像模板 4 ↓ docker run 5 容器運行實例什么時候需要寫 Dockerfile把自己的代碼打包成鏡像需要安裝額外的系統(tǒng)依賴如ffmpeg、gcc、libssl-dev需要定制化的啟動命令或環(huán)境變量需要優(yōu)化鏡像體積、提升構(gòu)建速度需要多階段構(gòu)建把編譯產(chǎn)物提取到輕量運行鏡像中6.2 Dockerfile 常用指令詳解6.2.1FROM— 指定基礎(chǔ)鏡像dockerfile1FROM python:3.12-slim 2# 基于 Python 3.12 slim 版本構(gòu)建 3 4FROM node:20-alpine 5# 基于 Node.js 20 Alpine 版本構(gòu)建 6 7FROM nginx:1.27 8# 基于 Nginx 1.27 構(gòu)建說明FROM必須是 Dockerfile 的第一條指令A(yù)RG除外?;A(chǔ)鏡像決定了后續(xù)可用的系統(tǒng)環(huán)境和工具?;A(chǔ)鏡像選擇建議類型特點適用場景debian/ubuntu體積較大工具齊全需要系統(tǒng)級依賴的復(fù)雜應(yīng)用slim精簡版 Debian體積適中Python / Java 等應(yīng)用的常規(guī)選擇alpine極小體積基于 musl libc對體積敏感的服務(wù)注意兼容性問題distroless極小無 shell生產(chǎn)環(huán)境高安全要求場景6.2.2WORKDIR— 設(shè)置工作目錄dockerfile1WORKDIR /app 2# 后續(xù) RUN、COPY、CMD 等指令都在 /app 目錄下執(zhí)行 3 4COPY . . 5# 將文件復(fù)制到 /app 目錄下建議始終使用WORKDIR而不是RUN cd /app ...。WORKDIR會在鏡像中創(chuàng)建目錄并切換語義更清晰。6.2.3COPYvsADD— 文件復(fù)制dockerfile1COPY requirements.txt /app/ 2# 將宿主機的 requirements.txt 復(fù)制到鏡像的 /app/ 目錄 3 4COPY . /app/ 5# 將當(dāng)前構(gòu)建上下文的所有文件復(fù)制到鏡像中 6 7ADD https://example.com/app.tar.gz /app/ 8# ADD 支持自動解壓 tar 文件和下載 URL不推薦用于 URL區(qū)別與選擇指令功能推薦程度COPY復(fù)制本地文件優(yōu)先使用ADD復(fù)制 自動解壓 tar 下載 URL? 僅在需要解壓 tar 時使用建議除非需要自動解壓.tar.gz文件否則一律使用COPY。ADD的隱式行為容易造成混淆。6.2.4RUN— 構(gòu)建時執(zhí)行命令dockerfile1RUN apt-get update apt-get install -y \ 2 gcc \ 3 libpq-dev \ 4 rm -rf /var/lib/apt/lists/* 5# 安裝系統(tǒng)依賴安裝后清理緩存以減小鏡像體積 6 7RUN pip install --no-cache-dir -r requirements.txt 8# 安裝 Python 依賴--no-cache-dir 避免緩存占用空間 9 10RUN npm install --production 11# 安裝 Node.js 生產(chǎn)依賴建議多個相關(guān)命令用合并到一條RUN中減少鏡像層數(shù)。每條RUN都會生成一個新層。6.2.5CMDvsENTRYPOINT— 啟動命令重點這是 Dockerfile 中最容易混淆的兩個指令。CMD— 提供默認(rèn)啟動命令可被docker run覆蓋dockerfile1CMD [python, app.py] 2# 默認(rèn)執(zhí)行 python app.py1docker run myapp 2# 執(zhí)行 python app.py 3 4docker run myapp bash 5# 覆蓋為 bashCMD 被忽略ENTRYPOINT— 固定啟動命令docker run的參數(shù)作為附加參數(shù)dockerfile1ENTRYPOINT [python, app.py] 2# 固定執(zhí)行 python app.py1docker run myapp 2# 執(zhí)行 python app.py 3 4docker run myapp --port 8080 5# 執(zhí)行 python app.py --port 8080兩者配合使用最佳實踐dockerfile1ENTRYPOINT [python, app.py] 2CMD [--port, 5000] 3# ENTRYPOINT 固定命令CMD 提供默認(rèn)參數(shù)1docker run myapp 2# 執(zhí)行 python app.py --port 5000 3 4docker run myapp --port 8080 5# 執(zhí)行 python app.py --port 8080JSON 數(shù)組格式 vs Shell 格式dockerfile1# JSON 數(shù)組格式推薦— 不會經(jīng)過 shell 解析 2CMD [python, app.py] 3 4# Shell 格式 — 實際執(zhí)行 /bin/sh -c python app.py 5CMD python app.py建議優(yōu)先使用 JSON 數(shù)組格式。Shell 格式無法正確接收信號如docker stop的 SIGTERM可能導(dǎo)致容器無法優(yōu)雅停止。6.2.6ENV— 設(shè)置環(huán)境變量dockerfile1ENV APP_ENVproduction 2ENV PORT8080 3 4# 在后續(xù)指令中引用 5RUN echo $APP_ENV 6CMD [python, app.py]bash1docker run -e APP_ENVstaging myapp 2# 運行時覆蓋環(huán)境變量場景數(shù)據(jù)庫連接串、API 密鑰、日志級別等配置通過環(huán)境變量注入方便同一鏡像在不同環(huán)境開發(fā)/測試/生產(chǎn)中使用。6.2.7EXPOSE— 聲明端口dockerfile1EXPOSE 8080 2# 聲明容器運行時監(jiān)聽 8080 端口說明EXPOSE只是文檔性質(zhì)的聲明不會自動映射端口。實際端口映射仍需在docker run時使用-p參數(shù)。6.2.8ARG— 構(gòu)建參數(shù)dockerfile1ARG APP_VERSION1.0 2ARG BUILD_ENVproduction 3 4RUN echo Building version $APP_VERSION for $BUILD_ENVbash1docker build --build-arg APP_VERSION2.0 --build-arg BUILD_ENVstaging -t myapp . 2# 構(gòu)建時傳入?yún)?shù)說明ARG僅在構(gòu)建時有效運行時不存在。ENV在構(gòu)建時和運行時都有效。6.2.9LABEL— 鏡像元信息dockerfile1LABEL maintainerdevexample.com 2LABEL version1.0 3LABEL descriptionMy Flask Application說明用于標(biāo)記鏡像的作者、版本、描述等信息方便團(tuán)隊協(xié)作和鏡像管理。6.2.10USER— 切換用戶dockerfile1RUN useradd -m appuser 2USER appuser 3# 后續(xù)指令及容器運行時都以 appuser 身份執(zhí)行建議生產(chǎn)環(huán)境不要以root運行容器。使用USER切換到非特權(quán)用戶提升安全性。6.2.11HEALTHCHECK— 健康檢查dockerfile1HEALTHCHECK --interval30s --timeout3s --start-period5s --retries3 \ 2 CMD curl -f http://localhost:8080/health || exit 1參數(shù)說明參數(shù)含義--interval檢查間隔--timeout超時時間--start-period啟動寬限期容器啟動后多久開始檢查--retries連續(xù)失敗多少次判定為不健康bash1docker ps 2# 健康狀態(tài)會顯示在 STATUS 列中healthy / unhealthy場景配合 Kubernetes、Docker Swarm 等編排工具自動重啟不健康的容器。6.3 構(gòu)建鏡像docker build6.3.1 基本構(gòu)建命令bash1docker build -t myapp:1.0 . 2# -t給鏡像打標(biāo)簽 3# .使用當(dāng)前目錄作為構(gòu)建上下文6.3.2 指定 Dockerfile 路徑bash1docker build -t myapp:1.0 -f Dockerfile.prod . 2# -f指定 Dockerfile 文件名6.3.3 禁用緩存bash1docker build --no-cache -t myapp:1.0 . 2# 每次構(gòu)建都重新執(zhí)行所有指令不使用緩存場景當(dāng)構(gòu)建緩存導(dǎo)致舊代碼或舊依賴被使用時用--no-cache強制重新構(gòu)建。6.3.4 傳入構(gòu)建參數(shù)bash1docker build --build-arg APP_VERSION2.0 -t myapp:2.0 . 2# 傳入 ARG 參數(shù)6.3.5.dockerignore文件在項目根目錄創(chuàng)建.dockerignore排除不需要復(fù)制到鏡像中的文件1.git 2node_modules 3__pycache__ 4*.pyc 5.env 6Dockerfile 7docker-compose.yml 8README.md說明.dockerignore的作用類似.gitignore可以減少構(gòu)建上下文大小加快構(gòu)建速度同時避免敏感文件如.env被打包進(jìn)鏡像。6.4 構(gòu)建緩存與層優(yōu)化Docker 構(gòu)建時會緩存每一層的結(jié)果。如果某一層及其之前的所有層都沒有變化Docker 會直接使用緩存跳過執(zhí)行。指令順序?qū)?gòu)建速度的影響dockerfile1# 不好的順序 — 代碼變化會導(dǎo)致 pip install 重新執(zhí)行 2COPY . /app/ 3RUN pip install -r requirements.txt 4 5# 好的順序 — 依賴文件不變時pip install 使用緩存 6COPY requirements.txt /app/ 7RUN pip install -r requirements.txt 8COPY . /app/原理requirements.txt變化頻率遠(yuǎn)低于代碼文件。先復(fù)制依賴文件并安裝再復(fù)制代碼可以充分利用緩存大幅提升構(gòu)建速度。Node.js 項目同理dockerfile1COPY package.json package-lock.json /app/ 2RUN npm install 3COPY . /app/6.5 鏡像體積優(yōu)化實戰(zhàn)6.5.1 選擇輕量基礎(chǔ)鏡像dockerfile1# 體積約 1GB 2FROM python:3.12 3 4# 體積約 150MB 5FROM python:3.12-slim 6 7# 體積約 50MB 8FROM python:3.12-alpine注意Alpine 基于 musl libc某些 Python 包如numpy、pandas可能需要額外編譯構(gòu)建時間可能更長。根據(jù)項目實際情況選擇。6.5.2 多階段構(gòu)建Multi-stage Build這是鏡像體積優(yōu)化的最重要手段。Go 應(yīng)用示例dockerfile1# 第一階段編譯 2FROM golang:1.22 AS builder 3 4WORKDIR /app 5COPY go.mod go.sum ./ 6RUN go mod download 7COPY . . 8RUN CGO_ENABLED0 GOOSlinux go build -o myapp . 9 10# 第二階段運行 11FROM alpine:3.19 12 13WORKDIR /app 14COPY --frombuilder /app/myapp . 15EXPOSE 8080 16CMD [./myapp]說明最終鏡像只包含編譯后的二進(jìn)制文件不包含 Go 編譯器和源碼體積從 ~800MB 降到 ~15MB。Python 應(yīng)用示例dockerfile1# 第一階段安裝依賴 2FROM python:3.12-slim AS builder 3 4WORKDIR /app 5COPY requirements.txt . 6RUN pip install --no-cache-dir --prefix/install -r requirements.txt 7 8# 第二階段運行 9FROM python:3.12-slim 10 11WORKDIR /app 12COPY --frombuilder /install /usr/local 13COPY . . 14EXPOSE 5000 15CMD [python, app.py]Node.js 應(yīng)用示例dockerfile1# 第一階段構(gòu)建 2FROM node:20-alpine AS builder 3 4WORKDIR /app 5COPY package.json package-lock.json ./ 6RUN npm ci 7COPY . . 8RUN npm run build 9 10# 第二階段運行 11FROM node:20-alpine 12 13WORKDIR /app 14COPY --frombuilder /app/dist ./dist 15COPY --frombuilder /app/node_modules ./node_modules 16COPY package.json ./ 17EXPOSE 3000 18CMD [node, dist/index.js]6.5.3 合并 RUN 指令dockerfile1# 生成多個層 2RUN apt-get update 3RUN apt-get install -y gcc 4RUN apt-get install -y libpq-dev 5RUN rm -rf /var/lib/apt/lists/* 6 7# 合并為一條減少層數(shù) 8RUN apt-get update apt-get install -y \ 9 gcc \ 10 libpq-dev \ 11 rm -rf /var/lib/apt/lists/*6.5.4 清理構(gòu)建緩存dockerfile1# Python 2RUN pip install --no-cache-dir -r requirements.txt 3 4# Debian/Ubuntu 5RUN apt-get update apt-get install -y xxx \ 6 rm -rf /var/lib/apt/lists/* 7 8# Alpine 9RUN apk add --no-cache xxx6.6 多項目類型實戰(zhàn)6.6.1 Python Flask 應(yīng)用項目結(jié)構(gòu)1flask-app/ 2├── app.py 3├── requirements.txt 4├── .dockerignore 5└── Dockerfilerequirements.txt1flask3.0.0 2gunicorn21.2.0Dockerfile1FROM python:3.12-slim 2 3WORKDIR /app 4 5# 先復(fù)制依賴文件利用緩存 6COPY requirements.txt . 7RUN pip install --no-cache-dir -r requirements.txt 8 9# 再復(fù)制代碼 10COPY . . 11 12# 非 root 用戶運行 13RUN useradd -m appuser 14USER appuser 15 16EXPOSE 5000 17 18# 使用 gunicorn 作為生產(chǎn) WSGI 服務(wù)器 19CMD [gunicorn, --bind, 0.0.0.0:5000, app:app]構(gòu)建與運行1docker build -t flask-app:1.0 . 2# 構(gòu)建鏡像 3 4docker run -d \ 5 --name flask-app \ 6 -p 5000:5000 \ 7 flask-app:1.0 8# 運行容器說明生產(chǎn)環(huán)境使用gunicorn而非 Flask 內(nèi)置服務(wù)器。gunicorn支持多 worker、優(yōu)雅重啟性能更好。6.6.2 Node.js Express 應(yīng)用項目結(jié)構(gòu)1node-app/ 2├── src/ 3│ └── index.js 4├── package.json 5├── package-lock.json 6├── .dockerignore 7└── DockerfileDockerfile1FROM node:20-alpine 2 3WORKDIR /app 4 5# 先復(fù)制依賴文件 6COPY package.json package-lock.json ./ 7RUN npm ci --onlyproduction 8 9# 再復(fù)制代碼 10COPY src/ ./src/ 11 12# 非 root 用戶 13RUN addgroup -S appgroup adduser -S appuser -G appgroup 14USER appuser 15 16EXPOSE 3000 17 18CMD [node, src/index.js]說明npm ci比npm install更快、更確定它嚴(yán)格按照package-lock.json安裝依賴適合 CI/CD 和 Docker 構(gòu)建。--onlyproduction跳過devDependencies減小體積。6.6.3 Go Web 應(yīng)用項目結(jié)構(gòu)1go-app/ 2├── main.go 3├── go.mod 4├── go.sum 5└── DockerfileDockerfile多階段構(gòu)建1# 編譯階段 2FROM golang:1.22-alpine AS builder 3 4WORKDIR /app 5COPY go.mod go.sum ./ 6RUN go mod download 7COPY . . 8RUN CGO_ENABLED0 GOOSlinux go build -ldflags-s -w -o myapp . 9 10# 運行階段 11FROM alpine:3.19 12 13WORKDIR /app 14COPY --frombuilder /app/myapp . 15 16EXPOSE 8080 17CMD [./myapp]說明-ldflags-s -w去掉調(diào)試信息進(jìn)一步減小二進(jìn)制體積。Go 編譯后是靜態(tài)二進(jìn)制文件運行階段甚至可以使用scratch空鏡像。6.6.4 Nginx 靜態(tài)站點項目結(jié)構(gòu)1static-site/ 2├── dist/ 3│ ├── index.html 4│ ├── css/ 5│ └── js/ 6├── nginx.conf 7└── Dockerfilenginx.conf1server { 2 listen 80; 3 server_name localhost; 4 root /usr/share/nginx/html; 5 index index.html; 6 7 location / { 8 try_files $uri $uri/ /index.html; 9 } 10}Dockerfile1FROM nginx:1.27-alpine 2 3# 復(fù)制自定義配置 4COPY nginx.conf /etc/nginx/conf.d/default.conf 5 6# 復(fù)制靜態(tài)文件 7COPY dist/ /usr/share/nginx/html/ 8 9EXPOSE 80構(gòu)建與運行1docker build -t my-site:1.0 . 2docker run -d --name my-site -p 80:80 my-site:1.0場景Vue / React / Angular 等前端項目構(gòu)建后生成dist目錄用 Nginx 托管是最常見的部署方式。try_files配置確保 SPA 路由刷新時不會 404。6.6.5 Java Spring Boot 應(yīng)用項目結(jié)構(gòu)1java-app/ 2├── src/ 3├── pom.xml 4├── target/ 5│ └── myapp.jar 6└── DockerfileDockerfile多階段構(gòu)建1# 編譯階段 2FROM maven:3.9-eclipse-temurin-17 AS builder 3 4WORKDIR /app 5COPY pom.xml . 6COPY src ./src 7RUN mvn clean package -DskipTests 8 9# 運行階段 10FROM eclipse-temurin:17-jre-alpine 11 12WORKDIR /app 13COPY --frombuilder /app/target/myapp.jar . 14 15EXPOSE 8080 16CMD [java, -jar, myapp.jar]說明運行階段使用 JRE 而非 JDK體積更小。-DskipTests跳過測試加快構(gòu)建CI 環(huán)境中測試應(yīng)在構(gòu)建前單獨執(zhí)行。6.7 鏡像推送與私有倉庫6.7.1 登錄 Docker Hub1docker login 2# 輸入 Docker Hub 用戶名和密碼6.7.2 重新打標(biāo)簽1docker tag myapp:1.0 username/myapp:1.0 2# username 是你的 Docker Hub 用戶名6.7.3 推送鏡像1docker push username/myapp:1.0 2# 推送到 Docker Hub6.7.4 拉取鏡像1docker pull username/myapp:1.0 2# 從 Docker Hub 拉取6.7.5 私有倉庫簡介倉庫類型說明Docker Hub公共倉庫免費私有倉庫需付費阿里云 ACR國內(nèi)訪問速度快有免費個人版Harbor開源私有倉庫可自建GitHub Container Registry與 GitHub 深度集成建議國內(nèi)團(tuán)隊優(yōu)先使用阿里云 ACR 或自建 Harbor避免 Docker Hub 拉取速度慢的問題。6.8 常見錯誤排查問題原因解決方案COPY failed: file not found文件不在構(gòu)建上下文中檢查.dockerignore和文件路徑容器啟動后立即退出CMD/ENTRYPOINT 命令執(zhí)行完畢或報錯用docker logs查看錯誤信息端口無法訪問未使用-p映射端口docker run -p 宿主機端口:容器端口docker run覆蓋了 CMDCMD 可被覆蓋需要固定命令時使用 ENTRYPOINT構(gòu)建緩存未更新Docker 使用了舊緩存使用--no-cache或調(diào)整指令順序