Dockerfile 与存储网络:构建镜像的完整指南
一、Dockerfile 基础
1.1 什么是 Dockerfile?
Dockerfile 是一个文本文件,包含了一系列指令,用于自动构建 Docker 镜像。通过 docker build 命令读取 Dockerfile 中的指令,按顺序执行并生成镜像。
1.2 基本语法
dockerfile
# 注释以 # 开头
# 指令格式:INSTRUCTION arguments
# 指令不区分大小写,但约定大写
二、Dockerfile 核心指令
2.1 FROM:指定基础镜像
dockerfile
# 格式:FROM <image>[:<tag>] [AS <name>]
FROM ubuntu:22.04
FROM node:18-alpine AS builder
FROM scratch # 空镜像,从零开始
最佳实践:
- 使用 Alpine 版本减小镜像体积
- 使用具体的 tag 而不是 latest
- 多阶段构建时给阶段命名
2.2 RUN:执行命令
dockerfile
# Shell 格式(默认)
RUN echo "Hello World"
RUN apt-get update && apt-get install -y nginx
# Exec 格式
RUN ["echo", "Hello World"]
最佳实践:
- 多个命令用
&&连接,减少镜像层数 - 清理缓存:
apt-get clean && rm -rf /var/lib/apt/lists/*
2.3 COPY 与 ADD:复制文件
dockerfile
# COPY:简单复制本地文件
COPY package.json /app/
COPY . /app/
COPY ["package.json", "/app/"]
# ADD:功能更强,支持自动解压 tar 和远程 URL
ADD app.tar.gz /app/ # 自动解压
ADD https://example.com/file.zip /app/ # 下载远程文件
区别:
- COPY 只能复制本地文件,语义更明确
- ADD 可以自动解压 tar 包,支持下载远程文件
- 推荐优先使用 COPY,需要解压功能时用 ADD
2.4 WORKDIR:设置工作目录
dockerfile
WORKDIR /app
COPY package.json . # 相当于 COPY package.json /app/
RUN npm install # 在 /app 目录下执行
作用:设置后续指令的工作目录,如果不存在会自动创建。
2.5 EXPOSE:声明端口
dockerfile
EXPOSE 80
EXPOSE 8080 8443
注意 :EXPOSE 只是声明,不会自动映射端口。运行容器时仍需用 -p 参数映射。
2.6 ENV:设置环境变量
dockerfile
ENV NODE_ENV=production
ENV APP_PORT=3000 APP_NAME=myapp
运行时可以通过 -e 覆盖:docker run -e NODE_ENV=dev myapp
2.7 ARG:构建参数
dockerfile
ARG VERSION=1.0
FROM ubuntu:${VERSION}
构建时通过 --build-arg 传入:docker build --build-arg VERSION=2.0 .
区别:ENV 设置的环境变量会保留到容器中,ARG 只在构建时有效。
2.8 CMD:容器启动命令
dockerfile
# Shell 格式
CMD echo "Hello World"
CMD nginx -g "daemon off;"
# Exec 格式(推荐)
CMD ["nginx", "-g", "daemon off;"]
CMD ["node", "app.js"]
特点:
- 一个 Dockerfile 只能有一个 CMD
- 如果有多个 CMD,只有最后一个生效
docker run后面跟的命令会覆盖 CMD
2.9 ENTRYPOINT:容器入口点
dockerfile
# Exec 格式(推荐)
ENTRYPOINT ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["python", "app.py"]
与 CMD 的区别:
- ENTRYPOINT 不会被
docker run命令覆盖(除非用--entrypoint) docker run后面的参数会作为 ENTRYPOINT 的参数传递- CMD 通常作为 ENTRYPOINT 的默认参数
dockerfile
ENTRYPOINT ["python"]
CMD ["app.py"] # 默认参数
bash
docker run myapp # 执行 python app.py
docker run myapp script.py # 执行 python script.py
2.10 其他常用指令
dockerfile
# VOLUME:声明匿名卷
VOLUME ["/data", "/logs"]
# USER:指定运行用户
USER nobody
# LABEL:添加元数据标签
LABEL version="1.0" maintainer="dev@example.com"
# HEALTHCHECK:健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/ || exit 1
# SHELL:更改默认 shell
SHELL ["/bin/bash", "-c"]
三、Dockerfile 实战
3.1 Node.js 应用
dockerfile
# 多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 最终镜像
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/main.js"]
3.2 Spring Boot 应用
dockerfile
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY . .
RUN ./gradlew bootJar --no-daemon
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
3.3 Python Flask 应用
dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
3.4 构建镜像
bash
# 基本构建
docker build -t myapp:v1.0 .
# 指定 Dockerfile 路径
docker build -f docker/Dockerfile -t myapp:v1.0 .
# 传递构建参数
docker build --build-arg VERSION=2.0 -t myapp:v2.0 .
# 不使用缓存构建
docker build --no-cache -t myapp:v1.0 .
四、数据持久化
4.1 为什么需要数据持久化?
容器是临时的,删除后数据会丢失。需要持久化:
- 数据库数据
- 用户上传的文件
- 应用日志
- 配置文件
4.2 数据卷(Volumes)
由 Docker 管理,存储在 Docker 专用目录中。
bash
# 创建数据卷
docker volume create mydata
# 查看所有数据卷
docker volume ls
# 查看数据卷详情
docker volume inspect mydata
# 使用数据卷运行容器
docker run -d -v mydata:/var/lib/mysql mysql:8.0
# 删除数据卷
docker volume rm mydata
# 清理所有未使用的数据卷
docker volume prune
特点:
- 生命周期独立于容器
- 可以在多个容器间共享
- 支持备份、迁移
- 性能优于 Bind Mount
4.3 绑定挂载(Bind Mounts)
将宿主机目录映射到容器。
bash
# 挂载宿主机目录
docker run -d -v /home/user/data:/app/data nginx
# 挂载单个文件
docker run -d -v /home/user/config.yml:/app/config.yml nginx
# 只读挂载
docker run -d -v /home/user/data:/app/data:ro nginx
特点:
- 直接使用宿主机路径
- 适合开发环境(代码热重载)
- 依赖宿主机目录结构
- 性能最好但可移植性差
4.4 选择建议
| 场景 | 推荐方式 |
|---|---|
| 数据库持久化 | Volume |
| 开发环境代码同步 | Bind Mount |
| 多个容器共享数据 | Volume |
| 配置文件 | Bind Mount 或 Config |
| 日志收集 | Bind Mount |
五、Docker 网络
5.1 网络模式
Docker 提供多种网络模式:
bash
# 查看所有网络
docker network ls
# 创建自定义网络
docker network create mynet
docker network create --driver bridge mynet
docker network create --subnet=172.20.0.0/16 mynet
5.2 Bridge 网络(默认)
容器通过虚拟网桥 docker0 通信。
bash
# 查看 bridge 网络详情
docker network inspect bridge
# 使用自定义 bridge 网络
docker network create mybridge
docker run -d --network mybridge --name container1 nginx
docker run -d --network mybridge --name container2 nginx
# 容器间通过容器名通信
docker exec container1 ping container2
特点:
- 默认网络模式
- 容器有独立 IP
- 同一网络的容器可以通过容器名互相访问
- 与宿主机隔离,需要通过端口映射访问
5.3 Host 网络
容器直接使用宿主机网络。
bash
docker run -d --network host nginx
特点:
- 容器不使用独立 IP,直接使用宿主机端口
- 性能最好(无网络转换)
- 端口冲突需要自行管理
- 不适合端口密集的场景
5.4 None 网络
禁用网络功能。
bash
docker run -d --network none alpine
特点:
- 容器没有网络接口
- 完全隔离
- 适合纯计算任务
5.5 Overlay 网络(Swarm 模式)
跨主机容器通信。
bash
# 初始化 Swarm
docker swarm init
# 创建 overlay 网络
docker network create --driver overlay myoverlay
# 在服务中使用
docker service create --network myoverlay --name myservice nginx
5.6 网络操作命令
bash
# 将容器连接到网络
docker network connect mynet mycontainer
# 从网络断开容器
docker network disconnect mynet mycontainer
# 删除网络
docker network rm mynet
# 清理未使用的网络
docker network prune
六、面试高频题
Q1:Dockerfile 中 COPY 和 ADD 有什么区别?
A:COPY 只能复制本地文件,语义明确;ADD 可以自动解压 tar 包,支持下载远程文件。推荐优先使用 COPY,只在需要自动解压时使用 ADD。
Q2:CMD 和 ENTRYPOINT 有什么区别?
A:CMD 设置容器启动的默认命令,可以被 docker run 参数覆盖;ENTRYPOINT 设置容器入口点,不会被覆盖(除非用 --entrypoint),docker run 参数会作为 ENTRYPOINT 的附加参数。通常两者结合使用:ENTRYPOINT 指定可执行程序,CMD 提供默认参数。
Q3:Docker 的 Volume 和 Bind Mount 有什么区别?
A:Volume 由 Docker 管理,存储在 /var/lib/docker/volumes/,生命周期独立于容器,可移植性好;Bind Mount 直接使用宿主机路径,依赖宿主机目录结构,适合开发环境。Volume 推荐用于生产环境,Bind Mount 适合开发测试。
Q4:什么是 Docker 多阶段构建?有什么好处?
A:多阶段构建允许在一个 Dockerfile 中使用多个 FROM 指令,每个阶段可以使用不同的基础镜像。好处是最终镜像只包含运行所需的文件,可以大幅减小镜像体积。例如构建阶段使用完整 JDK,运行阶段只使用 JRE。
下一篇:Docker Compose 多容器编排