An example deployment in Docker Swarm from Jenkins using stack deploy.
Jenkinsfile:
pipeline { agent any environment { PROJECT_NAME = 'artem-api' SLACK_CHANNEL = 'maintenance-artem-api' SWARM_MANAGER_IP = '8.8.8.8' SWARM_WORKER_1_IP = '1.1.1.1' SWARM_WORKER_2_IP = '2.2.2.2' TLS_CA = '/var/lib/jenkins/TLS/swarm/ca.pem' TLS_CERT = '/var/lib/jenkins/TLS/swarm/cert.pem' TLS_KEY = '/var/lib/jenkins/TLS/swarm/key.pem' ECR = '111111111111.dkr.ecr.us-west-1.amazonaws.com/artem' PROFILE = 'artem' } stages { stage ('Auth to AWS ECR.') { steps { sh "echo 'Auth to image repo'" sh "eval \$(aws ecr get-login --profile ${PROFILE} --no-include-email) &>/dev/null" } } stage ('Build image'){ steps{ script{ withDockerServer([uri:'tcp://127.0.0.1:4243']) { sh "cp ./.jenkins/Dockerfile ./Dockerfile" echo "Debug: Building with ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER}" def image = docker.build ("${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER}") image.push () echo "Debug: After push to registry" } } } } stage ('Update API on Staging') { when { branch 'staging' } steps { sh 'envsubst < ".jenkins/Staging.yml" > "Staging.yml"' sh "ssh ubuntu@${SWARM_WORKER_1_IP} 'sudo docker pull ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER}'" sh "ssh ubuntu@${SWARM_WORKER_2_IP} 'sudo docker pull ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER}'" sh "docker -H tcp://${SWARM_MANAGER_IP}:2376 --tlsverify --tlscacert=${TLS_CA} --tlscert=${TLS_CERT} --tlskey=${TLS_KEY} stack deploy --compose-file Staging.yml artem-api" } } stage ('Update API on Production') { when { branch 'production' } steps { sh 'envsubst < ".jenkins/Production.yml" > "Production.yml"' sh "ssh ubuntu@${SWARM_WORKER_1_IP} 'sudo docker pull ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER}'" sh "ssh ubuntu@${SWARM_WORKER_2_IP} 'sudo docker pull ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER}'" sh "docker -H tcp://${SWARM_MANAGER_IP}:2376 --tlsverify --tlscacert=${TLS_CA} --tlscert=${TLS_CERT} --tlskey=${TLS_KEY} stack deploy --compose-file Production.yml artem-api" } } } post { success { slackSend channel: "${SLACK_CHANNEL}", color: 'good', message: "Job: ${JOB_NAME}${BUILD_NUMBER} build was successful." } failure { slackSend channel: "${SLACK_CHANNEL}", color: 'danger', message: "Job: ${JOB_NAME}${BUILD_NUMBER} was finished with some error. It may occurs because of the build was rollbacked by docker swarm, or because of other error (watch the Jenkins Console Output): ${JOB_URL}${BUILD_ID}/consoleFull" } unstable { slackSend channel: "${SLACK_CHANNEL}", color: 'warning', message: "Job: ${JOB_NAME}${BUILD_NUMBER} was finished with some error. Please watch the Jenkins Console Output: ${JOB_URL}${BUILD_ID}/console." } } }
Production.yml
version: '3.7' services: production: image: ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER} deploy: replicas: 2 update_config: parallelism: 1 delay: 30s placement: constraints: - node.labels.env == production volumes: - "/var/docker/api/production:/usr/src/app/storage" environment: SERVER_URL: "api.artem.service" ports: - 8080:80
Staging.yml
version: '3.7' services: staging: image: ${ECR}:${PROJECT_NAME}-${JOB_BASE_NAME}-${BUILD_NUMBER} deploy: replicas: 2 update_config: parallelism: 1 delay: 30s placement: constraints: - node.labels.env == staging volumes: - "/var/docker/api/staging:/usr/src/app/storage" environment: SERVER_URL: "stg.api.artem.service" ports: - 8081:80
To use Nginx as a Load Balancer, you need to add it to the namespace with the other containers. For connectivity, add the following to the "http" block in the nginx.conf configuration file:
upstream production { server artem-api_production; } upstream staging { server artem-api_staging; }
And proxy already by the names of upstream.