跳转至

dockerfile

什么是Dockerfile?

Dockerfile 是一个包含一系列指令的文本文件,用于定义一个 Docker 镜像的构建过程。这些指令告诉 Docker 如何从一个基础镜像出发,安装软件、复制文件、设置环境等,最终生成一个自定义的 Docker 镜像。

Dockerfile 的基本结构

Dockerfile 文件由一系列指令组成,每条指令执行一个任务,比如: - FROM:指定基础镜像。 - RUN:运行命令来安装软件或配置环境。 - COPY/ADD:将文件从本地复制到镜像内。 - CMD/ENTRYPOINT:定义容器启动时执行的命令。 - ENV:设置环境变量。 - EXPOSE:声明容器内的端口号。 - WORKDIR:设置工作目录。

一个简单的示例:

# 基础镜像
FROM ubuntu:20.04

# 设置环境变量
ENV LANG=C.UTF-8

# 安装必要的软件
RUN apt-get update && apt-get install -y python3 python3-pip

# 复制应用程序文件
COPY app.py /app/app.py

# 设置工作目录
WORKDIR /app

# 暴露端口
EXPOSE 5000

# 容器启动时运行的命令
CMD ["python3", "app.py"]

Dockerfile 的工作原理

  1. 逐步构建镜像:Docker 使用 Dockerfile 的指令生成一个镜像,每条指令都会创建一个新的镜像层(layer)。
  2. 镜像层的缓存机制:Docker 对每一层都进行缓存,只有当某一层发生变化时,才会重新构建该层及其之后的层,从而加快构建速度。
  3. 联合文件系统(UnionFS):镜像的多层设计基于联合文件系统,允许镜像层以只读模式叠加,减少存储需求。
  4. 最终生成镜像:所有层叠加后形成最终的镜像,镜像是只读的,容器启动时会在镜像之上添加一个可写层。

Dockerfile 的核心原理

  1. 镜像分层:镜像由多个只读层组成,新增的每一层记录的是前一层的增量变化。
  2. 指令触发动作:每条指令对应一个文件系统的变更或操作,比如复制文件、安装软件等。
  3. 自动化与复用:通过 Dockerfile,可以实现镜像构建的自动化和重复利用。

你可以通过 docker build 命令构建镜像,例如:

docker build -f /path/to/dockerfile文件 -t my-container-image /path/to/

总结

Dockerfile 是 Docker 自动化构建镜像的重要工具,其原理是通过分层机制和联合文件系统,将每条指令生成的变化叠加在一起,形成一个高效、灵活的镜像构建流程。

查看分层

在 Docker 中,镜像的分层信息可以通过以下几种方法查看。分层的核心思想是,每一层代表 Dockerfile 中的一条指令所生成的文件系统变更。


1. 使用 docker history 命令

docker history 命令可以列出镜像的分层信息,包括: - 创建时间 - 大小 - 具体的 Dockerfile 指令

示例:

docker history <镜像名或镜像ID>

输出格式:

IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
<image_id>     3 days ago      /bin/sh -c apt-get install -y python3           50MB      -
<image_id>     3 days ago      /bin/sh -c apt-get update                       5MB       -
<image_id>     3 days ago      /bin/sh -c #(nop) WORKDIR /app                  0B        -
<image_id>     3 days ago      /bin/sh -c #(nop) COPY file:abc1234 in /app/    10kB      -
<image_id>     3 days ago      FROM ubuntu:20.04                               29MB      -
  • CREATED BY:显示每一层是由哪条指令生成的。
  • SIZE:显示每一层的大小。
  • IMAGE:对应的镜像 ID(可以为空,如果是基础层)。

2. 使用 docker image inspect 命令

docker image inspect 命令提供更详细的镜像元信息,其中包括分层信息。

示例:

docker image inspect <镜像名或镜像ID>

输出格式:

[
    {
        "Id": "sha256:...",
        "RepoTags": [
            "my-image:latest"
        ],
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:12345...",
                "sha256:67890...",
                "sha256:abcde..."
            ]
        },
        ...
    }
]
  • RootFS -> Layers:列出了镜像的每一层的哈希值。
  • 这些哈希值对应的是每一层的具体内容,可以与镜像缓存机制关联。

3. 使用第三方工具查看分层

如果想直观地查看镜像的每一层及其内容,可以使用一些可视化工具,如: 1. Dive: - Dive 是一个开源工具,用于分析 Docker 镜像的分层,并显示每一层的文件变更。 - 安装

brew install dive  # macOS
apt install dive   # Ubuntu
- 使用
dive <镜像名或镜像ID>
- 功能: - 直观查看每一层的文件系统变化。 - 分析镜像中哪些文件占用空间,优化镜像大小。

  1. Container-diff
  2. Google 开发的工具,用于比较 Docker 镜像之间的差异,包括分层和文件变化。

4. 手动查看分层文件内容

在 Docker 的存储引擎中,镜像层实际存储在宿主机上的一个文件夹中,路径因存储驱动不同而异。以下是常见路径: - 默认路径/var/lib/docker/ - 分层目录: - 对于 overlay2 驱动:/var/lib/docker/overlay2/ - 每一层的数据存储在该目录下。

注意:

直接查看文件层次适用于高级用户,一般情况下不推荐手动修改这些文件。

  1. 使用 docker history 查看每一层的指令和大小。
  2. 使用 docker image inspect 查看镜像的分层哈希。
  3. 使用工具(如 Dive)直观查看和优化分层。
  4. 通过文件系统查看原始数据(不建议手动操作)。

指令含义


基础指令

  1. FROM
    指定基础镜像,必须是 Dockerfile 的第一条指令(除非使用 ARG 定义构建时变量)。

    FROM ubuntu:20.04

  2. RUN
    在构建镜像时运行命令。通常用于安装依赖、配置环境等。

    RUN apt-get update && apt-get install -y curl

  3. CMD
    指定容器启动时运行的默认命令或脚本。如果镜像中没有提供其他命令,容器启动时会运行该指令。

    CMD ["echo", "Hello World"]

  4. ENTRYPOINT
    配置容器的主进程(可执行文件)。通常与 CMD 配合使用以添加默认参数。

    ENTRYPOINT ["python", "app.py"]

  5. COPY
    将本地文件复制到镜像中。

    COPY . /app

  6. ADD
    类似 COPY,但支持自动解压压缩文件和从 URL 下载文件。

    ADD https://example.com/file.tar.gz /data/

  7. WORKDIR
    设置后续指令的工作目录。

    WORKDIR /app

  8. EXPOSE
    声明容器监听的网络端口。

    EXPOSE 8080

  9. ENV
    设置环境变量。

    ENV APP_ENV=production

  10. ARG
    定义构建时的变量。

    ARG VERSION=1.0

  11. VOLUME
    定义挂载点,以便持久化数据。

    VOLUME /data

  12. USER
    指定运行容器的用户。

    USER nonrootuser

  13. ONBUILD
    定义触发器指令,仅在从此镜像构建其他镜像时运行。

    ONBUILD RUN echo "This is a parent image"

  14. LABEL
    添加元数据到镜像中。

    LABEL maintainer="you@example.com"

  15. SHELL
    修改默认的 shell 环境(如 shbash)。

    SHELL ["powershell", "-Command"]


扩展功能指令

  1. HEALTHCHECK
    配置容器健康检查指令。

    HEALTHCHECK --interval=30s CMD curl -f http://localhost/ || exit 1

  2. STOPSIGNAL
    指定容器停止时发送的系统信号。

    STOPSIGNAL SIGKILL

  3. MAINTAINER (已弃用,使用 LABEL 替代)
    设置镜像的维护者信息。

    MAINTAINER yourname@example.com


常见组合

  • CMDENTRYPOINT 配合:

    ENTRYPOINT ["python"]
    CMD ["app.py"]

  • 使用 ARG 配置版本:

    ARG APP_VERSION=1.0
    ENV VERSION=$APP_VERSION

  • 使用 COPYWORKDIR 构建项目:

    WORKDIR /app
    COPY . .
    RUN npm install

完整的 Dockerfile 指令及其说明可以在以下几个网站上找到:

Docker 官方文档
Docker 官方提供了权威的 Dockerfile 指令参考,包括每个指令的用法和示例:Dockerfile Reference

CMDENTRYPOINT区别

CMDENTRYPOINT 是 Dockerfile 中用于指定容器启动时执行的命令,但它们的功能和用法有所不同。以下是它们的主要区别和使用场景:


1. 基本区别

特性 CMD ENTRYPOINT
目的 为容器提供默认命令,可以被运行时覆盖。 定义容器的固定入口点,通常不可轻易覆盖。
覆盖性 在运行容器时,命令行参数可以直接替换 CMD。 在运行容器时,命令行参数会被附加到 ENTRYPOINT 后面
灵活性 灵活,可以让用户在运行时轻松指定新命令。 更适合固定功能的应用程序(如服务或脚本)。

2. 用法比较

CMD 示例

FROM ubuntu:20.04

# CMD 定义默认命令
CMD ["echo", "Hello, World!"]

运行容器:

docker run my-image
输出:
Hello, World!

如果运行时指定新命令:

docker run my-image ls
输出:
<当前目录文件列表>
- 覆盖性:运行时的 ls 替换了 CMD 的默认命令。


ENTRYPOINT 示例

FROM ubuntu:20.04

# ENTRYPOINT 定义固定入口点
ENTRYPOINT ["echo"]

运行容器:

docker run my-image Hello
输出:
Hello

如果运行时指定新参数:

docker run my-image "Hello, Docker!"
输出:
Hello, Docker!
- 附加性:运行时的参数被附加到 ENTRYPOINT 后面。


3. CMD 和 ENTRYPOINT 结合使用

可以在一个 Dockerfile 中同时使用 ENTRYPOINTCMD,它们的组合方式如下:

FROM ubuntu:20.04

# 定义固定的入口点
ENTRYPOINT ["echo"]

# 定义默认参数
CMD ["Hello, World!"]

运行容器:

docker run my-image
输出:
Hello, World!

运行容器时覆盖参数:

docker run my-image "Hello, Docker!"
输出:
Hello, Docker!

原理

  • CMD 只在没有运行时参数时才会生效,作为 ENTRYPOINT 的默认参数。

4. 覆盖 ENTRYPOINT 的方法

如果需要在运行时临时覆盖 ENTRYPOINT,可以使用 --entrypoint 参数:

docker run --entrypoint /bin/bash my-image


5. 使用建议

  • 使用 CMD
  • 用于提供容器的默认命令,但允许用户在运行时轻松替换。
  • 适用于通用镜像(如基础镜像)。
  • 使用 ENTRYPOINT
  • 用于定义固定功能的容器,例如服务或脚本。
  • 适合需要强制执行特定命令逻辑的场景。
  • 两者结合
  • 当固定的命令需要配合默认参数时,使用 ENTRYPOINT + CMD
  • 例如:ENTRYPOINT 定义工具或服务,CMD 提供默认参数。

总结

  • CMD 是默认命令,可被覆盖。
  • ENTRYPOINT 是入口点,默认固定,但可附加参数。
  • 根据镜像的用途和灵活性需求选择合适的指令。