Skip to content

Instantly share code, notes, and snippets.

@bvis
Last active January 3, 2023 20:45
Show Gist options
  • Save bvis/68f3ab6946134f7379c80f1a9132057a to your computer and use it in GitHub Desktop.
Save bvis/68f3ab6946134f7379c80f1a9132057a to your computer and use it in GitHub Desktop.
Jenkin pipeline definition example to be integrated with Docker Swarm cluster in our CI/CD environment
pipeline {
agent { node { label 'swarm-ci' } }
environment {
TEST_PREFIX = "test-IMAGE"
TEST_IMAGE = "${env.TEST_PREFIX}:${env.BUILD_NUMBER}"
TEST_CONTAINER = "${env.TEST_PREFIX}-${env.BUILD_NUMBER}"
REGISTRY_ADDRESS = "my.registry.address.com"
SLACK_CHANNEL = "#deployment-notifications"
SLACK_TEAM_DOMAIN = "MY-SLACK-TEAM"
SLACK_TOKEN = credentials("slack_token")
DEPLOY_URL = "https://deployment.example.com/"
COMPOSE_FILE = "docker-compose.yml"
REGISTRY_AUTH = credentials("docker-registry")
STACK_PREFIX = "my-project-stack-name"
}
stages {
stage("Prepare") {
steps {
bitbucketStatusNotify buildState: "INPROGRESS"
}
}
stage("Build and start test image") {
steps {
sh "docker-composer build"
sh "docker-compose up -d"
sh """
docker run --rm \
-v '${env.WORKSPACE}':'/project':ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-e TIMEOUT=30 \
-e COMPOSE_PROJECT_NAME=\$(basename \"'${env.WORKSPACE}'\") \
softonic/compose-project-is-up
"""
}
}
stage("Run tests") {
steps {
sh "docker-compose exec -T php-fpm composer --no-ansi --no-interaction tests-ci"
sh "docker-compose exec -T php-fpm composer --no-ansi --no-interaction behat-ci"
}
post {
always {
junit "build/junit/*.xml"
step([
$class: "CloverPublisher",
cloverReportDir: "build/coverage",
cloverReportFileName: "clover.xml"
])
}
}
}
stage("Determine new version") {
when {
branch "master"
}
steps {
script {
env.DEPLOY_VERSION = sh (returnStdout: true, script: "docker run --rm -v '${env.WORKSPACE}':/repo:ro softonic/ci-version:0.1.0 --compatible-with package.json").trim()
env.DEPLOY_MAJOR_VERSION = sh (returnStdout: true, script: "echo '${env.DEPLOY_VERSION}' | awk -F'[ .]' '{print \$1}'").trim()
env.DEPLOY_COMMIT_HASH = sh (returnStdout: true, script: "git rev-parse HEAD | cut -c1-7").trim()
env.DEPLOY_BUILD_DATE = sh (returnStdout: true, script: "date -u +'%Y-%m-%dT%H:%M:%SZ'").trim()
env.DEPLOY_STACK_NAME = "${env.STACK_PREFIX}-v${env.DEPLOY_MAJOR_VERSION}"
env.IS_NEW_VERSION = sh (returnStdout: true, script: "[ '${env.DEPLOY_VERSION}' ] && echo 'YES'").trim()
}
}
}
stage("Create new version") {
when {
branch "master"
environment name: "IS_NEW_VERSION", value: "YES"
}
steps {
script {
sshagent(['ci-ssh']) {
sh """
git config user.email "ci-user@email.com"
git config user.name "Jenkins"
git tag -a "v${env.DEPLOY_VERSION}" \
-m "Generated by: ${env.JENKINS_URL}" \
-m "Job: ${env.JOB_NAME}" \
-m "Build: ${env.BUILD_NUMBER}" \
-m "Env Branch: ${env.BRANCH_NAME}"
git push origin "v${env.DEPLOY_VERSION}"
"""
}
}
sh "docker login -u=$REGISTRY_AUTH_USR -p=$REGISTRY_AUTH_PSW ${env.REGISTRY_ADDRESS}"
sh "docker-compose -f ${env.COMPOSE_FILE} build"
sh "docker-compose -f ${env.COMPOSE_FILE} push"
}
}
stage("Deploy to production") {
agent { node { label "swarm-prod" } }
when {
branch "master"
environment name: "IS_NEW_VERSION", value: "YES"
}
steps {
sh "docker login -u=$REGISTRY_AUTH_USR -p=$REGISTRY_AUTH_PSW ${env.REGISTRY_ADDRESS}"
sh "docker stack deploy ${env.DEPLOY_STACK_NAME} -c ${env.COMPOSE_FILE} --with-registry-auth"
}
post {
success {
slackSend (
teamDomain: "${env.SLACK_TEAM_DOMAIN}",
token: "${env.SLACK_TOKEN}",
channel: "${env.SLACK_CHANNEL}",
color: "good",
message: "${env.STACK_PREFIX} production deploy: *${env.DEPLOY_VERSION}*. <${env.DEPLOY_URL}|Access service> - <${env.BUILD_URL}|Check build>"
)
}
failure {
slackSend (
teamDomain: "${env.SLACK_TEAM_DOMAIN}",
token: "${env.SLACK_TOKEN}",
channel: "${env.SLACK_CHANNEL}",
color: "danger",
message: "${env.STACK_PREFIX} production deploy failed: *${env.DEPLOY_VERSION}*. <${env.BUILD_URL}|Check build>"
)
}
}
}
}
post {
always {
sh "docker-compose down || true"
}
success {
bitbucketStatusNotify buildState: "SUCCESSFUL"
}
failure {
bitbucketStatusNotify buildState: "FAILED"
}
}
}
@iamdanthedev
Copy link

Thanks that's a very useful script of advanced usage

@zhex900
Copy link

zhex900 commented Aug 10, 2018

Hi,
I am having trouble mounting the workspace into the docker container. I tried to follow your example. But it doesn't work on my end. My Jenkins is in a container. Inside the Jenkins container, I can mount all the path except /var/jenkins_home/workspace/learn, learn is my project name.

pipeline {
    agent any
    stages {
        stage('build') {
            steps {
                    sh "ls"
                    sh "docker run --rm -v '${env.WORKSPACE}':'/project'  busybox cat /project/Dockerfile"
            }
        }
    }
}

Obtained Jenkinsfile from git https://github.com/zhex900/nginx-pagespeed
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/learn
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Declarative: Checkout SCM)
[Pipeline] checkout
 > git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
 > git config remote.origin.url https://github.com/zhex900/nginx-pagespeed # timeout=10
Fetching upstream changes from https://github.com/zhex900/nginx-pagespeed
 > git --version # timeout=10
 > git fetch --tags --progress https://github.com/zhex900/nginx-pagespeed +refs/heads/*:refs/remotes/origin/*
 > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
 > git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
Checking out Revision 3bd0272e04d0873853eaae673319e014abc7e7a1 (refs/remotes/origin/master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 3bd0272e04d0873853eaae673319e014abc7e7a1
Commit message: "learning jenkins"
 > git rev-list --no-walk 152536ea3e38189abc3d68c494b1b6bd2bda50e1 # timeout=10
[Pipeline] }
[Pipeline] // stage
[Pipeline] withEnv
[Pipeline] {
[Pipeline] stage
[Pipeline] { (build)
[Pipeline] sh
[learn] Running shell script
+ ls
Dockerfile
Jenkinsfile
README.md
config
docker-compose
docker-compose.yml
env-example
scripts
typescript
[Pipeline] sh
[learn] Running shell script
+ docker run --rm -v /var/jenkins_home/workspace/learn:/project busybox cat /project/Dockerfile
cat: can't open '/project/Dockerfile': No such file or directory
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
ERROR: script returned exit code 1
Finished: FAILURE

@touch-dev-null
Copy link

How it will work:

...
post {
      always {
          sh "docker-compose down || true"
      }
...

if 2 or more builds will be scheduled?

First completed build will execute docker-compose down and all other builds will fail?

@sang-d
Copy link

sang-d commented Jul 21, 2019

how did you pass oauth credential into: bitbucketStatusNotify buildState: "INPROGRESS"?

@mts88
Copy link

mts88 commented Oct 29, 2019

Hi, i have an error docker-compose: not found. I have installed Docker plugins in Jenkins but doesn't works.
Thanks in advance.

@bvis
Copy link
Author

bvis commented Oct 29, 2019

Hi, docker-compose is a binary that needs to be installed "manually", you have more info about it here: https://docs.docker.com/compose/install/

Anyway take this Jenkinsfile as a sample to just get the idea of how to implement your own pipeline, don't take it as a real pipeline, each application has its own needs.

@mts88
Copy link

mts88 commented Oct 29, 2019

Of course. Thank you for your help, you save my day ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment