Implementing GitLab CI/CD with Docker Swarm, Portainer, and Private Registry in a Local Environment — Part 3/3

Mohamed Fourti
4 min readNov 26, 2023

--

In this final part, we will implement our Continuous Integration and Continuous Deployment (CI/CD) pipeline.

Creating The Project

Now that we have GitLab set up, we can proceed to create a project. Since our focus is more on the CI/CD side, we have chosen a simple free HTML website template to test our architecture.

Create a new repository

cd existing_folder
git init --initial-branch=main
git remote add origin http://192.168.246.139/test-mohamed-fourti/testz.git
git add .
git commit -m "Initial commit"
git push --set-upstream origin main

Git will prompt you to log in with your GitLab credentials. After the push is complete, we can check our project.

Website project

Implementing the CI/CD Workflow

We will have three stages: building and pushing, testing, and finally deploying.

Build/Push Stage

Within our CI/CD pipeline, the “Build/Push” stage transforms the contents of our repository, using the Docker file (provided at the end) and the associated project files, into a deployable Docker image.

Create a “.gitlab-ci.yml” file in our project and place the following script inside it.

stages:
- build
- test
- deploy
variables:
DOCKER_DRIVER: overlay2
IMAGE_TAG: v2

build:
stage: build
image: docker:latest

services:
- docker:dind

before_script:
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" "$DOCKER_REGISTRY"

script:
- echo "Building Docker image..."
- docker version
- docker build --tag $DOCKER_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$IMAGE_TAG -f Dockerfile .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$IMAGE_TAG

tags:
- build-push

Before moving on to the next stage, make sure to add the Docker registry variables. You can read more about GitLab CI/CD variables Here.

GitLab CI/CD variables

Test Stage

In this stage, we will perform a simple test for the Docker image, checking if it is deployable and returns a response.

After the Build Stage.

test:
stage: test
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" "$DOCKER_REGISTRY"
script:
- echo "Deploying services using docker-compose..."
- docker pull $DOCKER_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$IMAGE_TAG
- docker-compose -f compose.yaml up -d web
- sleep 10
- RESPONSE=$(docker-compose -f compose.yaml exec web curl -s http://localhost:80)
- |
if [[ $? -eq 0 ]]; then
echo "Service deployment and test passed."
docker-compose -f compose.yaml down
else
echo "Service deployment or test failed."
docker-compose -f compose.yaml down
exit 1
fi
needs:
- build
tags:
- test

Deploy Stage

Now that we have passed the testing stage, we can proceed to deploy our project as a container in our Docker Swarm cluster.

After the Test Stage.

deploy:
stage: deploy
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" "$DOCKER_REGISTRY"
script:
- echo "Deploying services using docker-compose..."
- docker pull $DOCKER_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$IMAGE_TAG
- docker stack deploy -c docker-compose.yaml $CI_PROJECT_NAME --with-registry-auth
- sleep 20
- |
if wget -qO- http://Manager-IP:7000/ > /dev/null; then
echo "Stack deployment passed."
else
echo "Stack deployment failed."
docker stack rm $CI_PROJECT_NAME
exit 1
fi
needs:
- test
tags:
- deploy

Now that our pipeline is complete, we can initiate it by committing changes. If everything goes as planned, you should see the three stages marked as passed.

All stages Passed

If we check Portainer, we can see that our website is running.

Website Container running

If we visit the website URL on the defined port from the docker-compose (provided at the end), we will see our site up and running.

Website Landing page

And that’s all. Now, each time you make changes and commit, a new image will be built, pushed to the private registry, tested, and then deployed with the new updates.

All the files shown in this three-part series can be found on my GitHub.

Conclusion

In this final part, we’ve successfully run GitLab CI/CD pipelines all in Docker Swarm, creating an automated workflow all while staying in a local environment.

--

--