jenkins pipeline parallel post task to clear up docker containers - docker

If I'm running parallel tasks across multiple agents, instead of having a post action on each job, how can I have it on the parallel stage itself? The issue with having them all on each stage is that if the stage is sharing an agent with another stage, docker system prune -f -a can interfere with the other stage.
I am trying to ensure the cache is clear on the build agent, so it's always pulling the latest fresh from Artifactory. Plus cleaning up after the build to make sure nothing is left dangling.
For example
stage("Docker stuff"){
parallel{
stage(foo){
agent{label "docker"}
steps{...}
}
stage(bar){
agent{label "docker"}
steps{...}
}
stage(foobar){
agent{label "docker"}
steps{...}
}
}
post{
always{
sh "docker image prune -f"
sh "docker volume prune -f"
sh "docker container prune -f"
sh "docker system prune -a -f"
sh "docker builder prune -a -f"
}
}
}
The issue with the above is that the post actions run only on the agent selected to manage the parallel stages, not the agents actually doing the running.
The issue with the below is the post actions can interfere with other stages.
stage("Docker stuff"){
parallel{
stage(foo){
agent{label "docker"}
steps{
script{
def dockerImage = _dockerImage("docker-local/foo-image")
docker.withRegistry( "https://artifactory-dev.company.com/", "svc_bar") {
dockerImage.push()
dockerImage.push("latest")
}
}
}
post{
always{
echo "========always========"
sh "docker image prune -f"
sh "docker volume prune -f"
sh "docker container prune -f"
sh "docker system prune -a -f"
sh "docker builder prune -a -f"
}
}
}
stage(bar){
agent{label "docker"}
steps{...}
post{...}
}
stage(foobar){
agent{label "docker"}
steps{...}
post{...}
}
}
}

stage(''){
steps {
script {
timeout(time: 24, unit: 'HOURS') {
try {
parallel(
a: {
},
b: {
},
c: {
}
)
}catch (any){
//currentBuild.result = 'FAILURE'
}finally{
}
}
}
}
}
post {
always {
}
}
It seems to me that you cannot perform certain actions in docker at the same time, in different nodes I do not see a problem

Related

Run job on all existing Jenkins workers

I have a Job in a pipeline that cleans up docker images. It runs the job on each worker individually. This is frustrating because when I add jenkins-cpu-worker3, I'll have to update this job.
I'd like to run this job in such a way that it runs on all workers without having to update it each time a new worker is present. I also want the job to be able to run regardless of what I name each worker. It needs to run on all workers no matter what.
Is there a way to query jenkins from within the pipeline to get me a list or array of all the workers that exist. I was leafing through documentation and posts online and I have not found a solution that works. If possible I'd like to do this without any additional Jenkins Plugins.
pipeline {
agent any
stages {
stage('Cleanup jenkins-cpu-worker1') {
agent {
node {
label 'jenkins-cpu-worker1'
}
}
steps {
sh "docker container prune -f"
sh "docker image prune -f"
sh '''docker images | awk '{print $1 ":" $2}' | xargs docker image rm || true'''
sh "docker network prune -f"
sh "docker volume prune -f"
}
}
stage('Cleanup jenkins-cpu-worker2') {
agent {
node {
label 'jenkins-cpu-worker2'
}
}
steps {
sh "docker container prune -f"
sh "docker image prune -f"
sh '''docker images | awk '{print $1 ":" $2}' | xargs docker image rm || true'''
sh "docker network prune -f"
sh "docker volume prune -f"
}
}
Here is an improved version of your Pipeline. This will dynamically get all the active agents, and run your cleanup task in parallel.
pipeline {
agent any
stages {
stage('CleanupWorkers') {
steps {
script {
echo "Something"
parallel parallelJobs()
}
}
}
}
}
def parallelJobs() {
jobs = [:]
for (def nodeName in getAllNodes()) {
jobs[nodeName] = getStage(nodeName)
}
return jobs
}
def getStage(def nodeName){
return {
stage("Cleaning $nodeName") {
node(nodeName){
sh'''
echo "Srating cleaning"
docker container prune -f
docker image prune -f
docker images | awk '{print $1 ":" $2}' | xargs docker image rm || true
docker network prune -f
docker volume prune -f
'''
}
}
}
}
def getAllNodes() {
def nodeNames = []
def jenkinsNodes = Jenkins.instance.getNodes().each { node ->
// Ignore offline agents
if (!node.getComputer().isOffline()){
nodeNames.add(node.getNodeName())
}
}
return nodeNames
}

Jenkins - Mark build as success

I use Jenkins to build my maven Java app then create Docker image and push it. After all of that I have try-catch where I Try to stop and remove the container if it's already running - If not it should just skip it and run the new Image - It works but always marks the build as failed. I tried to change the build status, but apparently that is not possible.
Here is my pipeline:
node {
stage('Clone repository') {
git branch: 'main', credentialsId: 'realsnack-git', url: 'https://github.com/Realsnack/Java-rest-api.git'
}
stage('Build maven project') {
sh './mvnw clean package'
}
stage('Build docker image') {
sh 'docker build -t 192.168.1.27:49153/java-restapi:latest .'
}
stage('Push image') {
sh 'docker push 192.168.1.27:49153/java-restapi:latest'
}
try {
stage('Remove old container') {
sh 'docker stop java-rest_api && docker rm java-rest_api'
}
} catch(all) {
sh 'No container to remove - runnning it anyway'
} finally {
stage('Run image') {
sh 'docker run -d --name java-rest_api -p 8081:8081 192.168.1.27:49153/java-restapi:latest'
}
}
}
docker stop will fail if it fails to stop the container.
You can solve the issue in one of the two following ways:
Check that there is a running container before attempting to stop it:
sh "if [[ docker ps -a | grep java-rest_api ]]; docker stop java-rest_api; fi"
Ignore the docker error:
sh "docker stop java-rest_api || true"

How to create new docker container and run it from Jenkinsfile

I've inherited this Jenkinsfile stage that will run a new docker image using withRun:
stage('Deploy') {
steps {
script {
docker.image('deployscript:latest').withRun("""\
-e 'IMAGE=${IMAGE_NAME}:${BUILD_ID}' \
-e 'CNAME=${IMAGE_NAME}' \
-e 'PORT=${PORT_1}:80' \
-e 'PORT=${PORT_2}:443'""") { c ->
sh "docker logs ${c.id}"
}
}
}
}
However, I believe this method is only meant for testing purposes and actually stops the container once the block is finished. I want this step to actually run the container and stop/restart the previous one if necessary. The documentation out there on this is surprisingly sparse. Please help.
If you want to run the docker container throughout all the stages, thenthe example would look like below:
Scripted Pipeline
node('master') {
/* Requires the Docker Pipeline plugin to be installed */
docker.image('alpine:latest').inside {
stage('01') {
sh 'echo STAGE01'
}
stage('02') {
sh 'echo STAGE02'
}
}
}
Declarative Pipeline
pipeline {
agent {
docker {
image 'alpine:latest'
label 'master'
args '-v /tmp:/tmp'
}
}
stages {
stage('01') {
steps {
sh "echo STAGE01"
}
}
stage('02') {
steps {
sh "echo STAGE02"
}
}
}
}
In both scripted and declarative pipelines, The docker container from the alpine image will active for all the stages to finish and only delete if the stage is a success or failure.
But If you would want to control start, stop, restart the container yourself on different stages, you can do it with bash command or by writing a small groovy script wrapping the docker command like below
node {
stage('init') {
docker create --name myImage1 -v $(pwd):/var/jenkins -w /var/jenkins imageName:tag
}
stage('build') {
// make use of docker command to start, stop and execute some script inside the container
// same goes for other stage
//once all done you can remove the container
docker rm myImage1
}
}
The following will stop the existing container and run a new one with the new image:
stage('Deploy') {
steps {
sh "docker stop ${IMAGE_NAME} || true && docker rm ${IMAGE_NAME} || true"
sh "docker run -d \
--name ${IMAGE_NAME} \
--publish ${PORT}:443 \
${IMAGE_NAME}:${BUILD_ID}"
}
}

How can I use agent docker in a declartive pipeline running on jenkins ssh-slave node?

I am running jenkins master and slave as docker container. I have setup a slave node using jenkins/ssh-slave image with label 'worker'. I can successfully run my pipeline on the worker node. However, when I am trying to run docker build command using the Jenkinsfile, I am getting error docker: not found.
pipeline {
agent { label 'worker' }
tools {nodejs "node"}
stages {
stage ('Build APP') {
steps {
echo 'BUILDING APPLICATION'
sh 'npm install'
}
}
stage ('Create Package') {
steps {
script{
echo 'BUILDING DOCKER IMAGE'
docker.build("package${env.BUILD_NUMBER}")
}
}
}
stage('Package Test') {
agent { docker }
steps {
echo 'RUNNING IMAGE IN CONATAINER'
sh "docker run -p 5050:4000 -d package${env.BUILD_NUMBER}"
echo 'CHECKING HEALTH STATUS'
script {
try {
sh "curl -s --head --request GET http://127.0.0.1:5050/ | grep '200'"
echo 'Health Check Passed!'
} catch(Exception e) {
echo "Health Check Failed!"
}
}
}
}
In the third step 'package test' I have placed agent docker in the file but it doesn't seem to work. How can I place agent docker in a declarative pipeline?

How to change the Agent label in Jenkins depending on Branch Name

I am creating a Jenkin Pipeline for below task.
Pull the latest code from vsts
Build the code and create .jar file out of it
creating a Docker image on the basis of the jar
tag the image
push the image into Docker registry
for this, I have written below Jenkinsfile
pipeline {
agent {
label "master"
}
stages {
stage('Build') {
steps {
echo '..........................Building Jar..........................'
sh 'npm install'
}
}
stage('Build-Image') {
steps {
echo '..........................Building Image..........................'
sh 'sudo docker build -t some-org/admin-portal:v0.1 --build-arg PORT=9007 --build-arg ENVIRONMENT=develop .'
}
}
stage('Tag-Image') {
steps {
echo '..........................Taging Image..........................'
sh 'sudo docker login some-repo -u username001 -p password'
sh 'sudo docker tag some-org/admin-portal:v0.1 some.dtr.io/some-org/admin-portal:v0.1'
}
}
stage('Push-Image') {
steps {
echo '..........................Pushing Image..........................'
sh 'sudo docker push some.dtr.io/some-org/admin-portal:v0.1'
}
}
}
}
Below is Jenkins job configuration snapshot for Pipeline
My Question is how can I change the agent label depending upon branch name or some conditions.
e.g if the branch is develop I want to use slave1 node and if the branch is production I want to use master
Any Help will be appreciable.
Thanks in Advance.
You can assign the agent labels inside the stage, so that you can execute the stages with required agents.
eg:
pipeline {
agent none
stages {
stage('Build') {
agent {
label "master"
}
steps {
echo '..........................Building Jar..........................'
sh 'npm install'
}
}
stage('Build-Image') {
agent {
label "master"
}
steps {
echo '..........................Building Image..........................'
sh 'sudo docker build -t some-org/admin-portal:v0.1 --build-arg PORT=9007 --build-arg ENVIRONMENT=develop .'
}
}
stage('Tag-Image') {
agent {
label "slave1"
}
steps {
echo '..........................Taging Image..........................'
sh 'sudo docker login some-repo -u username001 -p password'
sh 'sudo docker tag some-org/admin-portal:v0.1 some.dtr.io/some-org/admin-portal:v0.1'
}
}
stage('Push-Image') {
agent {
label "slave1"
}
steps {
echo '..........................Pushing Image..........................'
sh 'sudo docker push some.dtr.io/some-org/admin-portal:v0.1'
}
}
}
}

Resources