Dockerfile 高级玩法:ARG、ENV、CMD、ENTRYPOINT、ADD


Dockerfile 高级玩法:ARG、ENV、CMD、ENTRYPOINT、ADD

上一篇我们动手写了第一个 Dockerfile,从基础镜像到 COPY 文件、安装依赖,跑起来一个静态服务。但一个真正好用的 Dockerfile 不止于此,它还得够灵活、够精简、够安全。今天咱们就来聊聊那些能让 Dockerfile 更“聪明”的指令和技巧,包括 ARG、ENV、CMD 与 ENTRYPOINT 的配合、ADD 与 COPY 的区别,让你的镜像构建水平再上一个台阶。

ARG:构建时的“临时变量”

有时候你希望在构建镜像时能传一些参数,比如版本号、编译开关,而不是把这些值硬编码在 Dockerfile 里。这时候就用到了 ARG

ARG 定义了一个可以在构建时传入的变量,默认值可有可无。举个例子:

FROM node:18-alpine
ARG APP_VERSION=1.0.0
RUN echo "Building version $APP_VERSION"

构建时你可以这样传参:

docker build --build-arg APP_VERSION=2.1.3 -t myapp .

这样在构建过程中就可以根据传入的值执行不同的逻辑,比如下载对应版本的源码包。ARG 只在构建过程中有效,容器运行时是访问不到的(除非你用 ENV 把它存下来)。

ENV:运行时的环境变量

如果说 ARG 是给构建过程用的,那 ENV 就是给容器运行时用的。它设置的环境变量在容器启动后可以被应用读取。

ENV NODE_ENV=production

这样在容器里,你用 process.env.NODE_ENV 就能拿到 production。而且 ENV 还可以结合 ARG 来动态设置:

ARG DEFAULT_PORT=3000
ENV PORT=$DEFAULT_PORT

构建时你可以改变 ARG,从而影响 ENV 的值。这样既保证了构建的灵活性,又让运行时能拿到正确的配置。

CMD 与 ENTRYPOINT:一个给默认值,一个定主命令

这两个指令都跟容器启动时的命令有关,但分工不同。新手容易混淆,咱们来捋清楚。

  • ENTRYPOINT:定义容器启动时要执行的“主命令”,且不能被 docker run 后面的命令覆盖。如果你想容器只能运行某个固定的程序,就用它。
  • CMD:给 ENTRYPOINT 提供默认参数,或者直接指定要运行的命令(如果没有 ENTRYPOINT)。CMD 可以被 docker run 后面的命令覆盖。

最常见的组合是:ENTRYPOINT 写固定的可执行文件,CMD 写默认的参数。这样用户可以在运行时通过追加参数来覆盖默认值。

看个例子:

FROM alpine
ENTRYPOINT ["ping"]
CMD ["localhost"]

构建后运行:

docker run myping           # 会 ping localhost
docker run myping google.com # 会 ping google.com,因为 CMD 被覆盖了

如果你既想固定命令,又想让用户能加参数,这种组合就非常实用。如果只有一个 CMD 而没有 ENTRYPOINT,那 CMD 就是完整的命令,可以被完全替换。如果只有一个 ENTRYPOINT 而没有 CMD,那容器启动只会执行 ENTRYPOINT,不能加额外参数。

ADD 与 COPY:复制文件的两种姿势

平时我们最常用的是 COPY,它就是把本地文件复制到镜像里,简单直接。而 ADD 除了复制,还有两个隐藏技能:自动解压 tar 文件、支持远程 URL 下载。

比如你有一个压缩包 app.tar.gz,用 ADD 复制到镜像里,它会自动解压:

ADD app.tar.gz /app/

而 COPY 只会把压缩包原样复制过去。另外 ADD 可以直接从 URL 下载文件:

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

但这里有个坑:ADD 下载的 tar 包不会自动解压,而且由于网络不稳定可能导致构建失败,一般建议用 RUN wget 或 curl 代替,更可控。所以最佳实践是:大多数情况下用 COPY,只有当你明确需要自动解压本地 tar 包时再用 ADD

把这些技巧串起来

我们来看一个综合一点的 Dockerfile,它用到了 ARG、ENV、ENTRYPOINT 和 CMD,还做了多阶段构建:

# 构建阶段
FROM node:18-alpine AS builder
ARG APP_VERSION
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN echo "Building version $APP_VERSION" && npm run build

# 运行阶段
FROM nginx:alpine
ARG NGINX_PORT=80
ENV NGINX_PORT=$NGINX_PORT
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE $NGINX_PORT
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

这里我们在构建阶段用 ARG 传入版本号,在运行阶段用 ARG 设置默认端口,并通过 ENV 传递给容器。ENTRYPOINT 固定为 nginx,CMD 提供默认参数,如果你需要修改配置,可以 docker run mynginx nginx -g 'daemon off; -c /custom/nginx.conf' 这样追加参数。

小结

掌握了这些指令,你的 Dockerfile 就不仅仅是“能工作”,而是“好用”了:

  • ARG 让构建可配置;
  • ENV 让运行时能读取配置;
  • ENTRYPOINT + CMD 让启动命令既固定又灵活;
  • COPY 为主,ADD 为辅 让文件复制更清晰。

声明:麋鹿与鲸鱼|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - Dockerfile 高级玩法:ARG、ENV、CMD、ENTRYPOINT、ADD


Carpe Diem and Do what I like