news 2026/4/23 11:35:15

第7章 构建自定义镜像

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第7章 构建自定义镜像

7.1 Dockerfile语法详解

7.1.1 什么是Dockerfile

Dockerfile是一个文本文件,包含一系列指令,用于自动化构建Docker镜像。

基本结构

# 注释 指令 参数

简单示例

# 使用官方Python运行时作为基础镜像 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 复制应用文件 COPY . . # 安装依赖 RUN pip install -r requirements.txt # 暴露端口 EXPOSE 5000 # 运行应用 CMD ["python", "app.py"]

7.1.2 Dockerfile指令顺序

指令执行顺序很重要:

FROM # 必须是第一条指令(除注释外) MAINTAINER # 已弃用,使用LABEL代替 LABEL # 添加元数据 ARG # 定义构建参数 ENV # 设置环境变量 WORKDIR # 设置工作目录 COPY/ADD # 复制文件 RUN # 执行命令 EXPOSE # 声明端口 VOLUME # 声明数据卷 USER # 切换用户 CMD # 容器启动命令 ENTRYPOINT # 容器入口点

7.1.3 构建上下文

# 构建镜像dockerbuild -t myapp:1.0.# └─ 构建上下文(当前目录)# 构建上下文中的文件会被发送给Docker守护进程# 因此要注意:# 1. 构建上下文不要太大# 2. 使用.dockerignore排除不需要的文件

.dockerignore示例

# 排除版本控制 .git .gitignore # 排除临时文件 *.tmp *.log .DS_Store # 排除构建产物 node_modules __pycache__ *.pyc dist/ build/ # 排除文档 README.md docs/ # 排除测试文件 tests/ *.test.js

7.2 常用指令详解

7.2.1 FROM - 基础镜像

# 基本格式 FROM <image> FROM <image>:<tag> FROM <image>@<digest> # 示例 FROM ubuntu:22.04 FROM python:3.9-slim FROM nginx:1.25-alpine # 多阶段构建 FROM golang:1.20 AS builder # ... 构建步骤 FROM alpine:latest # ... 复制构建产物 # 使用ARG指定基础镜像版本 ARG PYTHON_VERSION=3.9 FROM python:${PYTHON_VERSION}-slim

选择基础镜像的原则

  1. 官方镜像优先
  2. 选择合适的变体
    • ubuntu:22.04- 完整系统(~77MB)
    • python:3.9-slim- 精简版(~120MB)
    • python:3.9-alpine- Alpine版(~50MB)
  3. 固定版本标签:使用python:3.9而非python:latest

7.2.2 RUN - 执行命令

# Shell形式(推荐) RUN apt-get update && apt-get install -y \ curl \ vim \ && rm -rf /var/lib/apt/lists/* # Exec形式 RUN ["/bin/bash", "-c", "echo hello"] # 多个RUN会创建多层 RUN apt-get update # Layer 1 RUN apt-get install -y curl # Layer 2 RUN apt-get install -y vim # Layer 3 # 合并为一层(推荐) RUN apt-get update && apt-get install -y \ curl \ vim \ && rm -rf /var/lib/apt/lists/* # Layer 1

最佳实践

# 安装软件包 RUN apt-get update && apt-get install -y \ package1 \ package2 \ package3 \ && rm -rf /var/lib/apt/lists/* # 清理缓存 # 创建用户 RUN groupadd -r appuser && useradd -r -g appuser appuser # 编译安装 RUN wget https://example.com/source.tar.gz \ && tar -xzf source.tar.gz \ && cd source \ && ./configure \ && make \ && make install \ && cd .. \ && rm -rf source source.tar.gz # 清理临时文件

7.2.3 COPY - 复制文件

# 基本格式 COPY <src>... <dest> COPY ["<src>",... "<dest>"] # 路径包含空格时使用 # 示例 COPY app.py /app/ COPY requirements.txt /app/ COPY . /app/ # 复制多个文件 COPY file1.txt file2.txt /app/ # 使用通配符 COPY *.py /app/ COPY src/*.js /app/src/ # 保留文件属性 COPY --chown=user:group file.txt /app/ # 设置权限 COPY --chmod=755 script.sh /app/

COPY vs ADD

# COPY:简单复制 COPY requirements.txt /app/ # ADD:支持URL和自动解压(不推荐常用) ADD http://example.com/file.tar.gz /tmp/ ADD archive.tar.gz /app/ # 自动解压 # 推荐:明确使用COPY,需要解压时手动RUN tar COPY archive.tar.gz /tmp/ RUN tar -xzf /tmp/archive.tar.gz -C /app/ \ && rm /tmp/archive.tar.gz

7.2.4 WORKDIR - 工作目录

# 设置工作目录 WORKDIR /app # 如果目录不存在会自动创建 WORKDIR /path/to/workdir # 可以多次使用 WORKDIR /a WORKDIR b WORKDIR c # 最终在 /a/b/c # 使用环境变量 ENV DIRPATH=/app WORKDIR ${DIRPATH}

最佳实践

# 不推荐 RUN cd /app COPY . . # 不在/app目录 # 推荐 WORKDIR /app COPY . . # 在/app目录

7.2.5 ENV - 环境变量

# 基本格式 ENV <key>=<value> ENV <key1>=<value1> <key2>=<value2> # 示例 ENV NODE_ENV=production ENV APP_HOME=/app \ APP_USER=appuser # 在后续指令中使用 ENV APP_HOME=/app WORKDIR ${APP_HOME} COPY . ${APP_HOME} # 运行时覆盖 # docker run -e NODE_ENV=development myapp

ARG vs ENV

# ARG:构建时参数 ARG PYTHON_VERSION=3.9 FROM python:${PYTHON_VERSION} # docker build --build-arg PYTHON_VERSION=3.10 -t myapp . # ENV:运行时环境变量 ENV APP_ENV=production # docker run -e APP_ENV=development myapp # 组合使用 ARG APP_VERSION=1.0 ENV VERSION=${APP_VERSION}

7.2.6 EXPOSE - 暴露端口

# 声明端口(仅文档作用) EXPOSE 80 EXPOSE 443 EXPOSE 8080/tcp EXPOSE 8080/udp # 多个端口 EXPOSE 80 443 8080 # 使用变量 ARG PORT=8080 EXPOSE ${PORT}

注意:EXPOSE只是声明,实际端口映射需要在运行时指定:

dockerrun -p8080:80 myapp

7.2.7 VOLUME - 数据卷

# 声明数据卷挂载点 VOLUME /data VOLUME ["/var/log", "/var/db"] # 示例 FROM postgres:13 VOLUME /var/lib/postgresql/data

7.2.8 USER - 切换用户

# 切换到非root用户(安全最佳实践) RUN groupadd -r appuser && useradd -r -g appuser appuser USER appuser # 切换回root USER root # 使用UID USER 1000

完整示例

FROM python:3.9-slim # 创建用户 RUN groupadd -r appuser && useradd -r -g appuser appuser WORKDIR /app # 以root身份安装系统依赖 RUN apt-get update && apt-get install -y \ gcc \ && rm -rf /var/lib/apt/lists/* # 复制文件 COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # 修改文件所有者 RUN chown -R appuser:appuser /app # 切换到非root用户 USER appuser CMD ["python", "app.py"]

7.2.9 CMD - 容器启动命令

# Shell形式 CMD python app.py # Exec形式(推荐) CMD ["python", "app.py"] # 作为ENTRYPOINT的参数 ENTRYPOINT ["python"] CMD ["app.py"]

注意

  • 一个Dockerfile只能有一个CMD
  • 多个CMD,只有最后一个生效
  • 运行时可以覆盖CMD
# Dockerfile中的CMDCMD["python","app.py"]# 运行时覆盖dockerrun myapp python other.py

7.2.10 ENTRYPOINT - 入口点

# Exec形式(推荐) ENTRYPOINT ["python", "app.py"] # Shell形式 ENTRYPOINT python app.py # 与CMD组合 ENTRYPOINT ["python"] CMD ["app.py"] # 等效于:python app.py # 运行时可以只覆盖CMD部分

ENTRYPOINT vs CMD

# 示例1:只用CMD CMD ["nginx", "-g", "daemon off;"] # 运行:docker run myapp # 覆盖:docker run myapp echo "hello" # 示例2:只用ENTRYPOINT ENTRYPOINT ["nginx", "-g", "daemon off;"] # 运行:docker run myapp # 不易覆盖(需要--entrypoint) # 示例3:组合使用(推荐) ENTRYPOINT ["python"] CMD ["app.py"] # 运行:docker run myapp # 执行:python app.py # 覆盖CMD:docker run myapp test.py # 执行:python test.py

实用示例

# 启动脚本作为ENTRYPOINT COPY docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["nginx", "-g", "daemon off;"]
# docker-entrypoint.sh#!/bin/bashset-e# 初始化操作echo"Initializing..."# 数据库迁移、配置生成等# 执行CMDexec"$@"

7.3 构建上下文(Build Context)

7.3.1 理解构建上下文

# 构建命令dockerbuild -t myapp:1.0.# └─ 构建上下文路径# Docker会将该路径下的所有文件打包发送给守护进程# 输出:Sending build context to Docker daemon 15.36MB

项目结构示例

myproject/ ├── Dockerfile ├── .dockerignore ├── app/ │ ├── __init__.py │ ├── main.py │ └── utils.py ├── requirements.txt ├── tests/ │ └── test_main.py ├── docs/ │ └── README.md └── .git/

7.3.2 优化构建上下文

.dockerignore文件

# 版本控制 .git .gitignore .gitattributes # IDE配置 .vscode/ .idea/ *.swp *.swo # Python __pycache__/ *.py[cod] *$py.class .pytest_cache/ .coverage htmlcov/ # Node.js node_modules/ npm-debug.log yarn-error.log # 构建产物 dist/ build/ *.egg-info/ # 临时文件 *.tmp *.log .DS_Store Thumbs.db # 文档和测试 docs/ tests/ *.md LICENSE # 环境文件 .env .env.local

7.3.3 使用远程上下文

# 从Git仓库构建dockerbuild https://github.com/user/repo.git#branch# 从Git仓库的子目录构建dockerbuild https://github.com/user/repo.git#branch:subdir# 从tar文件构建dockerbuild http://example.com/context.tar.gz

7.3.4 构建上下文最佳实践

# 1. 在项目根目录创建Dockerfilemyproject/ ├── Dockerfile ├── src/ └──...# 2. 使用.dockerignore排除不需要的文件# 3. 避免COPY整个上下文# 不推荐COPY./app# 推荐:只复制必要文件COPY requirements.txt /app/ COPY src/ /app/src/# 4. 查看构建上下文大小dockerbuild --no-cache -ttest.2>&1|grep"build context"

7.4 多阶段构建(Multi-stage Build)

7.4.1 为什么需要多阶段构建

问题

# 单阶段构建 FROM golang:1.20 WORKDIR /app COPY . . RUN go build -o myapp # 结果:镜像包含Go编译器等工具,体积巨大(~800MB)

解决

# 多阶段构建 # 阶段1:构建 FROM golang:1.20 AS builder WORKDIR /app COPY . . RUN go build -o myapp # 阶段2:运行 FROM alpine:latest WORKDIR /app COPY --from=builder /app/myapp . CMD ["./myapp"] # 结果:只包含可执行文件和运行时依赖(~15MB)

7.4.2 多阶段构建示例

Python应用

# 阶段1:构建依赖 FROM python:3.9 AS builder WORKDIR /app COPY requirements.txt . # 安装依赖到指定目录 RUN pip install --user --no-cache-dir -r requirements.txt # 阶段2:运行环境 FROM python:3.9-slim WORKDIR /app # 复制安装的依赖 COPY --from=builder /root/.local /root/.local COPY . . # 确保PATH包含用户安装的包 ENV PATH=/root/.local/bin:$PATH CMD ["python", "app.py"]

Node.js应用

# 阶段1:构建 FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 阶段2:运行 FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY --from=builder /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/index.js"]

Go应用

# 阶段1:构建 FROM golang:1.20-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . # 阶段2:运行 FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/main . EXPOSE 8080 CMD ["./main"]

7.4.3 命名构建阶段

# 命名阶段 FROM golang:1.20 AS builder FROM node:18 AS frontend-builder FROM python:3.9 AS backend-builder # 从指定阶段复制 COPY --from=builder /app/binary . COPY --from=frontend-builder /app/dist ./static COPY --from=backend-builder /app/app.py .

7.4.4 停止在特定阶段

# 只构建到builder阶段(用于调试)dockerbuild --target builder -t myapp:builder.# 完整构建dockerbuild -t myapp:latest.

调试用例

FROM golang:1.20 AS builder WORKDIR /app COPY . . RUN go build -o myapp FROM alpine:latest AS debug COPY --from=builder /app/myapp . RUN apk add --no-cache gdb CMD ["gdb", "./myapp"] FROM alpine:latest AS release COPY --from=builder /app/myapp . CMD ["./myapp"]
# 构建debug版本dockerbuild --target debug -t myapp:debug.# 构建release版本dockerbuild --target release -t myapp:release.# 或dockerbuild -t myapp:release.

7.4.5 外部镜像作为阶段

# 从外部镜像复制文件 FROM nginx:1.25 AS nginx-base FROM alpine:latest COPY --from=nginx-base /etc/nginx/nginx.conf /config/

7.5 构建缓存优化技巧

7.5.1 理解构建缓存

Docker在构建时会缓存每一层:

FROM python:3.9-slim # Layer 1: 缓存 WORKDIR /app # Layer 2: 缓存 COPY requirements.txt . # Layer 3: 如果文件未改变,使用缓存 RUN pip install -r requirements.txt # Layer 4: 如果Layer 3缓存,使用缓存 COPY . . # Layer 5: 如果文件改变,重新构建 CMD ["python", "app.py"] # Layer 6: 重新构建

7.5.2 优化层的顺序

不好的顺序

FROM python:3.9-slim WORKDIR /app COPY . . # 代码改变,此层失效 RUN pip install -r requirements.txt # 每次都要重新安装 CMD ["python", "app.py"]

好的顺序

FROM python:3.9-slim WORKDIR /app COPY requirements.txt . # 只有依赖改变时失效 RUN pip install -r requirements.txt # 大部分时间使用缓存 COPY . . # 代码改变,只影响这一层 CMD ["python", "app.py"]

7.5.3 分离变化频繁的层

# Node.js应用优化 FROM node:18-alpine WORKDIR /app # 1. 先复制package.json(不常改变) COPY package*.json ./ RUN npm ci # 2. 再复制源代码(经常改变) COPY src/ ./src/ COPY public/ ./public/ # 3. 最后复制配置文件 COPY config/ ./config/ CMD ["npm", "start"]

7.5.4 使用.dockerignore

# .dockerignore node_modules .git .env *.log # 减少构建上下文,避免不必要的缓存失效

7.5.5 构建参数管理

# 使用ARG避免硬编码 ARG PYTHON_VERSION=3.9 FROM python:${PYTHON_VERSION}-slim ARG APP_VERSION=1.0.0 ENV VERSION=${APP_VERSION} # 标签信息 LABEL version="${APP_VERSION}" \ description="My Application"

7.5.6 禁用缓存

# 完全禁用缓存dockerbuild --no-cache -t myapp:1.0.# 从特定层开始禁用缓存dockerbuild --cache-from myapp:latest -t myapp:1.0.# 拉取缓存dockerpull myapp:latestdockerbuild --cache-from myapp:latest -t myapp:1.1.

7.6 实战:构建完整应用镜像

7.6.1 Python Flask应用

项目结构

flask-app/ ├── Dockerfile ├── .dockerignore ├── requirements.txt ├── app.py └── templates/ └── index.html

requirements.txt

Flask==2.3.0 gunicorn==20.1.0

app.py

fromflaskimportFlask,render_template app=Flask(__name__)@app.route('/')defhello():returnrender_template('index.html')if__name__=='__main__':app.run(host='0.0.0.0',port=5000)

Dockerfile

FROM python:3.9-slim # 设置环境变量 ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 # 创建非root用户 RUN groupadd -r appuser && useradd -r -g appuser appuser WORKDIR /app # 安装系统依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ && rm -rf /var/lib/apt/lists/* # 安装Python依赖 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用文件 COPY . . # 修改文件所有者 RUN chown -R appuser:appuser /app # 切换用户 USER appuser # 暴露端口 EXPOSE 5000 # 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:5000/ || exit 1 # 启动应用 CMD ["gunicorn", "-b", "0.0.0.0:5000", "-w", "4", "app:app"]

.dockerignore

__pycache__ *.pyc .pytest_cache .coverage .env .git README.md

构建和运行

# 构建dockerbuild -t flask-app:1.0.# 运行dockerrun -d -p5000:5000 --name myflask flask-app:1.0# 测试curlhttp://localhost:5000

7.6.2 React前端应用

Dockerfile

# 多阶段构建 # 阶段1:构建 FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 阶段2:生产环境 FROM nginx:1.25-alpine # 复制构建产物 COPY --from=builder /app/build /usr/share/nginx/html # 复制Nginx配置 COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

nginx.conf

server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api { proxy_pass http://backend:8080; } }

7.7 小结

通过本章学习,我们掌握了构建自定义镜像的核心技能:

Dockerfile语法

  • 基本结构和指令顺序
  • 构建上下文理解

常用指令

  • FROM、RUN、COPY、WORKDIR
  • ENV、EXPOSE、VOLUME
  • CMD、ENTRYPOINT的区别

构建上下文

  • .dockerignore使用
  • 优化构建上下文大小

多阶段构建

  • 减小镜像体积
  • 分离构建和运行环境

缓存优化

  • 层的顺序优化
  • 分离变化频繁的内容

实战应用

  • Python应用镜像
  • Node.js应用镜像
  • 最佳实践

下一步

在第8章中,我们将学习镜像最佳实践:

  • 选择合适的基础镜像
  • 减小镜像体积的技巧
  • 分层原理与优化
  • 安全扫描与漏洞修复

这些是生产环境中必须掌握的技能。


本章思考题

  1. CMD和ENTRYPOINT的区别是什么?各适用于什么场景?
  2. 多阶段构建如何减小镜像体积?
  3. 如何优化Docker镜像的构建缓存?
  4. 为什么要使用非root用户运行容器?

相关资源

  • Dockerfile参考:https://docs.docker.com/engine/reference/builder/
  • 最佳实践:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
  • 多阶段构建:https://docs.docker.com/build/building/multi-stage/
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 4:48:32

云容笔谈实操手册:利用‘避讳卷宗’精准排除杂乱元素提升画面纯净度

云容笔谈实操手册&#xff1a;利用避讳卷宗精准排除杂乱元素提升画面纯净度 1. 认识云容笔谈与避讳功能 云容笔谈是一款专注于东方审美风格的影像创作平台&#xff0c;它集成了现代AI算法与古典美学意境&#xff0c;能够将用户的创意灵感转化为具有东方韵味的高清视觉作品。该…

作者头像 李华
网站建设 2026/3/21 23:05:14

ChatGLM3-6B技术解析:Streamlit架构的优势与特点

ChatGLM3-6B技术解析&#xff1a;Streamlit架构的优势与特点 1. 引言 如果你正在寻找一个能在本地快速部署、稳定运行的智能对话系统&#xff0c;那么基于ChatGLM3-6B和Streamlit的方案绝对值得深入了解。传统的AI模型部署往往伴随着复杂的依赖冲突、缓慢的界面响应&#xff…

作者头像 李华
网站建设 2026/4/22 2:28:19

实时手机检测-通用保姆级教程:检测框坐标转GIS地理围栏坐标方法

实时手机检测-通用保姆级教程&#xff1a;检测框坐标转GIS地理围栏坐标方法 1. 学习目标与前置知识 本教程将手把手教你如何将实时手机检测模型输出的检测框坐标&#xff0c;转换为GIS地理围栏坐标。学完本文&#xff0c;你将掌握&#xff1a; 实时手机检测模型的基本使用检…

作者头像 李华
网站建设 2026/4/23 4:35:43

意义复权:夺回被时代「盗走」的生命意义感

意义复权&#xff1a;夺回被时代「盗走」的生命意义感开篇&#xff1a;当意义成为最稀缺的资源凌晨三点&#xff0c;你又一次在毫无睡意中拿起手机。屏幕的光映着一张疲惫的脸——这是你&#xff0c;也是这个时代无数人的缩影。你拥有着祖辈难以想象的物质条件&#xff1a;指尖…

作者头像 李华
网站建设 2026/4/10 19:51:47

手机检测WebUI定制化指南:修改置信度阈值、调整标注颜色、导出JSON

手机检测WebUI定制化指南&#xff1a;修改置信度阈值、调整标注颜色、导出JSON 1. 项目概述 手机检测系统基于DAMO-YOLO和TinyNAS技术构建&#xff0c;专为移动端低算力场景优化。这个Web界面不仅提供基础的检测功能&#xff0c;还支持深度定制化配置&#xff0c;让用户可以根…

作者头像 李华