GitOps 101 (Part 2)
In this series, we will create the CI/CD pipeline structure to achieve the GitOps approach. As we mentioned in our previous article, we chose to develop a “React Application”, use GitLab as a development environment, Kubernetes as a deployment environment, and ArgoCD tool for the GitOps approach.
In order to deploy a “React Application” into the Kubernetes environment, the code must be compiled and dockerized. For this reason, we need a build stage to compile our code, an image stage to dockerize, and a deploy stage to allow ArgoCD to update our deployment. Then we can write these steps as stages when creating our “.gitlab-ci.yml” file.
stages:
- build
- image
- deploy
Note: In the image stage, you can compile your project in the Dockerfile you will use and create the docker image if you want. In this way, you can also make an edit consisting of only Image and Deploy stages.
First of all, Our “React Application” in the repository at the build stage should be able to compile without errors, and the output artifacts must be kept. Therefore, the necessary npm packages should be installed first and then the build script should run. (These are defined in the package.json file). We also need to make a definition for the output artifacts of the build script. We will use these stored artifacts in the image stage in our Dockerfile.
build-job:
stage: build
image: node:14.17-alpine
script:
- npm install
- npm run-script build
artifacts:
when: on_success
paths:
- "build"
- "public"
Note: We recommend adding a condition for the build job to create the artifacts folders only on a successful build as they may have already been created or they may contain old artifacts. Defining only the directories containing only the required files reduces the artifacts size and the time spent in the pipeline.
After the aforementioned “React Application” is successfully compiled and the artifacts are created, we can move on to our next stage, the Image stage. We will create an image in accordance with the naming convention we have set with the help of docker-cli commands from the Dockerfile, which is used to produce a container image (containing our application), on the image stage.
To make this step wait for the build process, we can add the completion of the build job, as a requirement. Then, we add the necessary condition to our job’s needs section to make it use the artifacts created in the build stage. We need various information when we build our Docker image and upload it on “GitLab Registry”. We should set this information in a job as a variable.
> Note: If you are doing this work in GitLab, you may not want to keep sensitive information in the pipeline. In such cases, you can define a variable in the CI/CD section of the repository setting and prevent these variables from appearing in the job’s output. In addition, if you are going to run GitLab Runner on a Docker Container, the dind (docker in docker) service must be defined, for Docker commands to run.
docker:
stage: package
image: docker:stable
needs:
- job: build-job
artifacts: true
variables:
DOCKER_BUILD_DIR: "."
DOCKER_BUILD_FILE: "Dockerfile"
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
DOCKER_USER: $CI_REGISTRY_USER
DOCKER_PASSWORD: $CI_REGISTRY_PASSWORD
DOCKER_REGISTRY: $CI_REGISTRY
DOCKER_IMAGE_URI: "${DOCKER_REGISTRY}/${CI_PROJECT_PATH}/${CI_COMMIT_REF_SLUG}"
services:
- name: docker:20.10.16-dind
entrypoint: ["dockerd-entrypoint.sh", "--tls=false"]
before_script:
- export DOCKER_IMAGE_TAG=${CI_COMMIT_TAG:="${CI_COMMIT_SHORT_SHA}"}
script:
- docker login -u $DOCKER_USER -p $DOCKER_PASSWORD $DOCKER_REGISTRY
- |
docker build -t "${DOCKER_IMAGE_URI}:${DOCKER_IMAGE_TAG}" -f "${DOCKER_BUILD_DIR}/${DOCKER_BUILD_FILE}" ${DOCKER_BUILD_DIR}
docker push "${DOCKER_IMAGE_URI}:${DOCKER_IMAGE_TAG}"
Since we store the output of the code compiled at the build stage as artifacts, defining the base image in the Dockerfile and copying these artifacts to the right place will be sufficient.
FROM nginx:alpine
COPY /build/* /usr/share/nginx/html/
When the image stage is completed, a ready-to-use docker image should be created.
Before moving on to the deployment stage, we need to see how this structure is created on ArgoCD and how the relevant ArgoCD repository is designed, in order to better understand the job specific definitions we will make in this stage.
In our next article, we will explain how to create our Kubernetes YAML files for our “React Application”, which will be used in the deployment stage. Thanks for followings us.