掌握 Docker 卷
对于刚开始使用 Docker 的人,我写了一些关于如何像容器一样思考的文章,我相信这是使用 Docker 的重要思维方式,也是在开发中正确使用 Docker 的重要思维方式。
但是,如果您想进一步掌握 Docker,那么了解Docker 中的卷和网络如何工作非常重要。
在这篇文章中,我将引导您了解卷的基础知识,而网络部分则留到另一篇文章中介绍。
容器是短暂的
需要注意的是,容器是短暂的。只要容器执行完命令,它就会彻底“关闭”。
=> docker run node ls
bin
boot
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
它使用图像节点启动一个新容器,并ls
在容器内执行命令(列出文件和目录)。
该命令给出输出并完成。命令完成后,容器将关闭,其所有数据将丢失。
执行 Javascript 程序
假设我们有一个包含非常基本的 Javascript 程序的文件:
helloWorld.js
greeting = (message) => {
console.log(message)
}
greeting("Hello, world")
我们如何使用容器运行这个程序?可以尝试这样做:
docker run node node helloWorld.js
...这引发了错误:
Error: Cannot find module '/app/helloWorld.js'
...
这是因为容器是隔离的并且不共享相同的主机文件系统。
在主机和容器之间同步数据
我们必须将文件helloWorld.js
与容器同步。Docker 提供了一种挂载卷的方法,这意味着:
我想将目录或文件从主机挂载到容器,这样在主机中所做的每个更改都将镜像到容器,反之亦然。
你在主机中更改文件/目录,容器也会看到这些变化。你在容器中更改文件/目录,主机也会看到这些变化。
helloWorld.js
让我们与容器同步:
docker run
-v $(pwd)/docker-101/helloWorld.js:/app/helloWorld.js
node
node /app/helloWorld.js
Hello, world
...在哪里:
- pwd指的是当前目录的完整路径名
- -v {hostPath}:{containerPath}将文件/目录挂载到容器
- 节点是图像
- node /app/helloWorld.js使用容器内定义的路径执行命令,
/app/helloWorld.js
一个真实的项目
假设我们有一个这样的项目树:
/docker-101
/src
/components
components.js
index.js
从目录内部docker-101
,我们想要使用该文件运行一个容器,但文件夹index.js
中的所有文件也必须位于容器应用程序中。src
docker run
-v $(pwd):/app
node
node /app/index.js
当前目录中的所有文件都pwd
将挂载到/app
容器内。
如果我们想进入容器并从那里操作文件怎么办?
docker run
-it
-v $(pwd):/app
node
bash
...在哪里:
- -它指示 Docker 保持容器终端(bash/shell)打开
- bash在容器内打开一个新的 bash/shell
root@8dcbfa6d777c:/# cd /app
root@8dcbfa6d777c:/app# ls
index.js src
root@8dcbfa6d777c:/app# touch new-file.js
root@8dcbfa6d777c:/app# ls
index.js new-file.js src
root@8dcbfa6d777c:/app# exit
exit
现在,退出容器后,如果我们ls
从主机执行,我们会得到:
index.js new-file.js src
这种类型的体积称为路径体积。
使用挂载选项
挂载卷的另一种方法是使用--mount
选项,该选项指定挂载的类型、源和目标。
docker run
--mount type=bind,source=$(pwd),target=/app
node
node /app/helloWorld.js
更mount
明确,但在大多数情况下使用-v
就足够了。
另一个真实的例子
假设我们想打印当前时间的时间戳。有很多方法可以实现,但现在我们将使用Underscore库。
index.js
var _ = require('underscore');
console.log(_.now())
我们执行docker run -it -v $(pwd):/app node node /app/index.js
,得到以下错误:
Error: Cannot find module 'underscore'
显然,我们必须将underscore
依赖项添加到我们的项目中。让我们使用以下命令来完成npm
:
docker run
-v $(pwd):/app
-w /app
node
npm add underscore
added 1 package, and audited 2 packages in 2s
注意-w /app
,它表示工作目录,我们告诉 Docker 从该目录内执行命令。在我们的例子中,该命令将在容器内的目录npm add underscore
中执行。/app
现在,我们可以运行index.js
:
docker run
-v $(pwd):/app
-w /app
node
node index.js
1644018496251
想要一些 NPM 缓存吗?
有时我们需要使用缓存来加速 npm 进程。在容器内部,默认情况下,npm
存储缓存在/root/.npm
。你可以运行以下命令进行检查:
npm config get cache
=> /root/.npm
目前,我们仅同步-v $(pwd):/app
卷。当容器将 npm 缓存写入时/root/.npm
,我们不会同步回主机。
我们了解到我们可以将多个卷安装到容器中,所以让我们这样做:
docker run
-v $(pwd):/app
-v $(pwd)/npm_cache:/root/.npm <---- sync the NPM cache
-w /app
node
npm install underscore
当我们ls
从主机执行时,我们得到以下内容:
index.js node_modules npm_cache package-lock.json package.json src
但是,我们不想在项目中跟踪“npm_cache”文件夹。这不关我们的事。
如果我们想将这样的缓存存储在主机的另一个路径中,因此我们不关心它存储在哪里,该怎么办?
Docker 提供了一种特殊类型的 Volume,它在 Host 中的路径由 Docker 选择,因此我们唯一需要知道的就是它的名称。Docker 知道它的位置,但我们并不关心。
是的,它们被称为命名卷。
docker volume create my-volume
docker volume inspect my-volume
[
{
"CreatedAt": "2022-02-05T00:47:59Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
"Name": "my-volume",
"Options": {},
"Scope": "local"
}
]
这Mountpoint
是主机中的确切路径。因此,每个使用此卷的容器都会将其数据直接同步到此挂载点,而我们则可以通过在其他地方使用其名称“my-volume”来管理和了解该卷。
在我们的下划线示例中,我们也可以使用命名卷启动容器:
docker run
-v $(pwd):/app
-v npm_cache:/root/.npm <---- named volume in the Host
-w /app
node
npm install underscore
这样,Docker 将创建一个名为的卷npm_cache
并将其挂载到容器中/root/.npm
。
不相信?你自己去看看吧:
docker volume inspect npm_cache
[
{
"CreatedAt": "2022-02-05T00:51:24Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/npm_cache/_data",
"Name": "npm_cache",
"Options": null,
"Scope": "local"
}
]
太棒了!现在清晰多了,因为我们保持了 npm 进程的加速。
docker run
-v $(pwd):/app
-v npm_cache:/root/.npm
-w /app
node
node index.js
1644022388847
总结
在本文中,我们学习了如何在 Docker 中使用路径卷和命名卷。
当我们想要跟踪主机中的路径(即安装我们正在处理的整个项目)时,路径卷非常有用。
当我们不想跟踪主机中的路径,而是利用 Docker 为我们选择路径时,命名卷非常方便mountpoint
,即使用 NPM、Yarn、Ruby bundler、Python 包等的缓存。