8

Using Jenkins Declarative Pipeline, one can easily specify a Dockerfile, agent label, build args and run args as follows:

Jenkinsfile (Declarative Pipeline)

agent {
    dockerfile {
        dir './path/to/dockerfile'
        label 'my-label'
        additionalBuildArgs  '--build-arg version=1.0'
        args '-v /tmp:/tmp'
    }
}

I am trying to achieve the same using the scripted pipeline syntax. I found a way to pass the agent label and run args, but was unable to to pass the directory and build args. Ideally, I would write something like this (label and run args are already working):

Jenkinsfile (Scripted Pipeline)

node ("my-label"){
    docker.dockerfile(
        dir: './path/to/dockerfile',
        additionalBuildArgs:'--build-arg version=1.0'
    ).inside('-v /tmp:/tmp') {
        \\ add stages here
    }
}

The documentation shows how this can be done using an existing docker image, i.e., with the image directive in the pipeline.

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent {
        docker { image 'node:7-alpine' }
    }
    stage('Test') {
        //...
    }
}

Jenkinsfile (Scripted Pipeline)

node {
    docker.image('node:7-alpine').inside {
        stage('Test') {
            //...
        }
    }
}

However, the scripted pipeline syntax for the dockerfile directive is missing. The workaround I am using at the moment is building the image myself.

node ("my-label"){
    def testImage = docker.build(
        "test-image",
        "./path/to/dockerfile",
        "--build-arg v1.0"
    )

    testImage.inside('-v /tmp:/tmp') {
        sh 'echo test'
    }
}

Any help is much appreciated!

Mig82
  • 4,856
  • 4
  • 40
  • 63
Philipp Jung
  • 105
  • 1
  • 1
  • 3
  • The scripted docker.build() command takes 2 arguments: image tag, docker build command line. The question shows 3 arguments. Instead it should be something like docker.build("name:1.0", "--build-arg version=v1.0 path/to/directory" – chrisinmtown Sep 14 '21 at 18:31

2 Answers2

2

I personally put the docker cli arguments before the image folder path and would specify the docker filename with -f argument

Apart from that, you are doing this the right way. agent dockerfile is building a docker image the same way docker.build step is doing. Except you can push your image to a registry by using the docker.build step

Here is I how do

def dockerImage
//jenkins needs entrypoint of the image to be empty
def runArgs = '--entrypoint \'\''
pipeline {
    agent {
        label 'linux_x64'
    }
    options {
        buildDiscarder(logRotator(numToKeepStr: '100', artifactNumToKeepStr: '20'))
        timestamps()
    }
    stages {
        stage('Build') {
            options { timeout(time: 30, unit: 'MINUTES') }
            steps {
                script {
                    def commit = checkout scm
                    // we set BRANCH_NAME to make when { branch } syntax work without multibranch job
                    env.BRANCH_NAME = commit.GIT_BRANCH.replace('origin/', '')

                    dockerImage = docker.build("myImage:${env.BUILD_ID}",
                        "--label \"GIT_COMMIT=${env.GIT_COMMIT}\""
                        + " --build-arg MY_ARG=myArg"
                        + " ."
                    )
                }
            }
        }
        stage('Push to docker repository') {
            when { branch 'master' }
            options { timeout(time: 5, unit: 'MINUTES') }
            steps {
                lock("${JOB_NAME}-Push") {
                    script {
                        docker.withRegistry('https://myrepo:5000', 'docker_registry') {
                            dockerImage.push('latest')
                        }
                    }
                    milestone 30
                }
            }
        }
    }
}
fredericrous
  • 2,833
  • 1
  • 25
  • 26
  • Thanks for your reply. In my distributed use-case adding a docker registry adds additional complexity. Using `agent dockerfile` instead automatically rebuilds on changes (and caches the changes locally). This is preferable in my case since I have various versions of dockerfiles, depending on the current git branch. I've been digging into the Jenkins DSL but was unable to understand what `agent dockerfile` uses internally. Do you have any recommendations on how to investigate further?. – Philipp Jung Jan 15 '20 at 11:52
  • I put the registry step just to show a full workflow. I cannot test on my own jenkins at the moment but if you are using the `docker.build` on the same linux slave agent, it should cache your previous builds. The only difference with agent dockerfile is that it uses the master server to build your docker image therefore it uses always the same machine and can reuse the cache made by docker – fredericrous Jan 15 '20 at 12:06
  • I didn't know you could mix declarative (new) and scripted (old) syntaxes! Is that required to access certain features, or could it all be done with scripted (old) syntax? – chrisinmtown Sep 14 '21 at 13:00
  • I guess everything can be done with the "old" syntax. However the declarative syntax allows you to lint your pipeline and gives you a good structure. Ideally, you would put your old syntax behind a method that lives in a shared library. You can also generate jobs from declarative pipelines with something like https://github.com/SAP-archive/jenkins-pipelayer – fredericrous Sep 14 '21 at 13:06
2

Here is a purely old-syntax scripted pipeline that solves the problem of checking out, building a docker image and pushing the image to a registry. It assumes the Jenkins project is type "Pipeline script from SCM".

I developed this pipeline for a server that requires proxies to reach the public internet. The Dockerfile accepts build arguments to configure its tools for proxies.

I think this has a pretty good structure @fredericrous :) but I'm new to pipelines, please help me improve!

def scmvars
def image
node {    
    stage('clone') {
        // enabled by project type "Pipeline script from SCM"
        scmvars = checkout(scm)
        echo "git details: ${scmvars}"
    }
    stage('env') {
        // Jenkins provides no environment variable view
        sh 'printenv|sort'
    }
    stage('build') {
        // arg 1 is the image name and tag
        // arg 2 is docker build command line
        image = docker.build("com.mycompany.myproject/my-image:${env.BUILD_ID}",
              " --build-arg commit=${scmvars.GIT_COMMIT}"
            + " --build-arg http_proxy=${env.http_proxy}"
            + " --build-arg https_proxy=${env.https_proxy}"
            + " --build-arg no_proxy=${env.no_proxy}"
            + " path/to/dir/with/Dockerfile")
    }
    stage('push') {
        docker.withRegistry('https://registry.mycompany.com:8100',
                            'jenkins-registry-credential-id') {
            image.push()
        }
    }
}
chrisinmtown
  • 3,571
  • 3
  • 34
  • 43