[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 上:

    [docker] volume 补充 &amp; 环境变量 &amp; 参数-LMLPHP

  • -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

[docker] volume 补充 &amp; 环境变量 &amp; 参数-LMLPHP

实现方式也和 .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 是无法被访问到的

04-25 19:42