在容器和主机之间共享“node_modules”文件夹
Sharing the `node_modules` folder between the container and the host
这是一个讨论得很好的话题,但我从未找到解决方案。
所以你可能知道,当我在我的容器中有一个卷并且我在 Docker 文件中使用 yarn install
安装我的依赖项时,Yarn 将创建一个 node_modules
文件夹在我的容器上,具有 root:root
访问权限。
这个方法有两个问题(在 local/dev 环境中):
node_modules
文件夹只在我的容器里,但宿主的代码编辑器(我的是VSC)可能需要这个文件夹才能正常工作。比如你没有,VSC就会对你大吼他找不到导入的模块...
如果主机想要安装一个包 yarn add ...
他将不得不重新启动并重建要安装的包的容器。
所以我想到了另一个想法,如果我在 Docker 文件中使用 CMD
安装依赖项(或者 command
属性 中的服务docker-compose
文件)。因此,Docker 将与主机共享 node_modules
。但这是主要问题,node_modules
具有 root:root
访问权限,因此如果您的主机的用户名被命名为例如 mint
并且没有相同的 uid
& gid
你将需要 运行 root 访问命令来 install/remove 依赖项(例如 sudo yarn add ...
)。
这是我当前的配置:
docker-compose.yml
:
version: '3.7'
services:
app:
container_name: 'app_DEV'
build: .
command: sh -c "yarn install && node ./server.js"
volumes:
- ./:/usr/src/app
ports:
- 3000:3000
tty: true
Dockerfile
:
FROM node:12.8.1-alpine
WORKDIR /usr/src/app
COPY . .
package.json
:
{
"dependencies": {
"express": "^4.17.1"
}
}
server.js
:
const app = require('express')();
app.get('/', (req, res) => {
res.send('Hello');
});
app.listen(3000, () => console.log('App is listening on port 3000'));
然后你可以尝试 运行 docker-compose up
然后做一个 ls -la
你应该会看到类似的东西:
-rw-r--r-- 1 mint mint 215 août 23 16:39 docker-compose.yml
-rw-r--r-- 1 mint mint 56 août 23 16:29 Dockerfile
drwxr-xr-x 52 root root 4096 août 23 16:31 node_modules
-rw-r--r-- 1 mint mint 53 août 23 16:31 package.json
-rw-r--r-- 1 mint mint 160 août 23 16:29 server.js
如您所见,每个 files/folders 都具有 mint:mint
访问权限,但 node_modules
除外(mint
是我的主机的用户)。这就是第二个解决方案的问题。
最后,我的问题是:是否有更好的方法来完成这整件事?
在你的情况下,为了让它以你想要的方式工作,你应该在 docker 文件中添加 USER
并在 [=39= 中添加 user:
]-compose.yml。例如
Docker 文件:
FROM node:12.8.1-alpine
WORKDIR /usr/src/app
USER node
COPY . .
docker-compose.yml:
version: '3.7'
services:
app:
container_name: 'app_DEV'
build: .
command: sh -c "yarn install && node ./server.js"
user: "1000:1000"
volumes:
- ./:/usr/src/app
ports:
- 3000:3000
tty: true
无论如何,我们遇到了类似的情况,我们选择了不同的方法。我们决定避免在 docker- compose.yml.
在我们的例子中,Dockerfile 如下所示:
FROM node:8.12.0-stretch
RUN mkdir /api
WORKDIR /api
COPY ./package.json ./package-lock.json ./
RUN npm ci --prod
COPY . .
CMD [ "nodemon", "server.js" ]
docker-compose.yml 看起来像这样:
version: '3.7'
services:
app:
build: .
volumes:
- "./:/api"
- "/api/node_modules/"
这样我们就可以在主机中创建 node_modules(可用于测试、开发等)并安全地保持 docker 容器的内容不变。这种方法的缺点是我们的开发人员还必须 运行 主机上的 npm ci
并且每次 package.json 更改时他们都需要重新创建图像。
一般来说,我不会推荐这种方法,因为您的主机和容器可能无法共享相同的模块。例如,如果您团队中的其他人使用 Windows 并且您有一些已编译的模块(即 node-sass 或 bcrypt),共享这些模块会使容器或主机无法使用它们。
另一个经常出现的解决方案是在 Docker 文件中分离 node_modules 安装步骤,并为此覆盖卷安装。每次要添加包时,您仍然需要重建 Docker 图像,但这(可能)不应该经常发生。
这是 Docker 文件的相关部分:
FROM node:12.8.1-alpine
WORKDIR /usr/src/app
COPY ./package*.json .
COPY ./yarn.lock .
RUN yarn
COPY . .
CMD [ "yarn", "start" ]
然后,在您的 docker-compose 文件中:
version: '3.7'
services:
app:
container_name: 'app_DEV'
build: .
command: sh -c "yarn install && node ./server.js"
volumes:
- ./:/usr/src/app
- /usr/src/app/node_modules/
ports:
- 3000:3000
tty: true
确保在 根装载之后包含 /usr/src/app/node_modules/
卷,因为它将在容器中覆盖它。另外,尾部斜杠很重要。
自从我最初写这个问题以来已经过去了几年。我想回来分享不同的意见,因为从那时起我的观点发生了一些变化,我现在认为我想使用容器的方式是不正确的。
首先,几乎所有在容器中创建的 file/folder 都不应在同一容器外更改。
在此 post 的上下文中,任何更改 node_modules
文件夹的命令都应来自容器内的 运行。我知道这可能有点麻烦,但我认为只要您使用 docker-compose(例如 docker-compose exec app npm i
)就可以了。
我认为它更适合 OCI 容器的使用方式。
在 OS 兼容性方面,因为一切(与开发环境相关)都应该在容器内部完成,所以应该没有任何问题。
请注意,我已经看到组织分发带有未安装和预安装依赖项的开发映像。我觉得这两种方式都可以,只是取决于你是否想要一个轻量级的开发镜像。
这是一个讨论得很好的话题,但我从未找到解决方案。
所以你可能知道,当我在我的容器中有一个卷并且我在 Docker 文件中使用 yarn install
安装我的依赖项时,Yarn 将创建一个 node_modules
文件夹在我的容器上,具有 root:root
访问权限。
这个方法有两个问题(在 local/dev 环境中):
node_modules
文件夹只在我的容器里,但宿主的代码编辑器(我的是VSC)可能需要这个文件夹才能正常工作。比如你没有,VSC就会对你大吼他找不到导入的模块...如果主机想要安装一个包
yarn add ...
他将不得不重新启动并重建要安装的包的容器。
所以我想到了另一个想法,如果我在 Docker 文件中使用 CMD
安装依赖项(或者 command
属性 中的服务docker-compose
文件)。因此,Docker 将与主机共享 node_modules
。但这是主要问题,node_modules
具有 root:root
访问权限,因此如果您的主机的用户名被命名为例如 mint
并且没有相同的 uid
& gid
你将需要 运行 root 访问命令来 install/remove 依赖项(例如 sudo yarn add ...
)。
这是我当前的配置:
docker-compose.yml
:
version: '3.7'
services:
app:
container_name: 'app_DEV'
build: .
command: sh -c "yarn install && node ./server.js"
volumes:
- ./:/usr/src/app
ports:
- 3000:3000
tty: true
Dockerfile
:
FROM node:12.8.1-alpine
WORKDIR /usr/src/app
COPY . .
package.json
:
{
"dependencies": {
"express": "^4.17.1"
}
}
server.js
:
const app = require('express')();
app.get('/', (req, res) => {
res.send('Hello');
});
app.listen(3000, () => console.log('App is listening on port 3000'));
然后你可以尝试 运行 docker-compose up
然后做一个 ls -la
你应该会看到类似的东西:
-rw-r--r-- 1 mint mint 215 août 23 16:39 docker-compose.yml
-rw-r--r-- 1 mint mint 56 août 23 16:29 Dockerfile
drwxr-xr-x 52 root root 4096 août 23 16:31 node_modules
-rw-r--r-- 1 mint mint 53 août 23 16:31 package.json
-rw-r--r-- 1 mint mint 160 août 23 16:29 server.js
如您所见,每个 files/folders 都具有 mint:mint
访问权限,但 node_modules
除外(mint
是我的主机的用户)。这就是第二个解决方案的问题。
最后,我的问题是:是否有更好的方法来完成这整件事?
在你的情况下,为了让它以你想要的方式工作,你应该在 docker 文件中添加 USER
并在 [=39= 中添加 user:
]-compose.yml。例如
Docker 文件:
FROM node:12.8.1-alpine
WORKDIR /usr/src/app
USER node
COPY . .
docker-compose.yml:
version: '3.7'
services:
app:
container_name: 'app_DEV'
build: .
command: sh -c "yarn install && node ./server.js"
user: "1000:1000"
volumes:
- ./:/usr/src/app
ports:
- 3000:3000
tty: true
无论如何,我们遇到了类似的情况,我们选择了不同的方法。我们决定避免在 docker- compose.yml.
在我们的例子中,Dockerfile 如下所示:
FROM node:8.12.0-stretch
RUN mkdir /api
WORKDIR /api
COPY ./package.json ./package-lock.json ./
RUN npm ci --prod
COPY . .
CMD [ "nodemon", "server.js" ]
docker-compose.yml 看起来像这样:
version: '3.7'
services:
app:
build: .
volumes:
- "./:/api"
- "/api/node_modules/"
这样我们就可以在主机中创建 node_modules(可用于测试、开发等)并安全地保持 docker 容器的内容不变。这种方法的缺点是我们的开发人员还必须 运行 主机上的 npm ci
并且每次 package.json 更改时他们都需要重新创建图像。
一般来说,我不会推荐这种方法,因为您的主机和容器可能无法共享相同的模块。例如,如果您团队中的其他人使用 Windows 并且您有一些已编译的模块(即 node-sass 或 bcrypt),共享这些模块会使容器或主机无法使用它们。
另一个经常出现的解决方案是在 Docker 文件中分离 node_modules 安装步骤,并为此覆盖卷安装。每次要添加包时,您仍然需要重建 Docker 图像,但这(可能)不应该经常发生。
这是 Docker 文件的相关部分:
FROM node:12.8.1-alpine WORKDIR /usr/src/app COPY ./package*.json . COPY ./yarn.lock . RUN yarn COPY . . CMD [ "yarn", "start" ]
然后,在您的 docker-compose 文件中:
version: '3.7' services: app: container_name: 'app_DEV' build: . command: sh -c "yarn install && node ./server.js" volumes: - ./:/usr/src/app - /usr/src/app/node_modules/ ports: - 3000:3000 tty: true
确保在 根装载之后包含 /usr/src/app/node_modules/
卷,因为它将在容器中覆盖它。另外,尾部斜杠很重要。
自从我最初写这个问题以来已经过去了几年。我想回来分享不同的意见,因为从那时起我的观点发生了一些变化,我现在认为我想使用容器的方式是不正确的。
首先,几乎所有在容器中创建的 file/folder 都不应在同一容器外更改。
在此 post 的上下文中,任何更改 node_modules
文件夹的命令都应来自容器内的 运行。我知道这可能有点麻烦,但我认为只要您使用 docker-compose(例如 docker-compose exec app npm i
)就可以了。
我认为它更适合 OCI 容器的使用方式。
在 OS 兼容性方面,因为一切(与开发环境相关)都应该在容器内部完成,所以应该没有任何问题。 请注意,我已经看到组织分发带有未安装和预安装依赖项的开发映像。我觉得这两种方式都可以,只是取决于你是否想要一个轻量级的开发镜像。