Daily Study
更新: 1/6/2026 字数: 0 字 时长: 0 分钟
Daily Plan
#todo
K8s学习
Dockerfile常见指令
FROM: 指定基础镜像。所有后续指令都将基于此镜像。这是 Dockerfile 的第一条指令。例如:FROM ubuntu:latestRUN: 在当前镜像之上的新层中执行任何命令并提交结果。常用于安装软件包。例如:RUN apt-get update && apt-get install -y python3CMD: 提供容器启动时执行的默认命令。Dockerfile 中只能有一条CMD指令。如果有多条,只有最后一条会生效。如果docker run时指定了命令,则CMD的命令会被覆盖。例如:CMD ["python3", "app.py"]ENTRYPOINT: 配置容器启动时运行的命令。与CMD类似,但ENTRYPOINT的命令不会被docker run命令行参数轻易覆盖,除非使用--entrypoint选项。通常用于将容器配置为可执行文件。例如:ENTRYPOINT ["nginx", "-g", "daemon off;"]WORKDIR: 为RUN,CMD,ENTRYPOINT,COPY和ADD指令设置工作目录。如果目录不存在,会自动创建。例如:WORKDIR /appCOPY: 从构建上下文(通常是 Dockerfile 所在的目录)复制文件或目录到镜像的文件系统中。例如:COPY . /appADD: 功能与COPY类似,但有一些额外的特性,比如可以处理远程 URL 和自动解压归档文件。一般推荐优先使用COPY,因为它的行为更明确。例如:ADD http://example.com/file.txt /app或ADD archive.tar.gz /appENV: 设置环境变量。这些环境变量在后续的RUN指令中可以使用,并且在容器运行时也会存在。例如:ENV APP_VERSION=1.0ARG: 定义构建时变量。这些变量只在 Dockerfile 构建过程中可用,容器运行时不可用。可以在docker build时通过--build-arg参数传递。- - 例如:ARG USER_ID=1000EXPOSE: 声明容器运行时监听的网络端口。这实际上是一个文档说明,并不会自动发布端口。在docker run时需要使用-p或-P参数来实际映射端口。例如:EXPOSE 80VOLUME: 创建一个挂载点,用于持久化数据或在容器间共享数据。例如:VOLUME /dataUSER: 设置运行后续RUN,CMD和ENTRYPOINT指令时使用的用户名或 UID,以及可选的用户组或 GID。例如:USER appuserLABEL: 为镜像添加元数据,如作者、版本等。例如:LABEL maintainer="example@example.com"ONBUILD: 当构建的镜像作为另一个构建的基础镜像时,ONBUILD指令才会被执行。例如:ONBUILD RUN echo "Building on top of this image"HEALTHCHECK: 指定如何测试容器以检查其是否仍在工作。例如:HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1SHELL: 允许覆盖用于RUN,CMD,ENTRYPOINT指令中 shell 形式命令的默认 shell。默认 shell 在 Linux 上是["/bin/sh", "-c"],在 Windows 上是["cmd", "/S", "/C"]。例如:SHELL ["/bin/bash", "-c"]
镜像打包技巧
- 保持镜像精简 (Keep Images Small):
- 选择合适的基础镜像: 从一个尽可能小的官方基础镜像开始(例如
alpine版本)。 - 清理不必要的依赖和文件: 在
RUN指令的同一层中删除缓存和不需要的软件包。例如,apt-get clean和rm -rf /var/lib/apt/lists/*。 - 使用
.dockerignore文件: 排除构建上下文中不需要的文件和目录,减少发送到 Docker 守护进程的数据量,加快构建速度。
- 选择合适的基础镜像: 从一个尽可能小的官方基础镜像开始(例如
- 利用构建缓存 (Leverage Build Cache):
- 合理安排指令顺序: 将不经常变化的指令(如安装固定版本的依赖)放在 Dockerfile 的前面,将经常变化的指令(如
COPY源代码)放在后面。这样可以最大限度地利用缓存。 - 避免不必要的缓存失效: 确保只有在真正需要时才修改文件。
- 合理安排指令顺序: 将不经常变化的指令(如安装固定版本的依赖)放在 Dockerfile 的前面,将经常变化的指令(如
- 多阶段构建 (Multi-stage Builds):
- 使用多阶段构建可以将编译、测试等构建过程和最终的运行时环境分离。最终镜像只包含运行应用程序所必需的文件,大大减小镜像体积并提高安全性。
- 例如,一个阶段使用包含构建工具的镜像编译代码,另一个阶段将编译好的产物复制到一个干净的运行时镜像中。
- 最小化层数 (Minimize Layers):
- 虽然缓存很重要,但过多的层也会增加镜像的复杂性和潜在的体积。尽可能将相关的
RUN命令合并到一条指令中,使用&&连接。 - 例如:
RUN apt-get update && apt-get install -y package1 package2 && rm -rf /var/lib/apt/lists/*。
- 虽然缓存很重要,但过多的层也会增加镜像的复杂性和潜在的体积。尽可能将相关的
- 明确指令 (Be Specific):
- 明确指定软件包版本: 例如
apt-get install -y python3=3.9.*,避免因基础镜像更新导致构建失败或行为不一致。 - 使用
COPY而不是ADD: 除非你需要ADD的特定功能(如解压或添加远程文件),否则优先使用COPY,因为它的行为更透明。
- 明确指定软件包版本: 例如
- 安全考虑 (Security Considerations):
- 不要以 root 用户运行: 在 Dockerfile 的末尾使用
USER指令切换到非 root 用户运行应用程序,以减少潜在的安全风险。 - 不存储敏感数据: 不要在 Dockerfile 中硬编码密码、密钥等敏感信息。使用环境变量、卷或者 Docker secrets 来管理。
- 定期更新基础镜像和依赖: 修复已知的安全漏洞。
- 不要以 root 用户运行: 在 Dockerfile 的末尾使用
- 使用
CMD和ENTRYPOINT的最佳实践:- 通常将
ENTRYPOINT用于设置镜像的主要执行命令,将CMD用于传递默认参数。 - 推荐使用 JSON 数组格式(exec form),例如
CMD ["executable", "param1", "param2"],而不是 shell 格式CMD executable param1 param2,以避免 shell 解析带来的问题。
- 通常将
如下可以将 300MB 大小的镜像变成只有 20MB 的镜像,甚至压缩上传到 DockerHub 后大小只有 10MB!:
# Dockerfile
FROM golang:1.16-buster AS builder
RUN mkdir /src
ADD . /src
WORKDIR /src
RUN go env -w GO111MODULE=auto
RUN go build -o main .
FROM gcr.io/distroless/base-debian10
WORKDIR /
COPY --from=builder /src/main /main
EXPOSE 3000
ENTRYPOINT ["/main"]- 多阶段构建 (Multi-Stage Build):
- 第一阶段 (
FROM golang:1.16-buster AS builder) 用于编译 - 第二阶段 (
FROM gcr.io/distroless/base-debian10) 只包含运行时所需组件 - 这样避免了编译环境、开发工具链、依赖包等进入最终镜像
- 第一阶段 (
- Distroless 基础镜像:
gcr.io/distroless/base-debian10是专为运行应用程序设计的极简镜像- 不包含 shell、包管理器和其他非必要工具
- 比标准 Debian 镜像小很多,通常只有几十 MB
- 只复制必要文件:
COPY --from=builder /src/main /main仅复制编译好的二进制文件- 不包含源代码、中间构建产物或开发依赖
