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:
-
- 设置工作目录。
- 所有后续指令(如
RUN
、CMD
、ENTRYPOINT
)都会在这个目录下执行。 - 示例:
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 镜像
- 构建镜像:
docker build -t my-nginx-image .
- 运行容器:
docker run -d -p 80:80 my-nginx-image
这将创建一个基于 Ubuntu 的 Docker 镜像,安装 Nginx,并启动 Nginx 服务器,监听 80 端口。
---
CMD 命令和 ENTRYPOINT 指令的区别
CMD
和 ENTRYPOINT
是 Dockerfile 中用于指定容器启动时执行命令的指令,但它们的行为和用途有所不同。
CMD
指令
- 用途: 指定容器启动时的默认命令或参数。
- 覆盖: 可以被
docker run
命令行上的参数覆盖。 - 格式:
-
- Shell 格式:
CMD echo "Hello World"
- Exec 格式:
CMD ["echo", "Hello World"]
- 参数格式:
CMD ["--default-arg1", "--default-arg2"]
(当与ENTRYPOINT
一起使用时)
- Shell 格式:
示例
# 使用 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"]
- Shell 格式:
示例
# 使用 Ubuntu 作为基础镜像
FROM ubuntu:latest
# 入口点命令
ENTRYPOINT ["echo", "Hello World"]
运行容器时,会始终执行 echo "Hello World"
:
docker run myimage
通过命令行参数传递给 ENTRYPOINT
:
docker run myimage "Hi there"
# 输出: Hello World Hi there 会在后面追加参数,不会替代
结合使用 CMD
和 ENTRYPOINT
可以结合使用 CMD
和 ENTRYPOINT
来设置默认参数。
示例
# 使用 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
指令。 - 如果有多个
CMD
或ENTRYPOINT
指令,只有最后一个会生效。 CMD
和ENTRYPOINT
可以结合使用,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
#永远都不会追加参数