Daily Study
更新: 5/26/2025 字数: 0 字 时长: 0 分钟
Daily Plan
#todo
K8s学习
Dockerfile常见指令
FROM
: 指定基础镜像。所有后续指令都将基于此镜像。这是 Dockerfile 的第一条指令。例如:FROM ubuntu:latest
RUN
: 在当前镜像之上的新层中执行任何命令并提交结果。常用于安装软件包。例如:RUN apt-get update && apt-get install -y python3
CMD
: 提供容器启动时执行的默认命令。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 /app
COPY
: 从构建上下文(通常是 Dockerfile 所在的目录)复制文件或目录到镜像的文件系统中。例如:COPY . /app
ADD
: 功能与COPY
类似,但有一些额外的特性,比如可以处理远程 URL 和自动解压归档文件。一般推荐优先使用COPY
,因为它的行为更明确。例如:ADD http://example.com/file.txt /app
或ADD archive.tar.gz /app
ENV
: 设置环境变量。这些环境变量在后续的RUN
指令中可以使用,并且在容器运行时也会存在。例如:ENV APP_VERSION=1.0
ARG
: 定义构建时变量。这些变量只在 Dockerfile 构建过程中可用,容器运行时不可用。可以在docker build
时通过--build-arg
参数传递。- - 例如:ARG USER_ID=1000
EXPOSE
: 声明容器运行时监听的网络端口。这实际上是一个文档说明,并不会自动发布端口。在docker run
时需要使用-p
或-P
参数来实际映射端口。例如:EXPOSE 80
VOLUME
: 创建一个挂载点,用于持久化数据或在容器间共享数据。例如:VOLUME /data
USER
: 设置运行后续RUN
,CMD
和ENTRYPOINT
指令时使用的用户名或 UID,以及可选的用户组或 GID。例如:USER appuser
LABEL
: 为镜像添加元数据,如作者、版本等。例如: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 1
SHELL
: 允许覆盖用于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.24-buster AS builder
) 用于编译 - 第二阶段 (
FROM gcr.io/distroless/base-debian10
) 只包含运行时所需组件 - 这样避免了编译环境、开发工具链、依赖包等进入最终镜像
- 第一阶段 (
- Distroless 基础镜像:
gcr.io/distroless/base-debian10
是专为运行应用程序设计的极简镜像- 不包含 shell、包管理器和其他非必要工具
- 比标准 Debian 镜像小很多,通常只有几十 MB
- 只复制必要文件:
COPY --from=builder /src/main /main
仅复制编译好的二进制文件- 不包含源代码、中间构建产物或开发依赖