How do I run multiple commands within a Docker container in Jenkins?

时间:2016-12-02 05:22:30

标签: jenkins docker

I have a somewhat complicated build-and-test process that I would like to run within a Docker container. Since the software under test fires up some xterm windows, I'm running the X server and VNC within the container using supervisord to start them. Then, once the container is running with supervisord as the ENTRYPOINT, I can manually kick off the build-and-test process using docker exec.

I'm having trouble nailing down the approach to do this using Jenkins and the Docker Pipeline plugin. There seem to be some limitations with the current version of the plugin that I can't sufficiently work around. Here's the basic idea:

node {
    stage('Fetch installer') {
        // SCM commands go here
    }

    def image = docker.image('metatest')
    image.inside() {
        stage('Run installer') {
            sh "./installer.sh"
        }
        stage('Run tests') {
            sh "/opt/custom/run_tests.sh"
        }
    }
}

This approach is broken, however, because the image.inside() method overrides both the default user (which it sets to Jenkins' user ID) and the ENTRYPOINT. I need to be able to start the Docker container using the default root user because the X server processes must be root. However, I need to run the installation and tests within the container as the Jenkins non-root user so that Jenkins can manage the output materials.

For the record, image.inside() sets up a number of very useful parameters when starting the Docker container:

docker run -t -d -u 113:123 \
  -w "/var/lib/jenkins/workspace/TestProj" \
  -v "/var/lib/jenkins/workspace/TestProj:/var/lib/jenkins/workspace/TestProj:rw" \
  -v "/var/lib/jenkins/workspace/TestProj@tmp:/var/lib/jenkins/workspace/TestProj@tmp:rw" \
  -e ******** -e ******** -e ******** -e ******** -e ******** \
  -e ******** -e ******** -e ******** -e ******** -e ******** \
  -e ******** -e ******** -e ******** -e ******** -e ******** \
  -e ******** -e ******** -e ******** \
  --entrypoint cat \
  metatest

The setup of the Jenkins uid:gid pair, the working directory, the volumes to share from the host, and the Jenkins environment variables (masked here) are all very useful and annoying to reproduce manually in Jenkins. The ENTRYPOINT override is not as helpful in this case.

Unfortunately the implementation of the Docker Pipeline command image.inside() does not allow me to separate the setup of a container from the execution of stages within the running container that it creates. There are other commands for part of that process, but they seem to be missing the actual execution step AND they don't include a lot of the useful parameter setup.

In other words, I'd like to do something like this:

node {
    stage('Fetch installer') {
        // SCM commands go here
    }

    def image = docker.image('metatest')
    def container = image.run('-u 0:0')
    container.inside() {
        stage('Run installer') {
            sh "./installer.sh"
        }
        stage('Run tests') {
            sh "/opt/custom/run_tests.sh"
        }
    }
    stage('Check output') {
        // run a script on the Jenkins host
    }
    container.inside() {
        stage('Final script step') {
            sh "./another_script.py"
        }
    }
    container.stop()
    container.remove()
}

The key differences are:

  1. image.run() sets up the same uid:gid pair for Jenkins (which I then override), plus all the workspace, volumes, and environment variables, but NOT the ENTRYPOINT override.
  2. container.inside() exists and allows me to perform only the execution step inside a running container.
  3. container.inside() sets up all the same parameters as step 1.
  4. container.stop() only stops the container, while remove() actually removes it. No need for them to be combined.

With this approach I can have separate commands executed inside the container, then run some additional commands in Jenkins, return to the container, stop and restart the container, save an image of the container before removing it, etc. All of those things are more difficult with the current plugin.

I've put in a request to Jenkins to add this separation, but I need an approach without that. Any ideas?

0 个答案:

没有答案