The purpose of this project is to build a ci/cd pipeline with Gitlab. I will be using my own Gitlab server, register a gitlab-runner, configure the pipeline to build a Docker image, push it to the Docker hub, and deploy it to a server using SSH. For demonstration purpose, I will deploy a small, static web page using Nginx
- 3 VMs using Ubuntu 22.04 (gitlab-server, gitlab-runner, web-server)
- A registered domain-name
- Docker installed in each VM
- Docker hub account
-
Install depedencies
sudo apt update sudo apt install ca-certificates curl openssh-server postfix tzdata perl
-
Install Gitlab
# move into the /tmp directory cd /tmp # download the installation script curl -LO https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh # run the installer sudo bash /tmp/script.deb.sh # install the actual GitLab application with apt sudo apt install gitlab-ce
-
Edit the Gitlab configuration file
- Open config file:
sudo nano /etc/gitlab/gitlab.rb - Match
external_urlwith yourdomainand make sure to changehttptohttpsto automatically redirect users to the site protected by the Let’s Encrypt certificate - Fill
letsencrypt['contact_emails']with your email. In case there are problems with your domain, Let’s Encrypt project can use to contact you - (Optional) If you prefer using your own TLS certs, you can follow this documentation: https://docs.gitlab.com/omnibus/settings/ssl/#configure-https-manually
- Open config file:
-
Reconfigure GitLab
sudo gitlab-ctl reconfigure
-
Logging In for the first time
-
Visit the domain name of your GitLab server in your web browser:
https://your_domain -
GitLab generates an
initial secure passwordfor you. It is stored in/etc/gitlab/initial_root_password -
On the login page, enter
Username: rootandPassword: [the password listed on /etc/gitlab/initial_root_password]
-
Let’s start by creating a GitLab project and adding an HTML file to it. You will later copy the HTML file into an Nginx Docker image, then deploy it to the server
-
Create new project
-
Add html file
-
Add Dockerfile
- Go back to the Project’s overview page, click the + button and select the New file
- Set the File name to
Dockerfile - Add these instructions to the file body:
FROM nginx:stable-alpine3.19-slim COPY index.html /usr/share/nginx/html
- Click Commit changes at the bottom of the page
Besides GitLab, the GitLab runner is yet another system that your private key will enter. For each pipeline, GitLab uses runners to perform the heavy work, that is, execute the jobs you have specified in the CI/CD configuration. That means the deployment job will ultimately be executed on a GitLab runner.
-
Login to
gitlab-runnerserverssh root @ gitlab_runner_ip
-
Install depedencies
sudo apt update sudo apt install ca-certificates curl openssh-server postfix tzdata perl
-
Install gitlab-runner
# add the official GitLab repository curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash # install the gitlab-runner package sudo apt-get install gitlab-runner # verify the installation by checking the service status systemctl status gitlab-runner
-
Obtain project token and GitLab URL
- In your GitLab project, navigate to Settings > CI/CD > Runners > Expand
- In the Project runners section, click on New project runner and follow the form to create a new runner for your project
- Fill a proper Tags e.g. deployment
- Once a runner is in place, you’ll find the registration token and the GitLab URL. Copy both to a text editor
-
Register the runner
# Back to your terminal, register the runner for your project sudo gitlab-runner register -n --url https://your_gitlab.com --registration-token project_token --executor docker --description "Deployment Runner" --docker-image "docker:stable" --tag-list deployment --docker-privileged # Output Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! -
Verify the registration process
The CI/CD will run a web server in this VM. We are going to create a user that is dedicated for the deployment task. You will later configure the CI/CD pipeline to log in to the server with that user.
-
Login to
web-serverssh root @ web-server_ip
-
Create a deployment user
# create a new user sudo adduser deployer # add the user to the Docker group sudo usermod -aG docker deployer
-
Setting up an SSH key for the deployment user
# switch to user deployer su deployer # generate a SSH key ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (~/.ssh/id_rsa): ENTER Enter passphrase (empty for no passphrase): ENTER Enter same passphrase again: ENTER Your identification has been saved in ~/.ssh/id_rsa Your public key has been saved in ~/.ssh/id_rsa.pub
-
To authorize the SSH key for the deployer user, you need to append the public key to the authorized_keys file
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
-
Store the SSH private key to a notepad. Later we will store it as variable in the CI/CD pipeline
cat ~/.ssh/id_rsa # output -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----
We are going to configure the GitLab CI/CD pipeline. The pipeline will build a Docker image and push it to the Docker hub. The final step in your pipeline is to log in to your server, pull the latest Docker image, remove the old container, and start a new container.
-
Navigate to Settings > CI/CD > Variables in your GitLab project and click Add Variable. Fill out the form as follows:
ID_RSA- Key : ID_RSA
- Value : Paste your SSH private key
- Type : File
- Environment Scope: All (default)
- Protect variable: Checked
- Mask variable: Unchecked
SERVER_IP- Key: SERVER_IP
- Value:
your_server_IP - Type: Variable
- Environment scope: All (default)
- Protect variable: Checked
- Mask variable: Checked
SERVER_USER- Key: SERVER_USER
- Value: deployer
- Type: Variable
- Environment scope: All (default)
- Protect variable: Checked
- Mask variable: Checked
CI_REGISTRY- Key: CI_REGISTRY
- Value: docker.io
- Type: Variable
- Environment scope: All (default)
- Protect variable: Unchecked
- Mask variable: Unchecked
CI_REGISTRY_USER- Key: CI_REGISTRY_USER
- Value:
your_docker_username - Type: Variable
- Environment scope: All (default)
- Protect variable: Unchecked
- Mask variable: Unchecked
CI_REGISTRY_PASSWORD- Key: CI_REGISTRY_PASSWORD
- Value:
your_docker_pat - Type: Variable
- Environment scope: All (default)
- Protect variable: Checked
- Mask variable: Checked
-
Go to the Project overview page, click the + button and select New file. Then set the File name to
.gitlab-ci.yml. -
Add these contents to the file body:
stages: - publish - deploy variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" DOCKER_IMAGE: $CI_REGISTRY_USER/<image-name>:$CI_COMMIT_SHORT_SHA publish: image: docker:latest stage: publish services: - docker:dind script: - apk add --no-cache docker - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY - docker build -t $DOCKER_IMAGE . - docker push $DOCKER_IMAGE deploy: image: alpine:latest stage: deploy tags: - deployment script: - chmod og= $ID_RSA - apk update && apk add openssh-client - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY" - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker pull $DOCKER_IMAGE" - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker container rm -f my-app || true" - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "docker run -d -p 80:80 --name my-app $DOCKER_IMAGE" environment: name: production url: http://<server-ip>
-
Finally click Commit changes at the bottom of the page in GitLab to create the
.gitlab-ci.ymlfile -
Validating the CI/CD pipeline
-
Validating the deployment on the web-server
- We want to check the deployed container on your web-server. Head over to your terminal and make sure to log in again
ssh root @ web_server_ip
- List the running containers
docker ps # output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fb86cae159e2 docker-user/my-app:9f4a2e61 "/docker-entrypoint.…" 23 hours ago Up 23 hours 0.0.0.0:80->80/tcp, :::80->80/tcp my-app
- We want to check the deployed container on your web-server. Head over to your terminal and make sure to log in again







