Skip to content

镜像与 Dockerfile

镜像基础操作

bash
# 拉取镜像(默认 latest 标签)
docker pull nginx
docker pull nginx:1.25-alpine

# 列出本地镜像
docker images            # 或 docker image ls

# 查看镜像历史分层
docker history nginx:alpine

# 查看镜像详情(元数据、层、端口、环境变量等)
docker inspect nginx:alpine

# 打标签(tag 相当于给镜像起别名/版本号)
docker tag nginx:latest myapp/nginx:v1

# 删除镜像(无容器依赖时)
docker rmi nginx:alpine          # 或 docker image rm

# 清理无标签悬空镜像
docker image prune

镜像命名规范:[registry/][namespace/]name[:tag]

  • nginxdocker.io/library/nginx:latest
  • myrepo/app:1.0 → 自定义仓库/命名空间

推送与拉取到远程仓库

bash
# 登录 Docker Hub(或私有仓库)
docker login                     # 默认 docker.io
docker login registry.example.com  # 私有仓库

# 给本地镜像打上仓库前缀后推送
docker tag myapp:1.0 myuser/myapp:1.0
docker push myuser/myapp:1.0

# 从仓库拉取
docker pull myuser/myapp:1.0

# 注销
docker logout

保存与加载(离线分发)

bash
# 导出镜像为 tar 文件
docker save -o myapp.tar myapp:1.0
docker save myapp:1.0 | gzip > myapp.tar.gz

# 从 tar 加载
docker load -i myapp.tar

save/load 保留完整镜像;export/import 只针对容器文件系统(丢失历史层),两者不要混淆。

Dockerfile 指令详解

Dockerfile 是构建镜像的脚本,每条指令生成一层FROMRUNCOPYADD 等会增层,CMDENTRYPOINTENV 等只写元数据)。

指令速览

指令作用关键点
FROM指定基础镜像必须是第一条(ARG 除外)
RUN构建时执行命令每条生成一层,尽量合并
COPY复制文件/目录进镜像推荐,比 ADD 纯粹
ADD复制,且支持 URL 与自动解压 tar有副作用,谨慎使用
WORKDIR设置工作目录用绝对路径,目录不存在会自动创建
ENV设置环境变量运行期仍可用
ARG构建参数仅构建期可用,可 --build-arg 传入
EXPOSE声明端口仅文档作用,不真正发布端口
CMD容器默认启动命令可被 docker run 覆盖
ENTRYPOINT容器入口点运行参数附加在其后
USER指定运行用户安全性,避免 root
HEALTHCHECK健康检查判断容器是否健康
LABEL添加元数据版本、维护者等
VOLUME声明匿名卷数据持久化提示
.dockerignore排除构建上下文文件缩小上下文、保护敏感文件

CMD 与 ENTRYPOINT 的区别

形式说明
Shell 形式 CMD echo hi/bin/sh -c 执行,支持变量展开
Exec 形式 CMD ["echo","hi"]直接执行,推荐,信号处理正确
dockerfile
# 场景一:CMD 作默认命令(可被覆盖)
FROM alpine
CMD ["echo", "hello"]
# docker run <img>          → hello
# docker run <img> echo bye → bye(覆盖 CMD)

# 场景二:ENTRYPOINT 固定入口,CMD 提供默认参数
FROM alpine
ENTRYPOINT ["echo"]
CMD ["hello"]
# docker run <img> world    → world(CMD 被覆盖,仍走 ENTRYPOINT)

组合经验:

  • 只用 CMD:镜像有默认行为,允许用户覆盖。
  • 只用 ENTRYPOINT:镜像就是某个固定程序(如命令行工具)。
  • 两者结合:ENTRYPOINT 固定程序,CMD 给默认参数。

完整示例

dockerfile
# 基础镜像:选体积小的 alpine 变体
FROM python:3.12-slim

# 元数据
LABEL maintainer="you@example.com" version="1.0"

# 工作目录
WORKDIR /app

# 先复制依赖文件,利用缓存(依赖不变则跳过安装)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制源码
COPY . .

# 声明端口
EXPOSE 8000

# 运行用户
RUN adduser --disabled-password appuser
USER appuser

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8000/ || exit 1

# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

构建镜像

bash
# 在 Dockerfile 所在目录执行
docker build -t myapp:1.0 .

# 指定 Dockerfile 路径与构建上下文
docker build -f Dockerfile.prod -t myapp:prod .

# 传入构建参数
docker build --build-arg VERSION=1.2.3 -t myapp:1.2.3 .

# 不使用缓存
docker build --no-cache -t myapp:1.0 .
  • -t:给镜像命名并打标签。
  • 末尾的 .构建上下文(发送给守护进程的目录),而非“当前目录”简写。
  • 构建上下文默认会整个发送,用 .dockerignore 排除 .gitnode_modules 等。

.dockerignore 示例

.git
.gitignore
node_modules
*.log
.env
Dockerfile
docker-compose.yml

多阶段构建(Multi-stage)

在同一个 Dockerfile 中用多个 FROM,最终镜像只保留最后一阶段的产物,实现构建依赖与运行镜像分离,显著缩小体积。

dockerfile
# 阶段 1:构建(带完整工具链)
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .

# 阶段 2:运行(最小镜像)
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /usr/local/bin/server
USER nobody
ENTRYPOINT ["server"]
dockerfile
# 也可从其他镜像直接拷贝
FROM node:20 AS build
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html

最佳实践清单

  1. 选合适基础镜像:优先 alpine/slim 变体,小且攻击面小;固定具体版本号而非 latest
  2. 合并 RUN:用 && 连接,减少层数。
    dockerfile
    RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
  3. 利用缓存:变化少的层放前面(依赖安装在前,源码复制在后)。
  4. 用 COPY 而非 ADD:语义清晰,无隐式解压/远程拉取副作用。
  5. 非 root 运行USER 指定非特权用户。
  6. 多阶段构建:缩小最终镜像。
  7. 显式打标签:不用 latest 做生产标签。
  8. 加 HEALTHCHECK:便于编排系统判断健康。
  9. 写 .dockerignore:缩小上下文、防泄密。

实用命令:清理与体积

bash
docker system df          # 查看磁盘占用(镜像/容器/卷/构建缓存)
docker image prune -a     # 清理所有未被容器使用的镜像
docker builder prune      # 清理构建缓存
docker system prune -a    # 大扫除(慎用,会删未运行容器相关数据)