当前位置: 首页 > news >正文

【Docker-Day 7】揭秘 Dockerfile 启动指令:CMD、ENTRYPOINT、ENV、ARG 与 EXPOSE 详解

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Python系列文章目录

Go语言系列文章目录

Docker系列文章目录

01-【Docker-Day 1】告别部署噩梦:为什么说 Docker 是每个开发者的必备技能?
02-【Docker-Day 2】从零开始:手把手教你在 Windows、macOS 和 Linux 上安装 Docker
03-【Docker-Day 3】深入浅出:彻底搞懂 Docker 的三大核心基石——镜像、容器与仓库
04-【Docker-Day 4】从创建到删除:一文精通 Docker 容器核心操作命令
05-【Docker-Day 5】玩转 Docker 镜像:search, pull, tag, rmi 四大金刚命令详解
06-【Docker-Day 6】从零到一:精通 Dockerfile 核心指令 (FROM, WORKDIR, COPY, RUN)
07-【Docker-Day 7】揭秘 Dockerfile 启动指令:CMD、ENTRYPOINT、ENV、ARG 与 EXPOSE 详解


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • Python系列文章目录
  • Go语言系列文章目录
  • Docker系列文章目录
  • 摘要
  • 一、`CMD` 与 `ENTRYPOINT`:定义容器的最终使命
    • 1.1 `CMD` 指令:容器的默认命令
      • 1.1.1 `CMD` 的三种形式
        • (1) Exec 形式 (推荐)
        • (2) Shell 形式
        • (3) `ENTRYPOINT` 的默认参数形式
      • 1.1.2 `CMD` 的核心特性:易于覆盖
    • 1.2 `ENTRYPOINT` 指令:容器的“可执行程序”
      • 1.2.1 `ENTRYPOINT` 的两种形式
        • (1) Exec 形式 (推荐)
        • (2) Shell 形式
      • 1.2.2 `ENTRYPOINT` 的核心特性:参数追加
    • 1.3 `CMD` 与 `ENTRYPOINT` 的巅峰对决与珠联璧合
      • 1.3.1 对比总结
      • 1.3.2 最佳实践:`ENTRYPOINT` + `CMD` 联合使用
  • 二、`ENV` 与 `ARG`:构建时与运行时的变量注入
    • 2.1 `ARG`:构建时的“临时演员”
      • 2.1.1 `ARG` 的定义与使用
      • 2.1.2 `ARG` 的作用域与局限性
    • 2.2 `ENV`:容器内的“永久居民”
      • 2.2.1 `ENV` 的定义与使用
      • 2.2.2 `ENV` 的灵活性:运行时覆盖
    • 2.3 `ARG` vs. `ENV`:如何选择?
  • 三、`EXPOSE` 与 `.dockerignore`:端口声明与构建优化
    • 3.1 `EXPOSE`:声明容器的“服务窗口”
      • 3.1.1 `EXPOSE` 的真正作用
      • 3.1.2 `EXPOSE` vs `-p` 参数
    • 3.2 `.dockerignore`:构建上下文的“瘦身器”
      • 3.2.1 `.dockerignore` 的作用与语法
  • 四、总结


摘要

在上一篇文章【Docker-Day 6】中,我们初步掌握了 Dockerfile 的基础指令 FROMWORKDIRCOPYRUN,成功为应用构建了一个基本的镜像“安装包”。然而,一个真正生产级别的镜像,不仅需要能被构建,更需要能灵活、正确地“跑起来”。本文将深入探讨 Dockerfile 的另一半核心内容——启动与配置指令。我们将详细剖析 CMDENTRYPOINT 的爱恨情仇,揭示它们如何共同决定容器的启动行为;辨析 ENVARG 在构建时与运行时传递变量的异同;并阐明 EXPOSE 的声明作用以及 .dockerignore 文件在优化构建过程中的重要性。学完本章,你将能够构建出配置更灵活、行为更可控、镜像更精简的专业级 Docker 镜像。

一、CMDENTRYPOINT:定义容器的最终使命

当我们通过 docker run 启动一个容器时,我们期望它执行一个特定的任务,比如启动一个 Web 服务、运行一个批处理脚本,或者进入一个交互式 Shell。CMDENTRYPOINT 这两个指令正是用来定义这个“默认任务”的,但它们之间存在着微妙而关键的区别。

1.1 CMD 指令:容器的默认命令

CMD 指令用于为执行中的容器提供默认命令。如果 docker run 命令后面指定了其他命令,则 CMD 的设置会被覆盖。

1.1.1 CMD 的三种形式

CMD 有三种语法形式,理解它们的差异至关重要。

(1) Exec 形式 (推荐)

这是 CMD 的首选形式,它以 JSON 数组的格式定义了要执行的命令及其参数。

  • 语法: CMD ["executable", "param1", "param2"]
  • 特点: 命令和参数被直接解析,不会通过 shell 执行。这意味着像 $HOME 这样的环境变量不会被 shell 替换。

示例

# Dockerfile
FROM ubuntu
CMD ["/bin/echo", "Hello, Docker"]

构建并运行时,它会直接执行 /bin/echo 命令,输出 “Hello, Docker”。

(2) Shell 形式

这种形式将命令作为字符串,它会被包裹在 sh -c 中执行。

  • 语法: CMD command param1 param2
  • 特点: 由于通过 shell 执行,可以使用 shell 的特性,如环境变量替换、管道等。

示例

# Dockerfile
FROM ubuntu
ENV NAME=World
CMD echo "Hello, $NAME"

运行时,shell 会将 $NAME 替换为 “World”,最终输出 “Hello, World”。

(3) ENTRYPOINT 的默认参数形式

Dockerfile 中同时定义了 ENTRYPOINT 时,CMD 的内容会作为 ENTRYPOINT 的默认参数。我们将在后面详细讨论这种用法。

1.1.2 CMD 的核心特性:易于覆盖

CMD 的最大特点就是它的值可以被 docker run 命令后面的参数轻松覆盖。

示例
假设我们有以下 Dockerfile:

# Dockerfile
FROM ubuntu
CMD ["/bin/echo", "Default command"]

构建镜像 my-ubuntu: docker build -t my-ubuntu .

  • 运行默认命令
    docker run my-ubuntu
    # 输出: Default command
    
  • 覆盖默认命令
    docker run my-ubuntu /bin/ls -l /
    # 输出: (类似 ls -l / 的结果,CMD 被完全覆盖)
    # total 64
    # dr-xr-xr-x   1 root root 4096 Jul 10 05:28 bin
    # ...
    

1.2 ENTRYPOINT 指令:容器的“可执行程序”

ENTRYPOINT 指令将容器配置为像一个可执行文件一样运行。它的命令不容易在 docker run 时被覆盖,而是将 docker run 后的参数作为其自身的参数来接收。

1.2.1 ENTRYPOINT 的两种形式

ENTRYPOINT 同样有 Exec 和 Shell 两种形式。

(1) Exec 形式 (推荐)

这是 ENTRYPOINT 的主要使用形式,它定义了容器启动时必须执行的固定命令。

  • 语法: ENTRYPOINT ["executable", "param1", "param2"]

示例

# Dockerfile
FROM alpine
ENTRYPOINT ["ping", "localhost"]

构建镜像 my-pinger: docker build -t my-pinger .

无论你 docker run my-pinger 后面跟什么,它都会尝试执行 ping localhost,并将后面的参数追加给它。

(2) Shell 形式
  • 语法: ENTRYPOINT command param1 param2
  • 问题: Shell 形式的 ENTRYPOINT 会导致 docker run 后面的参数无法传递,并且 CMD 的值也无法作为其参数。因此,强烈不推荐使用此形式。

1.2.2 ENTRYPOINT 的核心特性:参数追加

CMD 不同,docker run 提供的参数会被追加到 ENTRYPOINT (Exec 形式) 的后面,而不是覆盖它。

示例
基于上面的 my-pinger 镜像:

# Dockerfile
FROM alpine
ENTRYPOINT ["ping"] # 将 ping 作为固定入口
CMD ["localhost"] # 提供默认的 ping 目标

构建镜像 my-pinger: docker build -t my-pinger .

  • 运行默认命令
    docker run my-pinger
    # 实际执行: ping localhost
    
  • 传递新参数
    docker run my-pinger google.com
    # 实际执行: ping google.com (google.com 覆盖了 CMD 的 localhost)
    
  • 覆盖 ENTRYPOINT
    虽然不推荐,但你依然可以通过 --entrypoint 标志来强制覆盖它。
    docker run --entrypoint /bin/echo my-pinger "Hello Override"
    # 输出: Hello Override
    

1.3 CMDENTRYPOINT 的巅峰对决与珠联璧合

理解了两者的区别后,我们才能在合适的场景下做出最佳选择。

1.3.1 对比总结

特性CMDENTRYPOINT (Exec 形式)
目的提供容器启动的 默认 命令和参数。将容器配置为一个 可执行程序,定义固定的入口点。
覆盖行为docker run 后的参数会 完全覆盖 CMDdocker run 后的参数会 追加ENTRYPOINT 之后,作为其参数。
主要场景1. 为 ENTRYPOINT 提供默认参数。<br>2. 独立的、易于覆盖的容器命令。1. 构建行为类似命令行的镜像(如 ping)。<br>2. 结合 CMD 使用,固定主命令,让 CMD 提供默认参数。

1.3.2 最佳实践:ENTRYPOINT + CMD 联合使用

ENTRYPOINTCMD 结合使用是构建灵活且强大的镜像的黄金法则。

  • ENTRYPOINT: 设置固定的、不会改变的主命令。
  • CMD: 提供该主命令的默认参数,这些参数可以被 docker run 轻松覆盖。

场景:创建一个通用的 curl 工具容器

# Dockerfile
FROM alpine
# 安装 curl
RUN apk add --no-cache curl
# 将 curl 设置为容器的固定入口程序
ENTRYPOINT ["curl"]
# 提供一个默认的参数,比如请求帮助文档
CMD ["--help"]

构建镜像 my-curl: docker build -t my-curl .

使用演示

  1. 运行默认命令(查看帮助):

    docker run --rm my-curl
    # 输出 curl 的帮助信息
    # curl: try 'curl --help' or 'curl --manual' for more information
    
  2. curl 传递新的参数(访问网站):

    docker run --rm my-curl -sL https://www.google.com
    # 输出 Google 首页的 HTML
    
  3. curl 传递多个参数(带请求头):

    docker run --rm my-curl -I httpbin.org/get
    # 输出 httpbin.org/get 的响应头信息
    

通过这种模式,我们创建了一个行为非常清晰的工具镜像:它就是 curl,而用户需要做的只是像在普通终端里一样提供 curl 所需的参数。

二、ENVARG:构建时与运行时的变量注入

在构建和运行镜像时,我们常常需要传递一些配置信息,例如版本号、数据库密码、API 地址等。ARGENV 就是为此而生,但它们的作用域和生命周期截然不同。

2.1 ARG:构建时的“临时演员”

ARG (Argument) 指令定义了一个变量,用户可以在构建时通过 docker build 命令使用 --build-arg <varname>=<value> 标志来传递它。

2.1.1 ARG 的定义与使用

ARG 定义的变量仅在 docker build 过程中有效,一旦镜像构建完成,ARG 变量就不再存在。

  • 语法: ARG <name>[=<default value>]

示例:构建时指定应用版本

# Dockerfile
FROM alpine
# 定义一个构建参数,并设置默认值为 1.0
ARG APP_VERSION=1.0
# 在构建过程中使用这个参数
RUN echo "Building application version: ${APP_VERSION}" > /app_version.txt
# 你也可以在 ENV 中使用 ARG
ENV APP_VERSION_ENV=${APP_VERSION}

构建过程

  1. 使用默认值构建

    docker build -t my-app:default .
    

    构建日志会显示 Building application version: 1.0

  2. 传递新值构建

    docker build --build-arg APP_VERSION=2.5-beta -t my-app:latest .
    

    构建日志会显示 Building application version: 2.5-beta

2.1.2 ARG 的作用域与局限性

ARG 的作用域从它在 Dockerfile 中被定义的那一行开始。如果 ARGFROM 之前定义,它只能被 FROM 指令使用。

核心局限ARG 是构建时变量。容器运行时无法访问 ARG 变量的值,除非像上例一样,你将它的值赋给了 ENV 变量。这使得 ARG 非常适合传递那些不希望残留在最终镜像中的敏感信息(如私有仓库的 token)或纯粹的构建时配置。

2.2 ENV:容器内的“永久居民”

ENV (Environment) 指令用于为容器设置环境变量。这个变量在后续的 RUN 指令中可用,并且在容器启动后也会持久存在。

2.2.1 ENV 的定义与使用

ENV 设置的环境变量是镜像元数据的一部分,并且会传递给所有从该镜像创建的容器。

  • 语法:
    • ENV <key> <value> (单条)
    • ENV <key1>=<value1> <key2>=<value2> ... (多条,推荐,可减少镜像层数)

示例:配置应用环境

# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY . .
# 设置环境变量
ENV NODE_ENV=production \API_URL=http://api.example.com/v1 \PORT=8080
# 声明端口,我们将在下一节讨论
EXPOSE 8080
# 启动命令会读取这些环境变量
CMD ["node", "server.js"]

2.2.2 ENV 的灵活性:运行时覆盖

CMD 类似,ENV 设置的环境变量也可以在容器启动时被覆盖。

运行演示

# 使用默认 API_URL
docker run my-node-app# 覆盖 API_URL 用于测试环境
docker run -e "API_URL=http://test-api.example.com/v1" my-node-app# 覆盖端口并映射到宿主机
docker run -e "PORT=3000" -p 8000:3000 my-node-app

2.3 ARG vs. ENV:如何选择?

特性ARGENV
生命周期仅在 构建时 有效,镜像构建完成后失效。构建时运行时 都有效。
持久性不会持久化到镜像或容器中。会作为镜像元数据持久化,并成为容器的环境变量。
设置方式ARG name=value in Dockerfile;<br>--build-arg in docker buildENV key=value in Dockerfile;<br>-e in docker run
主要用途1. 定义构建时配置,如版本号、依赖源。<br>2. 传递构建时所需的秘密信息(如 token),避免泄露。1. 定义应用运行所需的配置,如数据库连接、端口、环境模式 (prod/dev)。<br>2. 设置路径等,方便 Dockerfile 后续指令使用。

简单总结

  • 需要在构建时传入、但不希望在最终容器里存在的变量,用 ARG
  • 需要在构建时设置、且希望在最终容器里继续使用的变量,用 ENV

三、EXPOSE.dockerignore:端口声明与构建优化

最后,我们来看两个虽然不直接影响容器启动命令,但对于镜像的使用和构建效率至关重要的指令。

3.1 EXPOSE:声明容器的“服务窗口”

EXPOSE 指令声明了容器在运行时监听的网络端口。

3.1.1 EXPOSE 的真正作用

  • 语法: EXPOSE <port> [<port>/<protocol>...] (protocol 默认为 tcp)

一个常见的误区是认为 EXPOSE 会自动将容器端口发布到宿主机上。这是错误的!

EXPOSE 的真正作用是:

  1. 文档化: 它告诉镜像使用者,这个容器内的应用程序期望在哪个端口上提供服务。这是一种元数据,方便人和工具理解镜像。
  2. 自动化辅助: 当使用 docker run -P (大写 P) 运行时,Docker 会自动读取 EXPOSE 的端口,并将其随机映射到宿主机的一个高位端口上。

示例

# Dockerfile
FROM nginx
# Nginx 默认监听 80 端口
EXPOSE 80
  • 使用 -P 自动映射
    docker run -d -P nginx
    docker ps
    # 输出类似:
    # CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                     NAMES
    # 9e8a7b6c5d4f   nginx     "nginx -g 'daemon of…"   5 seconds ago    Up 4 seconds    0.0.0.0:32768->80/tcp     goofy_nobel
    
    Docker 自动将容器的 80 端口映射到了宿主机的 32768 端口。

3.1.2 EXPOSE vs -p 参数

无论 Dockerfile 中是否有 EXPOSE 指令,真正控制端口映射的始终是 docker run 命令的 -p (小写 p) 或 -P 参数。

  • -p <host_port>:<container_port>: 精确指定将容器的哪个端口映射到宿主机的哪个端口。
  • -P: 自动映射所有 EXPOSE 声明的端口。

结论EXPOSE 是一个非常有用的声明性指令,建议始终为你的网络服务添加 EXPOSE,但切记它本身不具备发布端口的功能

3.2 .dockerignore:构建上下文的“瘦身器”

当执行 docker build . 时,命令末尾的 . 表示当前的目录是“构建上下文”(build context)。Docker 客户端会将这个上下文中的所有文件和目录打包发送给 Docker 守护进程。如果目录中包含大量无关文件(如 .git 目录、本地依赖 node_modules、日志文件、IDE 配置文件等),将会导致:

  1. 构建缓慢:发送巨大的上下文会消耗大量时间。
  2. 缓存失效:不必要的文件变动可能导致 Docker 缓存失效,从而重新执行耗时操作。
  3. 镜像臃肿:如果误将这些文件 COPY 进镜像,会导致镜像体积不必要地增大。
  4. 安全风险:可能泄露敏感信息,如 .env 文件、密钥等。

3.2.1 .dockerignore 的作用与语法

.dockerignore 文件就是用来解决这个问题的。它的工作方式类似 .gitignore,你可以在其中列出需要从构建上下文中排除的文件和目录。

示例 .dockerignore 文件

# 忽略版本控制目录
.git
.gitignore# 忽略 Node.js 的本地依赖目录
node_modules# 忽略日志文件和 npm 调试日志
*.log
npm-debug.log# 忽略 IDE 和操作系统生成的文件
.idea
.vscode
*.DS_Store# 忽略 Docker 相关文件
Dockerfile
.dockerignore# 忽略本地配置文件
.env.local

将此文件放在构建上下文的根目录(通常是项目根目录),docker build 时就会自动忽略这些文件,从而实现构建过程的“瘦身”。

四、总结

通过对 Dockerfile 启动与配置相关指令的深入学习,我们现在可以构建出更加专业和实用的 Docker 镜像。以下是本文的核心要点:

  1. CMD vs ENTRYPOINT: CMD 提供可被轻松覆盖的默认命令ENTRYPOINT 定义容器的固定入口,将 docker run 参数作为自己的参数。最佳实践是将两者结合,用 ENTRYPOINT 指定主程序,用 CMD 提供默认参数。

  2. ARG vs ENV: ARG构建时的临时变量,用于配置构建过程,不会保留在最终镜像中;ENV运行时的环境变量,用于配置应用程序,会永久存在于容器中。

  3. EXPOSE 的作用: EXPOSE 是一种元数据声明,用于指明容器内服务监听的端口,它本身不发布端口。真正的端口映射由 docker run-p-P 参数完成。

  4. .dockerignore 的重要性: 通过创建 .dockerignore 文件,可以从构建上下文中排除无关文件,从而加快构建速度、减小镜像体积、增强安全性,是 Dockerfile 最佳实践中不可或缺的一环。

熟练掌握这些指令,你将能自如地控制容器的启动行为、灵活地注入配置,并优化整个镜像的构建流程,为迈向更复杂的 Docker 应用打下坚实的基础。


http://www.xdnf.cn/news/1155259.html

相关文章:

  • 常用框架知识
  • Python基础-列表
  • 【Lua】大G表
  • 06 51单片机之矩阵键盘
  • 【Kafka】深入理解 Kafka MirrorMaker2 - 实战篇
  • 链表的基本操作
  • 费曼学习法
  • 吴恩达机器学习笔记(3)—线性代数回顾(可选)
  • 嵌入式硬件篇---按键
  • Nginx的location匹配规则
  • Android 项目中如何在执行 assemble 或 Run 前自动执行 clean 操作?
  • Go语言--语法基础6--基本数据类型--map类型
  • Node.js 中基于请求 ID 实现简单队列(即时阻止策略/排队等待策略)
  • 在NLP深层语义分析中,深度学习和机器学习的区别与联系
  • 【数据结构】二维差分数组
  • 技术演进中的开发沉思-40 MFC系列:多线程协作
  • JavaScript平滑滚动与锚点偏移控制的完整指南
  • InfluxDB 核心概念与发展历程全景解读(二)
  • 18.TaskExecutor获取ResourceManagerGateway
  • Unity笔记——Unity 封装方法指南
  • OpenCV 入门知识:图片展示、摄像头捕获、控制鼠标及其 Trackbar(滑动条)生成!
  • QT无边框窗口
  • 2025 年科技革命时刻表:四大关键节点将如何重塑未来?
  • 详解Mysql Order by排序底层原理
  • RK3588 编译 Android 13 镜像方法
  • 用C语言实现控制台应用的按键方向控制
  • Qt的安装和环境配置
  • 【愚公系列】《MIoT.VC》002-构建基本仿真工作站(布局一个基本工作站)
  • OPC UA, CAN, PROFINET, SOCKET, MODBUS, HTTP, S7七种物联网常用协议解释
  • 金融工程、金融与经济学知识点