镜像与 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]
nginx→docker.io/library/nginx:latestmyrepo/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 是构建镜像的脚本,每条指令生成一层(FROM、RUN、COPY、ADD 等会增层,CMD、ENTRYPOINT、ENV 等只写元数据)。
指令速览
| 指令 | 作用 | 关键点 |
|---|---|---|
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排除.git、node_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最佳实践清单
- 选合适基础镜像:优先
alpine/slim变体,小且攻击面小;固定具体版本号而非latest。 - 合并 RUN:用
&&连接,减少层数。dockerfileRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* - 利用缓存:变化少的层放前面(依赖安装在前,源码复制在后)。
- 用 COPY 而非 ADD:语义清晰,无隐式解压/远程拉取副作用。
- 非 root 运行:
USER指定非特权用户。 - 多阶段构建:缩小最终镜像。
- 显式打标签:不用
latest做生产标签。 - 加 HEALTHCHECK:便于编排系统判断健康。
- 写 .dockerignore:缩小上下文、防泄密。
实用命令:清理与体积
bash
docker system df # 查看磁盘占用(镜像/容器/卷/构建缓存)
docker image prune -a # 清理所有未被容器使用的镜像
docker builder prune # 清理构建缓存
docker system prune -a # 大扫除(慎用,会删未运行容器相关数据)