GitLab CI/CD automated build and release practice

Keywords: Scala Big Data Spark DevOps

Process introduction

CI/CD is a method to frequently deliver applications to customers by introducing automation in the application development phase. The core concepts of CI/CD are continuous integration, continuous delivery and continuous deployment. In this article, I will introduce the practice of automated build and release based on GitLab CI/CD. As shown in the figure below, the whole process will be divided into several parts:

  • 1. First, after the developer completes the development of the project locally, push the code to the Gitlab warehouse.
  • 2. When the code is submitted to the gitlab warehouse, the Pipeline will be triggered, and the Gitlab Runner will run the tasks of each stage in the Pipeline according to the. gitlab-ci.yml configuration file. I have defined three phases: compile, build and deploy.
  • 3. In the compile phase, Gitlab Runner compiles the project into jar packages, uses MinIO as cache, compresses and uploads the dependencies required by the project to MinIO, and can directly download the dependency files from MinIO during the next compilation.
  • 4. In the build phase, Gitlab Runner uses the jar package compiled in the compile phase to build the Docker image and push the image to the image warehouse.
  • 5. In the deploy phase, Gitlab Runner uses the built Docker image to deploy applications in the Kubernetes cluster.

Gitlab CI/CD

Introduction to GitLab

GitLab[1] is an open source application developed by Ruby on Rails, which implements a self managed Git project warehouse, and can access public or private projects through the Web interface. It has similar functions to GitHub, which can browse the source code and manage defects and comments. It can manage the team's access to the warehouse. It is very easy to browse the submitted versions and provides a file history library.

Introduction to GitLab CI/CD

Gitlab CI/CD[2] is a tool built into GitLab for software development through a continuous approach.

  • Continuous Integration
    Suppose an application whose code is stored in GitLab's Git repository. Developers push code changes many times a day. For each push to the repository, you can create a set of scripts to automatically build and test your application, reducing the chance of introducing errors into the application. This approach is called continuous integration. For each change submitted to the application (or even the development branch), it will automatically build and test continuously to ensure that the introduced change passes all the tests, guidelines and code compliance standards you have established for the application.
  • Continuous Delivery
    Continuous delivery is a further operation beyond continuous integration. Not only will the application be built and tested at each code change pushed to the code base, but it can also be deployed continuously as an additional step, although the deployment is triggered manually. This method ensures that the code is checked automatically, but manual intervention is required to manually trigger the change from the policy.
  • Continuous Deployment
    It is similar to continuous delivery, but the difference is that you do not need to deploy it manually, but set it to automatic deployment. Deploy your application without human intervention at all.

Introduction to GitLab Runner

GitLab Runner [3] is used to execute a series of jobs triggered by Gitlab CI/CD and send the results back to gitlab. GitLab Runner can be run in Docker container or deployed to Kubernetes cluster.

Pipeline

Pipeline [4], called pipeline in Chinese, is a phased construction task. For example, the processes of installing dependencies, running tests, packaging, deploying development servers, and deploying production servers are collectively called pipeline.

Stage

Stage [5] refers to the construction stage, which can be understood as the processes of "installation dependency" and "running test" mentioned above. We can define multiple stages in a Pipeline.

Job

Job [6] represents a built job (or task), and represents a specific task executed in a Stage. We can define multiple Jobs in Stages.

The relationship between Pipeline, Stage and Job can be represented by the following figure.


Take an actual Pipeline in Gitlab as an example to explain the meaning of Pipeline, Stage and Job. See the following figure for details.

Introduction to MinIO

MinIO is a distributed, high-performance object storage service designed for large private cloud environments. MinIO is compatible with Amazon S3 object storage interface and is very suitable for storing large amounts of unstructured data, such as pictures, videos, log files, images, etc.
This article will use MinIo as the cache used when compiling the Springboot project. When compiling the project for the first time, the dependency will be pulled from Maven's official website, and then the dependency will be compressed and uploaded to MinIo. The saved dependency file can be directly used when compiling the project next time.

Environment construction

prerequisite

  • Deploy a set of Kubernetes clusters.
  • Install the Helm tool. For Helm installation, please refer to https://helm.sh/zh/docs/intro/install/

MinIO deployment

Helm is a software package management tool in Kubernetes ecosystem, which is convenient for us to quickly deploy applications. We chose to use helm to deploy MinIO in the Kubernetes cluster.

Add Helm warehouse.

helm repo add minio https://helm.min.io/

Install Helm using the following command. Set the user name to admin and the password to admin123456, and deploy in the minio namespace.

helm install minio \
--set accessKey=admin,secretKey=admin123456 \
--namespace minio --create-namespace \
minio/minio

#Return results
minio/minio
NAME: minio
LAST DEPLOYED: Wed Aug 18 13:23:45 2021
NAMESPACE: minio
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Minio can be accessed via port 9000 on the following DNS name from within your cluster:
minio.minio.svc.cluster.local

To access Minio from localhost, run the below commands:

  1. export POD_NAME=$(kubectl get pods --namespace minio -l "release=minio" -o jsonpath="{.items[0].metadata.name}")

  2. kubectl port-forward $POD_NAME 9000 --namespace minio

Read more about port forwarding here: http://kubernetes.io/docs/user-guide/kubectl/kubectl_port-forward/

You can now access Minio server on http://localhost:9000. Follow the below steps to connect to Minio server with mc client:

  1. Download the Minio mc client - https://docs.minio.io/docs/minio-client-quickstart-guide

  2. Get the ACCESS_KEY=$(kubectl get secret minio -o jsonpath="{.data.accesskey}" | base64 --decode) and the SECRET_KEY=$(kubectl get secret minio -o jsonpath="{.data.secretkey}" | base64 --decode)

  3. mc alias set minio-local http://localhost:9000 "$ACCESS_KEY" "$SECRET_KEY" --api s3v4

  4. mc ls minio-local

Alternately, you can use your browser or the Minio SDK to access the server - https://docs.minio.io/categories/17

In order to access the Web interface of MinIO on the local computer, use the following command to turn on port forwarding. You can also choose to expose the MinIO service outside the cluster through NodePort or other methods.

export POD_NAME=$(kubectl get pods --namespace minio -l "release=minio" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward $POD_NAME 9000 --namespace minio

Browser input http://localhost:9000 Access the Minio interface. User name: admin, password: admin123456, which was set when we used helm install to follow Minio.

Create a Bucket named gitlab runner cache Maven to store the dependent files of the compiled project.

Gitlab Runner deployment

Create a Secret to save the user name and password of MinIO, and then the Gitlab Runner container will use this user name and password to log in to MinIO.

kubectl create secret -n acp generic s3access \
--from-literal=accesskey=admin \
--from-literal=secretkey=admin123456

Add the Gilab Helm repository and download the Gitlab Runner Helm resource file.

helm repo add gitlab https://charts.gitlab.io
helm pull gitlab/gitlab-runner --untar

Edit the values.yaml file in the gitlab runner directory. The following configurations need to be modified.

  • gitlabUrl: set the IP address of Gitlab.
  • runnerRegistrationToken: sets the token registered by gitlab runner. Go to project - > Settings - > CI / CD - > runners settings to view the registration token required to register gitlab runner.
  • Cache: set the cache.
# gitlab IP address
gitlabUrl: http://gitlab ip address/

# Register the token of gitlab runner
runnerRegistrationToken: "o_4r2uvptQYmmr79e2uF"

runners:
# Set cache
  cache:
    ## General settings
    cacheType: s3
    cachePath: "gitlab-runner-elasticsearch-api" # Cache path. gitlab runner will automatically create the directory under the bucket
    cacheShared: true

    ## S3 settings
    s3ServerAddress: minio.minio.svc.cluster.local:9000 # Address accessed by kubernetes cluster clusterip service
    s3BucketName: gitlab-runner-cache-maven # bucket name
    s3BucketLocation:
    s3CacheInsecure: true # http login
    secretName: s3access # secert created with Minio username and password

After the configuration is complete, install Gitlab Runner using the following command.

helm install -n acp gitlab-runner-elasticsearch-api gitlab-runner

If everything goes well, you can see Gitlab Runner successfully registered on Gitlab.

View the Pod of Gitlab Runner created in Kubernetes cluster

❯ kubectl get pod -n acp | grep runner
gitlab-runner-elasticsearch-api-gitlab-runner-88f7b64fc-rdfch   1/1     Running     0          113s

Configure the. gitlab-ci.yml file

Gitlab CI/CD defines each Stage of the Pipeline and several jobs in each Stage through the. gitlab-ci.yml configuration file. For example, in the following configuration file, we define three stages:

  • 1.compile: use the openjdk image to compile the project.
  • 2.build: use the jar package compiled in the compile phase to build the Docker image and upload it to the image warehouse.
  • 3.deploy: deploy the application using the built Docker image in the Kubernetes cluster.
stages:
  - compile
  - build
  - deploy

variables:
  KUBECONFIG: /tmp/config
  CI_REGISTRY: Your mirror warehouse IP
  CI_REGISTRY_IMAGE: Your mirror warehouse project path

# maven dependent cache
cache:
  paths:
    - cache 

# Compile project
compile:
  stage: compile
  image: openjdk:8-jdk-alpine
  # The task will be triggered only when you hit tag
  only:
    - tags
  # Compile the project, skip the unit test, and specify the local dependency directory
  script:
    - ./mvnw package -Dmaven.test.skip=true -Dmaven.repo.local=cache
  # Pass the compiled jar package to the next stage for kaniko to build the docker image
  artifacts:
    paths:
      - target

# Build mirror
build:
  image: gcr.io/kaniko-project/executor:debug # You may need to manually download in advance
  stage: build
  only:
    - tags
  script:
   # Using kaniko to build a docker image
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

#Deploy to kubernetes environment
deploy:
  image: bitnami/kubectl:1.19
  stage: deploy
  only:
    - tags
  script:
    # Set the kubeconfig file of the kubectl container so that the kubectl container has the permission to deploy applications in Kubernetes
    - echo $kube_config | base64 -id > $KUBECONFIG
    - sed -i "s/CI_COMMIT_TAG/$CI_COMMIT_TAG/g" manifests/deployment.yaml
    - cat manifests/*.yaml
    - kubectl apply -f manifests/

matters needing attention:

  • For security, we set the user name and password of the image warehouse and the kubeconfig file of the Kubernetes cluster in Gitlab's Secret variables. Enter project - > Settings - > CI / CD - > Secret variables. Note that the value of kubeconfig file is encrypted with base64, and the user name and password of the image warehouse can be set normally.

  • You need to create a cache directory under the project root path to temporarily store dependencies downloaded from Minio. This directory name can be customized and should be consistent with the cache path set in the. gitlab-ci.yml file. In the compile phase, specify the - Dmaven.repo.local=cache parameter and use cache as the dependency repository, so that the dependencies extracted from Minio can be used.
  • In the build phase, use the jar package compiled in the compile phase to build the Docker image. The Dockerfile content is as follows.
FROM openjdk:8-jdk-alpine  
# Set working directory  
WORKDIR /app  
  
# Language, time zone settings  
ENV TZ=Asia/Shanghai  
ENV LANG=en_US.UTF-8  
ENV LANGUAGE=en_US:en  
ENV iC_ALL=en_US.UTF-8  
  
EXPOSE 8080  
# Copy the jar package compiled in the compile phase  
COPY target/*.jar elasticsearch-api.jar  
CMD ["java","-jar","elasticsearch-api.jar"]
  • In the deploy phase, the resource files used to deploy in the Kubernetes environment are written in the manifest directory according to your own project.

Process validation

After completing the development of the project, push the code to the Gitlab warehouse.

git add .
git commit -m "First trigger task"
git push

Pipline will not be triggered at this time, because we set it in the. gitlab-ci.yml configuration file that pipline will be triggered only after the tag is hit. Push tag triggers Pipeline.

git tag 3.0.4
git push origin 3.0.4

Wait for Pipeline execution to complete.

Click the Pipeline number to view the details. You can see that there are three stages in it.


For the first time, Pipeline will be slow in the compile stage, because the dependencies need to be downloaded from the public network when compiling the project. After the compilation is completed, the dependencies in the cache directory will be compressed and packaged and uploaded to Minio. They can be used directly after the next compilation to avoid repeated downloading from the public network, which will speed up a lot.


You can see the dependent compressed files uploaded by Gitlab Runner on MinIO.

The build phase will obtain the compiled jar package and build the Docker image from the target directory set by the artifact in the compile phase.

The deploy phase deploys the image to the Kubernetes environment.

This completes the complete Gitlab CI/CD process:

  • You can see the built image in the image warehouse.

  • You can see the deployed applications in Kubernetes.
❯ kubectl get pod -n acp | grep elasticsearch
elasticsearch-api-7d9656cf5d-5sngv                               2/2     Running     0          30s
elasticsearch-api-7d9656cf5d-9zgzt                               2/2     Running     0          30s
elasticsearch-api-7d9656cf5d-tthx5                               2/2     Running     0          30s

Now we have modified the code, and now push a new tag to trigger the Pipeline again.

git add .
git commit -m "Second trigger task"
git push

git tag 3.0.5
git push origin 3.0.5


You can see that in the compile phase, you did not download the dependency from the public network, but directly downloaded the dependent compressed package from Minio. The whole Pipeline process is also shortened from 5 minutes to 1 minute.

reference material

Posted by SwarleyAUS on Sun, 21 Nov 2021 11:12:41 -0800