简体   繁体   中英

npm install fails in jenkins pipeline in docker

I'm following a tutorial about Jenkins pipeline and I can get a "hello world" working under at node 6.10 docker container.

But, when I added a default EmberJS app (using ember init ) to the repo and attempt to build that in the pipeline, it fails when running npm install (because of directory access issues). The Jenkinsfile can be seen here: https://github.com/CloudTrap/pipeline-tutorial/blob/fix-build/Jenkinsfile

The error message printed by the build is (which is installed locally and run using java -jar jenkins.war on a Macbook, not relevant but included just in case) is:

npm ERR! Linux 4.9.12-moby
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install"
npm ERR! node v6.10.0
npm ERR! npm  v3.10.10
npm ERR! path /.npm
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall mkdir

npm ERR! Error: EACCES: permission denied, mkdir '/.npm'
npm ERR!     at Error (native)
npm ERR!  { Error: EACCES: permission denied, mkdir '/.npm'
npm ERR!     at Error (native)
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/.npm',
npm ERR!   parent: 'pipeline-tutorial' }
npm ERR! 
npm ERR! Please try running this command again as root/Administrator.

Note: I would like to not run npm install as root / sudo.

UPDATE: I have been able to make some progress as follows:

I found the command that Jenkins uses to build using the container from the logs:

[Pipeline] withDockerContainer
$ docker run -t -d -u 501:20 -w /long-workspace-directory -v /long-workspace-directory:/long-workspace-directory:rw -v /long-workspace-directory@tmp:/long-workspace-directory@tmp:rw -e

So when the docker image runs, it's work directory is a /long-workspace-directory (it's really a cryptic looking jenkins workspace path) and the user id is 501 (group id 20), etc. The user doesn't have a name (which is apparently breaking other things not related to this question).

  1. Changed agent to use a Dockefile:

     agent { dockerfile { filename 'Dockerfile' args '-v /.cache/ -v /.bower/ -v /.config/configstore/' } }
  2. Specify args '-v ...' for creating volumes for the directories npm install / bower needs.

Adding the environments and setting the Home to '.' solves this as below.

pipeline {
    agent { docker { image 'node:8.12.0' } }
    environment {
        HOME = '.'
    }
    stages {
        stage('Clone') {
            steps {
                git branch: 'master',
                    credentialsId: '121231k3jkj2kjkjk',
                    url: 'https://myserver.com/my-repo.git'
            }
        }
        stage('Build') {
            steps {
                sh "npm install"
            }
        }
    }
}

from https://github.com/jenkins-infra/jenkins.io/blob/master/Jenkinsfile

docker.image('openjdk:8').inside {
    /* One Weird Trick(tm) to allow git(1) to clone inside of a
    * container
    */
    withEnv([
        /* Override the npm cache directory to avoid: EACCES: permission denied, mkdir '/.npm' */
        'npm_config_cache=npm-cache',
        /* set home to our current directory because other bower
        * nonsense breaks with HOME=/, e.g.:
        * EACCES: permission denied, mkdir '/.config'
        */
        'HOME=.',
    ]) {
            // your code
    }
}

在这个问题上浪费了一整天,我发现只需在代理阶段使用管道编辑器添加以下内容作为环境变量就可以解决问题。

'npm_config_cache=npm-cache'

When Jenkins runs the stage in Docker agent it usually sets HOME=/ (image WORKDIR value), but Jenkins user does not have write permissions in this directory, that's why npm cannot create its cache directory ~/.npm . To solve this you need either to override HOME or NPM default cache directory:

pipeline {
    agent {
        docker {
            reuseNode true
            image 'node:16.15'
        }
    }

    environment {
        // Override HOME to WORKSPACE
        HOME = "${WORKSPACE}"
        // or override default cache directory (~/.npm)
        NPM_CONFIG_CACHE = "${WORKSPACE}/.npm"
    }

    stages {
        stage('Build') {
            steps {
                sh 'npm install'
                sh 'npm run build'
            }
        }
    }
}

Wanted to just provide a bit more detail, in short the accepted answer works but that I'm new to Docker and wanted to get a better understanding and figured i'd share what i found.

So for our jenkins setup, it starts containers via

docker run -t -d -u 995:315 -w /folder/forProject -v /folder/forProject:/folder/forProject:rw,z ...

As a result this container is running as user uid=995 gid=315 groups=315

Since the image I was using (circleci/node:latest) doesn't have a user with this UID/GID, the user will not have a “home” folder and will only have permission on the mounted volume.

When NPM commands are called it will try using that users home directory (for cache) and since that user wasn't created on the image, the home directory gets set to / (default for linux?). So to get NPM to work correctly we simply point the containers HOME environment variable for the user to the current folder via the Jenkins file

pipeline {
  agent none
  stages {
    stage('NPM Installs') {
      agent {
        docker {
            image 'circleci/node:latest'
        }
      }
      environment { HOME="." }
      ...
    }
  }
}

Thus giving the user the ability to create the required .npm folder in /folder/forProject/.npm

Hopefully this is helpful to someone and if you see something that i got wrong please let me know :D

I add the same issue. I solved it using the root user to run the Docker image:

node {
    stage("Prepare environment") {
        checkout scm
        // Build the Docker image from the Dockerfile located at the root of the project
        docker.build("${JOB_NAME}")
    }

    stage("Install dependencies") {
        // Run the container as `root` user
        // Note: you can run any official Docker image here
        withDockerContainer(args: "-u root", image: "${JOB_NAME}") {
            sh "npm install"
        }
    }
}

You can override the user that Jenkins runs the docker container with, for example here I override with the root (userid:groupid is 0:0):

docker { 
    image 'node:8'
    args '-u 0:0'
}

You can spot the current user in the docker run parameters in the console output.

We had the same issue, the core of the problem for us was, that the user in the Container and the User running the Jenkins node had different UIDs. After changing the UID+GID of the user in the container (and changing ownership of the users home-directory) to match the user running the build node npm would behave normal.

This might also happen if the Home-Directory of the container-user is not writeable.

Code in the Dockerfile:

RUN usermod -u <uid of buildnode> <container user> && \
    groupmod -g <gid of buildnode> <container user group> && \
    chown -R <container user>:<container user group> /home/<container user>

As the Workspace is mounted into the container it will already belong to the UID. When running the container through the Jenkinsfile the UID and GID of the container user are set automatically to match the buildnode. But the home directory will still have its original owner.

Now the node_modules will be placed in the current directory.

In my case the problem was that inside the container I was user jenkins instead of root. I got there by setting whoami inside the container and got error like cannot determine user 111 (which happens to be jenkins). So I did the following:

stage('Run build') {
        webappImage.inside("-u root") {
            sh "yarn run build"
        }
    }

this configuration work for me.

pipeline {
    agent {
        docker {
            image 'node:6-alpine'
            args '-p 3000:3000 -p 5000:5000'
            args '-u 0:0'

        }
    }
    environment {
        CI = 'true'
    }
    stages {
        stage('Build') {
            steps {
                sh 'npm install --unsafe-perm'
            }
        }
        stage('Test') {
            steps {
                sh './jenkins/scripts/test.sh'
            }
        }
        stage('Deliver for development') {
            when {
                branch 'development' 
            }
            steps {
                sh './jenkins/scripts/deliver-for-development.sh'
                input message: 'Finished using the web site? (Click "Proceed" to continue)'
                sh './jenkins/scripts/kill.sh'
            }
        }
        stage('Deploy for production') {
            when {
                branch 'production'  
            }
            steps {
                sh './jenkins/scripts/deploy-for-production.sh'
                input message: 'Finished using the web site? (Click "Proceed" to continue)'
                sh './jenkins/scripts/kill.sh'
            }
        }
    }
}

In my case this solved the problem

            agent {
                docker {
                    image 'node:10-stretch'
                    args '-v /home/jenkins/.ssh:/home/jenkins/.ssh:ro -u 0'
                }
            }

I was using maven to build the project and which indeed was running the frontend-maven-plugin which is responsible for calling the npm install So npm install was breaking with error: path /.npm npm ERR! code EACCES npm ERR! errno -13 npm ERR! syscall mkdir

I changed it to npm install --cache /tmp/empty-cache

and it worked

You can install nvm on the fly before building, in a local directory with NVM_DIR without setting it as global dependency :

mkdir -p node_dir
export NVM_DIR=$(pwd)/node_dir
curl https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
source $(pwd)/node_dir/nvm.sh
nvm install 7
nvm use 7

The new locations are :

$ which node
~/someDir/node_dir/versions/node/v7.7.2/bin/node

$ which npm
~/someDir/node_dir/versions/node/v7.7.2/bin/npm

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM