vzshi
vzshi

Reputation: 171

How do I pull a Docker image from one private registry and push it to a second different private registry in Jenkins pipeline

I am able to connect to both private registries from Jenkins and I can pull the image I want to, however I don't know how to push that same image to a different repo.

Note, I am using scripted pipeline syntax since declarative syntax doesn't support pushing/pulling or custom registries as far as I know. I'm also not familiar with Groovy syntax.

Here's what I've got so far for my Jenkinsfile:

node {
    checkout scm

    docker.withRegistry('https://private-registry-1', 'credentials-1') {
        def image = docker.image('my-image:tag')
        image.pull()

        docker.withRegistry('https://private-registry-2', 'credentials-2') {
            image.push()
        }
    }
}

I put the second "withRegistry()" method within the first so that I could use the defined "image" variable.

I successfully connect to the first registry and pull the latest image. From Jenkins console output:

Login Succeeded
[Pipeline] {
[Pipeline] sh
+ docker pull private-registry-1/my-image:tag
tag: Pulling from my-image
Digest: sha256:XXXXX
Status: Image is up to date for private-registry-1/my-image:tag

However, here's the relevant error snippet after connecting to the second registry:

...
Login Succeeded
[Pipeline] {
[Pipeline] sh
+ docker tag my-image:tag private-registry-2/my-image:tag
Error response from daemon: No such image: my-image:tag
...

I am using a Jenkins container on my local Windows machine. It's connected to Docker for Windows through my Ubuntu terminal (Windows Subsystem for Linux).

Upvotes: 5

Views: 14315

Answers (3)

hznut
hznut

Reputation: 33

For the declarative syntax the following worked for me:

pipeline {
    agent {
        docker {
            label 'service'
            alwaysPull false
            registryUrl "${my-private-docker-registry_url_with_https}"
            registryCredentialsId "${jenkins_credential_id_for_login}"
            image 'lambci/lambda:build-python3.7'
            args '-v /var/run/docker.sock:/var/run/docker.sock --network host'
        }
    }

Upvotes: 1

Zi Sang
Zi Sang

Reputation: 89

Thanks to the answer from VictoryShoe!

One thing is important and took a long time for me to find out the mistake:

  • "docker.withRegistry('https://private-registry-x', 'credentials-x')" -> Do not forget to add the "https://" at the front of the registryURL
  • In command line "sh 'docker tag private-registry-1/my-image:tag private-registry-2/my-image:tag'", please do NOT add any "https://" in the front of the registryURL

The following jenkinsfile has worked for me:

PS: in my use case, i pull a source-image from DockerHub, tag this image, then push this target-image to a private company image registry.

def registryCredentials1 = "cridentialIdOfJenkinsForRegistry1"
def registryCredentials2 = "cridentialIdOfJenkinsForRegistry2"
def protocol = "https://"
def registryURL1 = "registry.hub.docker.com"
def registryURL2= "harbor.mycompany.xx.yy.com"

pipeline {
    agent any

    parameters {
         string(name: 'sourceImageName', defaultValue: '', description: 'Source-Image-Name, name-schema is like user/foo, e.g. jenkins/jenkins')
         string(name: 'sourceImageTag', defaultValue: '', description: 'Source-Image-Tag, e.g. lts')
         string(name: 'targetImageName', defaultValue: '', description: 'Target-Image-Name, name-schema is like user/foo, e.g. jenkins/jenkins')
         string(name: 'targetImageTag', defaultValue: '', description: 'Target-Image-Tag, e.g. lts')
    }

    stages {

        stage('Pull source-image from Registry 1 & tag the image') {
            steps {
                script {
                    //pull source-image from registry 1
                    docker.withRegistry(protocol + registryURL1, registryCredentials1) {
                        docker.image("${params.sourceImageName}:${params.sourceImageTag}").pull()
                    }

                    //tag the image
                    sh "docker tag ${registryURL1}/${params.sourceImageName}:${params.sourceImageTag} ${registryURL2}/${params.targetImageName}:${params.targetImageTag}"
                }
            }
        }

        stage('push target-image to Registry 2') {
            steps {
                script {
                    //push target-image to registry 2
                    docker.withRegistry(protocol + registryURL2, registryCredentials2) {
                        sh "docker push ${registryURL2}/${params.targetImageName}:${params.targetImageTag}"
                    }
                }
            }
        }
    }
}

Upvotes: 1

vzshi
vzshi

Reputation: 171

The solution was to tag the image before pushing it, final code:

node {
    checkout scm

    stage 'Pull latest image from private-registry-1'

    def image
    docker.withRegistry('https://private-registry-1', 'credentials-1') {
        image = docker.image('my-image:tag')
        image.pull()
    }

    stage 'Push image to private-registry-2'

    // SOLUTION START
    sh 'docker tag private-registry-1/my-image:tag private-registry-2/my-image:tag'
    image = docker.image('private-registry-2/my-image:tag')
    // SOLUTION END

    docker.withRegistry('https://private-registry-2', 'credentials-2') {
        image.push()
    }
}

I don't like how the tagging is done manually through "sh" but I couldn't find a way to do it through the built-in Docker syntax. I will also need to parameterize the image name and tag (my-image:tag) for future use.

Upvotes: 10

Related Questions