[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
去进行清理
另外两个比较少用的功能有 create
和 inspect
,前者可以用来创建一个新的 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
是无法被访问到的