[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_modulesfeedback-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 去进行清理
另外两个比较少用的功能有 create 和 inspect,前者可以用来创建一个新的 volume:
❯ docker volume create --helpUsage:  docker volume create [OPTIONS] [VOLUME]Create a volumeOptions:-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_modulesfeedback-node:volumes649caeddc7df378188c5df5e5c1ac3389f5631d868b933a69ac9253918e6e063
❯ 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=80ENV 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 是无法被访问到的