Dockerfile入门,及其问题思考

129 阅读7分钟

Dockerfile 是一种文本文件,它包含了一组指令,用于定义和构建 Docker 镜像。每条指令都构建了一层,最终形成一个可运行的 Docker 容器。Dockerfile 通常包括基础镜像、安装软件包、复制文件、设置环境变量以及定义容器启动时的命令等。

语法关键字

以下是 Dockerfile 中常用的语法关键字及其说明:

1. FROM:

    • 指定基础镜像。
    • 每个 Dockerfile 必须以 FROM 指令开始。
    • 示例:FROM ubuntu:latest

2. RUN:

    • 在镜像构建过程中执行命令。
    • 通常用于安装软件包或其他配置操作。
    • 示例:RUN apt-get update && apt-get install -y nginx

3. CMD:

    • 指定容器启动时要执行的命令或参数。
    • 只会运行最后一个 CMD 指令,如果指定多个,只有最后一个会被执行。CMD 指令会被 docker run 中的命令行参数覆盖掉。
    • (说白了,CMD 就相当于 docker run ubuntu tail -f /dev/null 命令行中的参数tail -f /dev/null,可以只是参数,也可以作为命令)
    • 示例:CMD ["nginx", "-g", "daemon off;"]

4. ENTRYPOINT:

    • 指定容器启动时要执行的命令。
    • CMD 类似,但不会被忽略,可以接受 CMD 作为参数。(或者接收 docker run 中的命令行参数。)
    • 如果有多个,只会运行最后一个。如果同时有CMD和ENTRYPOINT,那么会执行ENTRYPOINT,CMD会被当做参数传递到ENTRYPOINT
    • 示例:ENTRYPOINT ["python", "app.py"]

5. COPY:

    • 将文件或目录从宿主机复制到镜像中。
    • 示例:COPY ./src /app/src

6. ADD:

    • 类似于 COPY,但功能更强大,可以自动解压 tar 文件和下载远程文件。
    • 示例:ADD http://example.com/file.tar.gz /app/

7. WORKDIR:

    • 设置工作目录。
    • 所有后续指令(如 RUNCMDENTRYPOINT)都会在这个目录下执行。
    • 示例:WORKDIR /app

8. ENV:

    • 设置环境变量。
    • 示例:ENV PORT 8080

9. EXPOSE:

    • 声明容器会监听的端口。
    • 示例:EXPOSE 80

10. VOLUME:

    • 创建挂载点,指定一个挂载点以持久化数据或与其他容器共享数据。
    • 示例:VOLUME ["/data"]

11. USER:

    • 指定运行容器时的用户。
    • 示例:USER appuser

12. ARG:

    • 定义在构建过程中使用的变量。
    • 示例:ARG VERSION=1.0

13. LABEL:

    • 添加元数据到镜像。
    • 示例:LABEL maintainer="name@example.com"

14. ONBUILD:

    • 添加触发器,在子镜像构建过程中执行。
    • 示例:ONBUILD RUN apt-get update && apt-get install -y build-essential

15. STOPSIGNAL:

    • 设置停止容器时发送的系统调用信号。
    • 示例:STOPSIGNAL SIGTERM

示例 Dockerfile

下面是一个简单的 Dockerfile 示例,展示了如何创建一个基于 Ubuntu 的镜像,并安装 Nginx 服务器:

# 使用 Ubuntu 作为基础镜像
FROM ubuntu:latest

# 维护者信息
LABEL maintainer="yourname@example.com"

# 更新软件包列表并安装 Nginx
RUN apt-get update && apt-get install -y nginx

# 复制本地文件到镜像中
COPY index.html /var/www/html/

# 设置工作目录
WORKDIR /var/www/html/

# 暴露端口
EXPOSE 80

# 设置环境变量
ENV ENVIRONMENT=production

# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

构建和运行 Docker 镜像

  1. 构建镜像
docker build -t my-nginx-image .
  1. 运行容器
docker run -d -p 80:80 my-nginx-image

这将创建一个基于 Ubuntu 的 Docker 镜像,安装 Nginx,并启动 Nginx 服务器,监听 80 端口。

---

CMD 命令和 ENTRYPOINT 指令的区别

CMDENTRYPOINT 是 Dockerfile 中用于指定容器启动时执行命令的指令,但它们的行为和用途有所不同。

CMD 指令

  • 用途: 指定容器启动时的默认命令或参数。
  • 覆盖: 可以被 docker run 命令行上的参数覆盖。
  • 格式:
    • Shell 格式: CMD echo "Hello World"
    • Exec 格式: CMD ["echo", "Hello World"]
    • 参数格式: CMD ["--default-arg1", "--default-arg2"](当与 ENTRYPOINT 一起使用时)

示例

# 使用 Ubuntu 作为基础镜像
FROM ubuntu:latest

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

运行容器时,默认执行 echo "Hello World":

docker run myimage

可以通过命令行参数覆盖 CMD 指令:

docker run myimage echo "Hi there"
# 输出:Hi there  因为cmd的命令被覆盖了
# 但是如果执行参数没有echo,会无效,因为cmd是所有命令会被替代。

ENTRYPOINT 指令

  • 用途: 指定容器启动时要执行的主命令。
  • 覆盖: 不会被 docker run 命令行上的参数覆盖,但可以通过 --entrypoint 选项覆盖。
  • 格式:
    • Shell 格式: ENTRYPOINT echo "Hello World"
    • Exec 格式: ENTRYPOINT ["echo", "Hello World"]

示例

# 使用 Ubuntu 作为基础镜像
FROM ubuntu:latest

# 入口点命令
ENTRYPOINT ["echo", "Hello World"]

运行容器时,会始终执行 echo "Hello World":

docker run myimage

通过命令行参数传递给 ENTRYPOINT:

docker run myimage "Hi there"
# 输出: Hello World Hi there   会在后面追加参数,不会替代

结合使用 CMDENTRYPOINT

可以结合使用 CMDENTRYPOINT 来设置默认参数。

示例

# 使用 Ubuntu 作为基础镜像
FROM ubuntu:latest

# 入口点命令
ENTRYPOINT ["echo"]

# 默认参数
CMD ["Hello World"]

运行容器时,会执行 echo Hello World:

docker run myimage
# 输出: Hello World

通过命令行参数覆盖 CMD 部分,但不会覆盖 ENTRYPOINT:

docker run myimage "Hi there"
# 输出: Hi there

总结

  • CMD: 设置默认命令或参数,允许在运行容器时通过命令行参数覆盖。
  • ENTRYPOINT: 设置容器启动时的主命令,始终执行,除非通过 --entrypoint 选项覆盖。
  • 结合使用: ENTRYPOINT 设置主命令,CMD 设置默认参数,提供灵活的默认行为和覆盖机制。

注意

  • 每个 Dockerfile 只能有一个 CMD 指令和一个 ENTRYPOINT 指令。
  • 如果有多个 CMDENTRYPOINT 指令,只有最后一个会生效。
  • CMDENTRYPOINT 可以结合使用,ENTRYPOINT 指定主命令,CMD 指定参数。

---

entrypoint 中的shell格式和exec格式有啥区别

ENTRYPOINT 指令在 Dockerfile 中有两种格式:Shell 格式和 Exec 格式。它们在行为和使用场景上有一些区别。

Shell 格式

  • 语法: ENTRYPOINT command param1 param2
  • 执行方式: 使用 /bin/sh -c 运行命令,因此会在一个新的 Shell 中执行。
  • 优点: 适合执行复杂的 Shell 命令或脚本。
  • 缺点: 不易携带参数,难以与 CMD 指令结合使用。不会捕获到 docker run 中携带的参数

示例

FROM ubuntu:latest

ENTRYPOINT echo "Hello World"

这种方式会在容器启动时执行以下命令:

/bin/sh -c "echo Hello World"

Exec 格式

  • 语法: ENTRYPOINT ["executable", "param1", "param2"]
  • 执行方式: 直接执行指定的可执行文件,不会启动一个新的 Shell。
  • 优点: 更高效,更易于携带参数,推荐与 CMD 指令结合使用。可以捕获到 docker run 中携带的参数
  • 缺点: 不支持 Shell 特性(例如变量替换、管道等)。

示例

FROM ubuntu:latest

ENTRYPOINT ["echo", "Hello World"]

这种方式会直接执行以下命令:

echo "Hello World"

---

dockerfile使用shell格式,那么从docker run中携带的参数能获取追加到吗

docker run 命令中携带的参数不会自动附加到 ENTRYPOINT 中。

然而,可以通过一个小技巧来实现这种功能:在 Shell 中显式地处理传递的参数。

以下是如何实现这一点的示例:

示例 Dockerfile

FROM ubuntu:latest

# 使用 Shell 格式定义 ENTRYPOINT
ENTRYPOINT echo "Default Message:" && exec "$@"

解释

  • ENTRYPOINT echo "Default Message:" && exec "$@":
    • echo "Default Message:" 会打印默认消息。
    • exec "$@" 会执行传递给 docker run 的任何附加命令或参数。

运行容器时传递参数:

docker run myimage "Hello from Docker"

输出将是:

Default Message:
Hello from Docker

在这个例子中,我们使用 exec "$@" 将传递给 docker run 的参数作为新的命令来执行。如果不传递任何参数,默认只会执行 echo "Default Message:"

另一种实现方式

使用一个脚本来处理参数也可以实现类似的效果:

示例 Dockerfile

FROM ubuntu:latest

# 将脚本添加到镜像中
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# 使用 Shell 格式定义 ENTRYPOINT
ENTRYPOINT ["/entrypoint.sh"]

示例 entrypoint.sh 脚本

#!/bin/sh
echo "Default Message:"

# 执行传递的参数
exec "$@"
docker run myimage "Hello from Docker"

输出将是:

Default Message:
Hello from Docker

这种方式使用了一个外部脚本 entrypoint.sh,在脚本中处理传递的参数。这样可以更灵活地处理启动时的参数和命令。

---

Dockerfile 同时有 ENTRYPOINT 和 CMD,会怎么样

ENTRYPOINT 指令将会优先执行,而 CMD 指令会作为默认参数传递给 ENTRYPOINT

如果在运行容器时传递了额外的参数,这些参数会替换掉 CMD 指令中的默认参数。

注意:

ENTRYPOINT 只有是 exec 格式,才会捕获追加的参数

---

不同 Dokcerfile 的现象实验

1 ENTRYPOINT使用 exec 方式

FROM ubuntu:latest

CMD  "iamcmd1"

ENTRYPOINT ["echo", "iamentrypoint1 "]

执行效果:

➜  docker run --rm myimage3 "hello"
iamentrypoint1  hello  #会替代cmd中的参数,追加到ENTRYPOINT中;侧面印证这种属于exec方式传参


➜  docker run --rm myimage3
iamentrypoint1  /bin/sh -c "iamcmd1"  #因为CMD仍然是shell格式传参,追加到ENTRYPOINT中

2 ENTRYPOINT使用 shell 方式

FROM ubuntu:latest

CMD  "iamcmd1"

ENTRYPOINT echo "iamentrypoint1 "

执行效果:

但是但是但是但是➜  docker run --rm myimage3 "hello"
iamentrypoint1 


➜  docker run --rm myimage3
iamentrypoint1  

#永远都不会追加参数