docker image 上的 npm global install 没有访问权限错误

Posted

技术标签:

【中文标题】docker image 上的 npm global install 没有访问权限错误【英文标题】:No access permission error with npm global install on docker image 【发布时间】:2017-11-21 20:07:25 【问题描述】:

我正在尝试使用 firebase-tools 和 angular-cli 的全局安装构建一个 docker 映像。 我正在为两个版本的节点构建相同的图像:6.x(LTS 硼)和 v8.x(最新的 alpine)。在本地,两个映像都构建得很好,但是当我尝试在 docker hub 中构建时,只有 v6.x 构建成功。在 v8.x 中,它被困在 未定义和无人用户的访问权限中。我已经在使用 root 用户(USER root),因为没有此设置(或使用 USER 节点),两个图像都无法构建。

这是我的 Dockerfile:

FROM node:latest

USER root

RUN npm install --quiet --no-progress -g @angular/cli@latest firebase-tools
RUN npm cache clean --force

这是输出:

Step 4/5 : RUN npm install --quiet --no-progress -g @angular/cli@latest firebase-tools

 ---> Running in fce3da11b04e

 npm WARN deprecated node-uuid@1.4.8: Use uuid module instead
 /usr/local/bin/firebase -> /usr/local/lib/node_modules/firebase-tools/bin/firebase
 /usr/local/bin/ng -> /usr/local/lib/node_modules/@angular/cli/bin/ng

> node-sass@4.5.3 install /usr/local/lib/node_modules/@angular/cli/node_modules/node-sass
> node scripts/install.js

Unable to save binary /usr/local/lib/node_modules/@angular/cli/node_modules/node-sass/vendor/linux-x64-57 :  Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/@angular/cli/node_modules/node-sass/vendor'
  at Object.fs.mkdirSync (fs.js:890:18)
  at sync (/usr/local/lib/node_modules/@angular/cli/node_modules/mkdirp/index.js:71:13)
  at Function.sync (/usr/local/lib/node_modules/@angular/cli/node_modules/mkdirp/index.js:77:24)
  at checkAndDownloadBinary (/usr/local/lib/node_modules/@angular/cli/node_modules/node-sass/scripts/install.js:111:11)
  at Object.<anonymous> (/usr/local/lib/node_modules/@angular/cli/node_modules/node-sass/scripts/install.js:154:1)
  at Module._compile (module.js:569:30)
  at Object.Module._extensions..js (module.js:580:10)
  at Module.load (module.js:503:32)
  at tryModuleLoad (module.js:466:12)
  at Function.Module._load (module.js:458:3)

errno: -13,
code: 'EACCES',
syscall: 'mkdir',
path: '/usr/local/lib/node_modules/@angular/cli/node_modules/node-sass/vendor' 

> grpc@1.3.8 install /usr/local/lib/node_modules/firebase-tools/node_modules/grpc > node-pre-gyp install --fallback-to-build --library=static_library
node-pre-gyp ERR! Tried to download(undefined): https://storage.googleapis.com/grpc-precompiled-binaries/node/grpc/v1.3.8/node-v57-linux-x64.tar.gz node-pre-gyp ERR! Pre-built binaries not found for grpc@1.3.8 and node@8.1.2 (node-v57 ABI) (falling back to source compile with node-gyp)
gyp WARN EACCES user "undefined" does not have permission to access the dev dir "/root/.node-gyp/8.1.2" gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp"
gyp WARN EACCES user "nobody" does not have permission to access the dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp/8.1.2" gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp"
gyp WARN EACCES user "nobody" does not have permission to access the dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp/8.1.2" gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp"
gyp WARN EACCES user "nobody" does not have permission to access the dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp/8.1.2" gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp"
gyp WARN EACCES user "nobody" does not have permission to access the dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp/8.1.2" gyp WARN EACCES attempting to reinstall using temporary dev dir "/usr/local/lib/node_modules/firebase-tools/node_modules/grpc/.node-gyp"
(infinite loop)

【问题讨论】:

【参考方案1】:

我能够通过更改默认的 npm-global 目录来使其正常工作。

这是我现在的 dockerfile:

FROM node:latest
USER node

RUN mkdir /home/node/.npm-global
ENV PATH=/home/node/.npm-global/bin:$PATH
ENV NPM_CONFIG_PREFIX=/home/node/.npm-global

RUN npm install --quiet --no-progress -g @angular/cli@latest firebase-tools
RUN npm cache clean --force

【讨论】:

你应该接受你自己的答案。目前接受的答案建议使用root 并获得太多的赞成票。【参考方案2】:

问题在于,虽然 NPM 以 nobody 用户身份运行全局安装的模块脚本,这很有意义,但最近版本的 NPM 开始将节点模块的文件权限设置为 root。因此,不再允许模块脚本在其模块中创建文件和目录。

请参阅讨论in NPM issue #3849,以获取一些参考资料。

一个在 docker 环境中有意义的简单解决方法是将 NPM 默认全局用户设置回root,如下所示:

npm -g config set user root

在此之后,您不应再有任何EACCES 错误。

【讨论】:

或者也使用 npm -g config set user $USER 对我有用,因为我真的希望我的用户成为默认用户,而不是 root。 @DanieleDellafiore 将全局用户设置为个人用户 ID 没有多大意义。也许您只想使用不带全局 (-g) 标志的 npm 尝试在全球范围内安装cordova-res,但安装成功后无法正常工作。谢谢。 完美运行!【参考方案3】:

使用--unsafe-perm 标志:

npm install --quiet --no-progress --unsafe-perm -g @angular/cli@latest firebase-tools

我认为这仍然比将npm 用户永久设置为root 更好。 --unsafe-perm 只能用于引起问题的软件包

【讨论】:

【参考方案4】:

我没有强迫 NPM 将包安装到容器中,而是通过映射/引用缺少模块的主机文件夹来绕过这个问题 这也可以防止将来当我用最新版本替换 docker 映像时感到头疼,我不必重复这些阶段来在容器内重新安装缺少的模块:

我采取的步骤:

    在主机环境中创建一个空文件夹(将用作节点 js 模块的目标)。称之为 node_modules

    在启动运行 docker 容器时使用 --volume 和 --env 开关

-- 传递的卷将新的主机文件夹(从步骤 1 开始)映射到 docker 内可访问的文件夹

--env 定义/设置环境变量 NPM_CONFIG_PREFIX 从 docker 内部到我们在步骤 1 中创建的 /node_modules 文件夹

    使用 :

    访问容器

    sudo docker exec -i -t sh

然后转到 local /node_modules 文件夹正上方的文件夹(这不是我们映射到环境变量的文件夹,而是 docker 映像附带的预先存在的文件夹)

然后运行命令:

> npm install -g <mdule-name>

例子

> npm install -g request

这会将模块安装在我们创建的主机文件夹中。这个模块也可以从 docker 和 host 访问。

【讨论】:

@kaiser 谢谢,只要人们仍然可以看到和使用这些信息......这才是最重要的【参考方案5】:

请勿将用户设置为root 或使用--unsafe-perm

只需使用node用户,提供当前官方(例如alpine)图片。

在下方评论Dockerfile

FROM node:15.5-alpine

# ? Security: do not use the `root` user.
ENV USER=node

# You can not use `$USER` here, but reference `/home/node`.
ENV PATH="/home/node/.npm-global/bin:$PATH"
# ? The `--global` install dir
ENV NPM_CONFIG_PREFIX="/home/node/.npm-global"

# All subsequent commands are run as the `node` user.
USER "$USER"

# Pre-create the target dir for global install.
RUN mkdir -p "$NPM_CONFIG_PREFIX/lib"

WORKDIR /usr/src/app

COPY package.json package-lock.json ./

# ? Configure NPM, so pkg get installed with correct credentials.
# Avoids `chmod u+x $DIR` and other workarounds.
RUN npm --global config set user "$USER" \
    && npm --global --quiet --no-progress install \
    && npm cache clean --force

@gabriel-araujo 答案的扩展版本。

您可以通过配置此in the .npmrc file 通过 CLI 标志 (npm --global config set user "$USER") 替换 setting the user。将user=node 放在项目根.npmrc 文件中,或者直接从Dockerfile 中设置。

RUN echo "user=node" > "$NPM_CONFIG_PREFIX/etc/.npmrc"

如果您使用docker-compose.yml,您可以将其添加为environment: - … 变量。

希望能帮助您在某处运行更安全的 NodeJS 容器并提取一些很棒的东西。

【讨论】:

以上是关于docker image 上的 npm global install 没有访问权限错误的主要内容,如果未能解决你的问题,请参考以下文章

npm install 后 Gitlab Shared Runner docker build

vuejs 应用程序不使用 dockerized npm 运行

sbt-native-packager:Alpine Docker Image 上的 Scala 应用程序失败,权限被拒绝

npm安装在docker build中

修改 npm 全局安装目录

docker image