在docker镜像上使用npm global install时没有访问权限错误

时间:2017-06-19 14:36:23

标签: docker npm

我正在尝试使用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)

5 个答案:

答案 0 :(得分:24)

问题是因为NPM运行全局安装的模块脚本作为nobody用户,哪种有意义,最新版本的NPM开始将节点模块的文件权限设置为root。因此,不再允许模块脚本在其模块中创建文件和目录。

有关参考资料,请参阅讨论in NPM issue #3849

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

npm -g config set user root

之后您不应再有EACCES个错误。

答案 1 :(得分:4)

请勿将用户设置为 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 答案的扩展版本。

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

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

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

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

答案 2 :(得分:3)

我能够通过更改默认的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

答案 3 :(得分:1)

使用--unsafe-perm标志:

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

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

答案 4 :(得分:0)

我没有强迫NPM手动将软件包安装在容器内,而是通过映射/引用缺少模块的主机文件夹来绕过此问题 这也避免了以后的麻烦,因为当我用最新版本替换docker映像时,我不必重复这些步骤来在容器中重新安装缺少的模块:

我采取的步骤:

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

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

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

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

  1. 使用:

    访问容器

    sudo docker exec -i -t sh

并转到 local / node_modules文件夹正上方的文件夹上方的文件夹(这不是我们映射到环境变量的文件夹,而是docker映像随附的先前存在的文件夹)< / p>

然后运行命令:

> npm install -g <mdule-name>

示例

> npm install -g request

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