docker 在容器和主机之间共享'node_modules'文件夹

daupos2t  于 2022-11-03  发布在  Docker
关注(0)|答案(3)|浏览(204)

这是一个非常热门的主题,但我从来没有真正找到一个解决办法。
正如您可能知道的,当我们在容器中有一个卷,并且我们从一个Dockerfile(使用默认权限)安装依赖项(使用npm i或其他内容)时,Npm将在容器中创建一个node_modules文件夹,并使用root:root访问权限。
我在使用此方法时遇到了两个问题(在本地/dev环境中):

  1. node_modules文件夹只存在于容器中,但宿主的IDE/LSP需要此文件夹才能正常工作(模块导入、类型定义等)。
    1.如果主机要安装/更新软件包(npm i ...等),则必须重新启动并为要更新的node_modules文件夹重建容器。
    所以我想出了另一个主意,如果我在Dockerfile中使用CMD安装依赖项会怎样(或docker-compose文件中服务的command属性)并使用一个卷,这样node_modules就可以与主机共享。不幸的是,这种方法引入了新的问题。例如,node_modules具有root:root权限访问,因此,如果您的主机用户名为“named”,并且不具有相同的uidgid,则需要运行root访问命令更新node_modulessudo npm i ...)。
    以下是我的当前配置:
    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

正如你所看到的每个文件/文件夹都有mint:mint访问除了node_modulesmint是我的主机的用户).
所以总结一下我的问题:有没有更好的方法来管理与Docker容器的NodeJS依赖关系?

f2uvfpb9

f2uvfpb91#

在你的例子中,为了让它以你想要的方式工作,你应该在docker文件中添加USER,在docker-compose. yml文件中添加user:
停靠文件:

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

无论如何,我们遇到了类似的情况,我们选择了一种不同的方法。我们决定避免在主机和容器之间共享node_modules文件夹(如果您与使用不同操作系统的同事一起工作,这是一种很糟糕的行为),而是避免在docker-compose. yml中挂载node_modules文件夹。
在我们的示例中,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更改时都需要重新创建映像。

ilmyapht

ilmyapht2#

自从我最初写这个问题以来已经过去了几年。我想回来分享一个不同的观点,因为我的POV从那时起发生了一些变化,我现在认为我想要使用容器的方式是不正确的。
首先,在容器中创建的文件/文件夹不应该在容器外修改。在本文中,修改node_modules文件夹的任何命令都应该在容器内运行。我知道这可能有点麻烦,但我认为只要使用docker-compose就可以了(例如docker-compose exec app npm i)。我认为它更适合OCI容器的使用方式。
在操作系统兼容性方面,由于所有(与开发环境相关的)操作都应该在容器内部完成,因此不应该有任何问题。注意,我看到过一些组织发布的开发映像既有卸载的,也有预装的依赖项。我认为这两种方式都很好,只是这真的取决于你是否想要一个轻量级的开发映像。

fiei3ece

fiei3ece3#

一般来说,我不会推荐这种方法,因为你是主机,容器可能无法共享相同的模块。例如,如果你的团队中有人使用Windows,而你有一些编译过的模块(如node-sass或bcrypt),共享这些模块会使容器或主机无法使用它们。
另一个经常出现的解决方案是在你的Docker文件中分离node_modules安装步骤,并覆盖这个步骤的卷挂载。每次你想添加一个包时,你仍然需要重建Docker映像,但这种情况(可能)不应该经常发生。
以下是Dockerfile的相关部分:

FROM node:12.8.1-alpine

WORKDIR /usr/src/app

COPY ./package*.json .
COPY ./yarn.lock .

RUN yarn

COPY . .

CMD [ "yarn", "start" ]

然后,在您的Dock合成文件中:

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/卷,因为它会覆盖容器中的/usr/src/app/node_modules/

相关问题