[docker] volume 补充 & 环境变量 & 参数

[docker] volume 补充 & 环境变量 & 参数

这里补充一下 volume 剩下的内容,以及添加参数(ARG) 和 环境变量 ENV 的内容

read only volumes

docker run
    -p 3000:80
    --rm
    --name feedback-app
    -v feedback:/app/feedback
    -v "$(pwd):/app"
    -v /app/node_modules
    feedback-node:volumes

这是上一篇笔记里用到的指令,并且提到了 docker 管理的容器和 host 上的文件会进行同步,但是本机的文件夹不会受到影响,这里进行一下补充为什么:

  • -v feedback:/app/feedback

    这里创造了一个 named volume,名称为 feedback

    • 所有在 container 中的变化会被持久到这个 volume 里

    • 这个 volume 也会被所有有这个指令的 container 共享

  • -v "$(pwd):/app"

    这里将 host machine——也就是 current working directory 和 <docker_container>/app 中进行了挂载(mounting),这代表当前的文件夹会会覆盖 container 里的内容

    当前 $(pwd) 中不存在 node_module,所以 container 里的 node_module 会被 shadowed/hidden

    这同样存在一个问题,那就是任何在 container 中发生的变化,实际上是会被同步到 host machine 上的

    也就是说,temp 的内容可能会出现在 host machine 上:

    在这里插入图片描述

  • -v /app/node_modules

    这是为了解决 node_module 被隐藏而使用的解决方案

    这样 node_module 完全由容器进行管理,就不会被隐藏掉

想要设置当前的 host machine 为只读模式——即 docker 无法写入 host machine,可以使用 :ro 这个 flag:

docker run
    -p 3000:80
    -d
    --rm
    --name feedback-app
    -v feedback:/app/feedback
    -v "$(pwd):/app:ro"
    -v /app/node_modules feedback-node:volumes

这个情况下,$(pwd} 就是只读模式了,不过这里还会有一个问题,那就是如果文件夹——如 node_module 不存在,docker 为了保证二者的同步,又没有 $(pwd} 的写的权利,就会报错,这个是已知问题,可以在 GitHub 上的 issue 查看:Mounting subdirectories of a host volume as named or anonymous volumes still mounts them as host volumes

目前除了添加 .gitkeep 去维持一个空的文件夹,保证 host machine 和 container 中的内容结构一致外,没有什么特别好的解决方案。

管理 volume

查看 volume 可以用 docker volume ls

docker volume ls
DRIVER    VOLUME NAME
local     3e12e9ad9bb9ea262e16e30ff1953138952428544437dcc859058c23b26947ef
local     417ec38d03c862da140a876341fe02adb0aebdd352ca31799d3dd56da43b5b62
local     a994aa8010d2b8ae7b7d2f973ee843cfae46736f1c8d6823b8d0775d01d988e1
local     feedback

之前也提到了,如果用 docker run --rm 会自动删除 anonymous volume,不用的话可能会出现 Orphaned Volumes,出现这种情况可以用 docker volume rm 或者 docker volume prune 去进行清理

另外两个比较少用的功能有 createinspect,前者可以用来创建一个新的 volume:

docker volume create --help

Usage:  docker volume create [OPTIONS] [VOLUME]

Create a volume

Options:
  -d, --driver string   Specify volume driver name (default "local")
      --label list      Set metadata for a volume
  -o, --opt map         Set driver specific options (default map[])

后者可以查看 volume 的细节:

docker volume inspect feedback
[
    {
        "CreatedAt": "2024-04-20T15:04:58Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/feedback/_data",
        "Name": "feedback",
        "Options": null,
        "Scope": "local"
    }
]

这里查看的是 anonymous volume:

docker volume inspect b3ed4fe45866f3b5527f36d8cd53d7cbb5096be73494a604dbab5f72a8cd603f
[
    {
        "CreatedAt": "2024-04-20T20:40:02Z",
        "Driver": "local",
        "Labels": {
            "com.docker.volume.anonymous": ""
        },
        "Mountpoint": "/var/lib/docker/volumes/b3ed4fe45866f3b5527f36d8cd53d7cbb5096be73494a604dbab5f72a8cd603f/_data",
        "Name": "b3ed4fe45866f3b5527f36d8cd53d7cbb5096be73494a604dbab5f72a8cd603f",
        "Options": null,
        "Scope": "local"
    }
]

一般最常用的还是 prune 去清理所有没有再被使用的 volume

COPY vs Bind Mounts

简单来说,在开发阶段如果使用了 Bind Mounts,那么 COPY 可以被忽略

但是生产环境肯定是要使用 COPY

dockerignore

类似于 .gitignore

在这里插入图片描述

实现方式也和 .gitignore 类似:

node_modules

一般比较常添加的忽略文件有: node_modules, .git, Dockerfile

除了 Dockerfile.git,大多数情况下也就是 .gitignore 经常忽略的文件

环境变量(env variable)

环境变量可以在 Dockerfile 中和代码中被访问到,可以使用两种方式设置:

  • 写入 Dockerfile
  • 在运行 docker run 时添加 --env

Dockerfile 配置

这里是 server.js 部分:

// 如果环境中有存在一个 PORT 的变量,那么使用 PORT
app.listen(process.env.PORT ?? 80);

这里是 Dockerfile 的修改:

ENV PORT 80

# $ indicate it's a variable
EXPOSE $PORT

docker run

docker run
    -p 3000:8000
    --env PORT=8000
    -d
    --rm
    --name feedback-app
    -v feedback:/app/feedback
    -v "$(pwd):/app"
    -v /app/node_modules
    feedback-node:volumes

649caeddc7df378188c5df5e5c1ac3389f5631d868b933a69ac9253918e6e063
❯ docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED         STATUS         PORTS                            NAMES
649caeddc7df   feedback-node:volumes   "docker-entrypoint.s…"   4 seconds ago   Up 4 seconds   80/tcp, 0.0.0.0:3000->8000/tcp   feedback-app
❯ docker port feedback-app
8000/tcp -> 0.0.0.0:3000

docker run 的优先权更高,会重写 Dockerfile 中的值,所以这时候 port mapping 的端口是 0.0.0.0:3000->8000/tcp

.env 配置

使用 nodejs 比较常见的配置方法用的是 .env,如:

PORT=8000

这时候可以使用 --env-file ./.env 这个参数去运行:

docker run -p 3000:8000 --env-file ./.env -d --rm --name feedback-app -v feedback:/app/feedback -v "$(pwd):/app" -v /app/node_modules feedback-node:volumes
9270cc97c71b18bd0b22349296e199bb402ec0d3e3cc2cee4d6bc13d4e06a488
❯ docker port feedback-app
8000/tcp -> 0.0.0.0:3000

使用 env 的安全性

一般来说不建议使用环境变量保存一些敏感信息,比如说 API key,原因是因为使用环境变量 ENV 会单独生成一个只读 layer,换言之当前的值会保存在 docker image 中。

如下面使用 docker history 去查看 image 的历史:

docker history feedback-node:volumes
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
6072de71477f   7 minutes ago   CMD ["npm" "start"]                             0B        buildkit.dockerfile.v0
<missing>      7 minutes ago   EXPOSE map[80/tcp:{}]                           0B        buildkit.dockerfile.v0
<missing>      7 minutes ago   ENV PORT=80                                     0B        buildkit.dockerfile.v0
<missing>      7 minutes ago   COPY . . # buildkit                             10.1kB    buildkit.dockerfile.v0
<missing>      4 days ago      RUN /bin/sh -c npm install # buildkit           12.6MB    buildkit.dockerfile.v0
<missing>      4 days ago      COPY package.json /app # buildkit               364B      buildkit.dockerfile.v0
<missing>      6 days ago      WORKDIR /app                                    0B        buildkit.dockerfile.v0
<missing>      9 days ago      /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>      9 days ago      /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>      9 days ago      /bin/sh -c #(nop) COPY file:4d192565a7220e13…   388B
<missing>      9 days ago      /bin/sh -c set -ex   && export GNUPGHOME="$(…   7.58MB
<missing>      9 days ago      /bin/sh -c #(nop)  ENV YARN_VERSION=1.22.19     0B
<missing>      9 days ago      /bin/sh -c ARCH= && dpkgArch="$(dpkg --print…   165MB
<missing>      9 days ago      /bin/sh -c #(nop)  ENV NODE_VERSION=21.7.3      0B
<missing>      10 days ago     /bin/sh -c groupadd --gid 1000 node   && use…   8.94kB
<missing>      10 days ago     /bin/sh -c set -ex;  apt-get update;  apt-ge…   587MB
<missing>      10 days ago     /bin/sh -c set -eux;  apt-get update;  apt-g…   177MB
<missing>      10 days ago     /bin/sh -c set -eux;  apt-get update;  apt-g…   48.4MB
<missing>      10 days ago     /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>      10 days ago     /bin/sh -c #(nop) ADD file:ca6d1f0f80dd6c91b…   117MB

可以看到 <missing> 7 minutes ago ENV PORT=80 这一段。

如果将关键信息存到 ENV中,任何拿到这个 image 的人都可以通过 docker history 去查看当前镜像的历史,从而获取关键信息的值。

参数(ARG)

ARG 只能在 Dockerfile 中使用,无法在 cmd 或是代码中获取 Dockerfile 中设立的 ARG

使用 ARG 不会单独留存一个 layer,因此不会将关键信息留存到 docker image 中,如下面的修改:

ARG DEFAULT_PORT=80

ENV PORT ${DEFAULT_PORT}

EXPOSE $PORT

或者是使用命令行运行:

docker build -t feedback-node:volumes --build-arg DEFAULT_PORT=5000 .docker history feedback-node:volumes
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
eedbc88d6560   16 seconds ago   CMD ["npm" "start"]                             0B        buildkit.dockerfile.v0
<missing>      16 seconds ago   EXPOSE map[5000/tcp:{}]                         0B        buildkit.dockerfile.v0
<missing>      16 seconds ago   ENV PORT=5000                                   0B        buildkit.dockerfile.v0
<missing>      16 seconds ago   COPY . . # buildkit                             10.2kB    buildkit.dockerfile.v0
<missing>      16 seconds ago   RUN |1 DEFAULT_PORT=5000 /bin/sh -c npm inst…   12.6MB    buildkit.dockerfile.v0
<missing>      4 days ago       COPY package.json /app # buildkit               364B      buildkit.dockerfile.v0
<missing>      6 days ago       WORKDIR /app                                    0B        buildkit.dockerfile.v0
<missing>      6 days ago       ARG DEFAULT_PORT=80                             0B        buildkit.dockerfile.v0
<missing>      9 days ago       /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>      9 days ago       /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>      9 days ago       /bin/sh -c #(nop) COPY file:4d192565a7220e13…   388B
<missing>      9 days ago       /bin/sh -c set -ex   && export GNUPGHOME="$(…   7.58MB
<missing>      9 days ago       /bin/sh -c #(nop)  ENV YARN_VERSION=1.22.19     0B
<missing>      9 days ago       /bin/sh -c ARCH= && dpkgArch="$(dpkg --print…   165MB
<missing>      9 days ago       /bin/sh -c #(nop)  ENV NODE_VERSION=21.7.3      0B
<missing>      10 days ago      /bin/sh -c groupadd --gid 1000 node   && use…   8.94kB
<missing>      10 days ago      /bin/sh -c set -ex;  apt-get update;  apt-ge…   587MB
<missing>      10 days ago      /bin/sh -c set -eux;  apt-get update;  apt-g…   177MB
<missing>      10 days ago      /bin/sh -c set -eux;  apt-get update;  apt-g…   48.4MB
<missing>      10 days ago      /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>      10 days ago      /bin/sh -c #(nop) ADD file:ca6d1f0f80dd6c91b…   117MB

可以看到, ARG 并没有出现在 docker history

另外, ARG 只能在 image 被建造时访问,换言之,在运行时,也就是执行到 CMD这一段, ARG 是无法被访问到的

&lt;think&gt;嗯,用户想了解Dockerfile中ENV指令的使用方法和作用。这个问题很具体,说明用户可能在编写Dockerfile时遇到了环境变量配置的需求。 从引用内容来看,ENV指令主要承担两个核心功能:一是设置容器运行时的环境变量,二是在构建阶段为后续指令提供变量替换。用户可能特别关心语法细节,比如等号和空格的区别,以及变量作用范围的问题。 用户提供的引用材料质量不错,特别是引用[5]详细解释了ENV指令的三层作用:设置变量、影响运行时环境、配置应用程序。这个Tomcat示例很典型,值得在回答中保留。引用[2]提到的变量值覆盖规则也很关键,需要强调环境变量&quot;仅向后生效&quot;特性。 考虑到用户是技术实践者,回答应该突出实用场景。除了基础语法,还要包含:1) 多变量设置的紧凑写法 2) ARG和ENV的区别 3) 特殊字符处理技巧。那个${PATH}的示例很能说明问题,要保留。 注意到用户上次询问过Dockerfile相关主题,这次问题更聚焦于ENV指令。可以预判ta接下来可能会问:1) 如何查看容器内的环境变量 2) ENV与docker run -e的区别 3) 多阶段构建中的变量传递。这些潜在问题可以整理成&quot;相关问题&quot;板块。 安全提示很重要,特别是密码管理部分。用户可能没意识到在Dockerfile中直接写密码的风险,需要明确警告。另外要强调${}显式引用的必要性,避免新手常犯的变量未展开错误。 最后的结构安排:先解决&quot;怎么写&quot;,再解释&quot;有什么用&quot;,最后补充&quot;怎么避免坑&quot;。这种渐进式结构符合技术文档的阅读习惯。那些注意事项要放在语法之后,作用之前,起到承上启下作用。&lt;/think&gt;### Dockerfile中`ENV`指令详解 #### 一、基本语法 `ENV`指令有两种写法: 1. **单变量定义** `ENV &lt;key&gt; &lt;value&gt;` ```dockerfile ENV APP_VERSION 1.2.0 ``` &gt; 注意:值含空格时需用引号包裹,如 `ENV GREETING &quot;Hello World&quot;` 2. **多变量定义(紧凑格式)** `ENV &lt;key1&gt;=&lt;value1&gt; &lt;key2&gt;=&lt;value2&gt; ...` ```dockerfile ENV PYTHON_VERSION=3.9 PATH=/usr/local/bin:$PATH ``` #### 二、核心作用 1. **构建阶段变量替换** 后续指令可直接使用`$变量名`引用值: ```dockerfile ENV TARGET_DIR /app WORKDIR $TARGET_DIR # 等价于 WORKDIR /app COPY . $TARGET_DIR # 复制到/app目录 ``` &gt; 支持替换的指令:`ADD`, `COPY`, `WORKDIR`, `EXPOSE`, `USER`, `VOLUME`等[^1][^3] 2. **持久化容器运行时环境** 通过`ENV`设置的变量在容器启动后仍存在: ```bash docker run -it my-image env | grep APP_VERSION # 输出: APP_VERSION=1.2.0 ``` #### 三、典型应用场景 1. **配置应用程序路径** ```dockerfile ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk ENV PATH $JAVA_HOME/bin:$PATH ``` 2. **定义版本参数**(便于统一修改) ```dockerfile ENV NGINX_VER 1.21.0 RUN wget http://nginx.org/download/nginx-$NGINX_VER.tar.gz ``` 3. **设置容器默认行为** ```dockerfile ENV LOG_LEVEL INFO CMD [&quot;sh&quot;, &quot;-c&quot;, &quot;app --log=$LOG_LEVEL&quot;] ``` #### 四、关键注意事项 1. **作用域规则** 变量仅对后续指令生效,且后定义的覆盖前值: ```dockerfile ENV NAME=Alice RUN echo $NAME # 输出 Alice ENV NAME=Bob RUN echo $NAME # 输出 Bob[^2] ``` 2. **与`ARG`指令的区别** | 指令 | 生效阶段 | 是否持久化到容器运行时 | |------|----------------|------------------------| | `ARG`| 仅构建阶段 | ❌ 否 | | `ENV`| 构建+运行时 | ✅ 是 | 3. **特殊字符处理** 需用反斜杠转义或引号包裹: ```dockerfile ENV URL &quot;https://example.com/?q=test&quot; ``` 4. **安全风险** **禁止**在`ENV`中存储敏感信息(如密码),应使用`docker run --env-file`或密钥管理服务。 &gt; 通过合理使用环境变量,可显著提升Dockerfile的可维护性和容器配置的灵活性[^5]。 --- ### 相关问题 1. 如何在运行容器时覆盖`ENV`设置的默认环境变量? 2. `ENV`与`ARG`指令在Docker多阶段构建中如何协作? 3. 如何验证Docker镜像中的环境变量是否生效? 4. 在Docker Compose中如何管理环境变量? 5. 环境变量在容器内进程的生命周期中如何传递? [^1]: ENV指令可以为镜像创建出来的容器声明环境变量,并被特定指令解释使用 [^2]: 环境变量的值仅对后续指令生效,后定义的覆盖前值 [^3]: ENV指令被包括ADD、COPY、WORKDIR等指令支持 [^5]: ENV指令设置的环境变量会影响镜像运行时的环境,常用于配置应用程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值