Click on one of them to see more details about the application and deployed Kubernetes objects. You can find the object inside the deployment/<env> directory in gitops-webapp repository. You can see that inside each folder, I created kustomization.yaml. Argo CD recognized it and apply using Kustomize without any additional settings!

I recommend you take a look at available options. The GUI is very intuitive, so you shouldn’t have any problems with understanding it.

GitLab CI Pipeline

The next step is to create the pipeline, which will automatically build our application, push to the container registry and update Kubernetes manifests in desired environments.

The below example is far from being perfect (missing tests etc.), but it should demonstrate to you the entire GitOps workflow.

Basically, the idea is, that developers are working on their own branches. For each commit and push of their branch, the stage build will be triggered. When they merge their changes with the master branch, the full pipeline will be triggered. It will build the application, containerize it, push an image to the registry and automatically update Kubernetes manifests depending on the stage. In addition, deploying to Prod requires manual action from DevOps.

Pipeline definition in GitLab CI is stored in .gitlab-ci.yml file in the root directory of the project.

At the top of the file are the definition of stages and default environment variables, images or before script steps. Excerpt from my pipeline:

stages:

- build

- publish

- deploy-dev

- deploy-prod

Next is the stage definition and required tasks. The build process here is very simple. It just executes one command inside docker image golang. Then it saves the artifact for the next stages. This step works for any new commit in any branch.

build:

stage: build

image:

name: golang:1.13.1

script:

- go build -o main main.go

artifacts:

paths:

- main

variables:

CGO_ENABLED: 0

The next stage is to build an image and push it to the GitLab registry. Here I’m using Kaniko, because the image is built inside the container which has no access to the docker engine. The image is building with current commit hash (variable $CI_COMMIT_HASH) and pushing to the GitLab registry. This stage is triggered only on changes in the master branch.

publish:

stage: publish

image:

name: gcr.io/kaniko-project/executor:debug

entrypoint: [""]

script:

- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json

- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile ./Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

dependencies:

- build

only:

- master

The next stage is to deploy the application to a dev environment. In GitOps it means, to update the Kubernetes manifests so Argo CD can pull updated version and apply the change. Here I’m using environment variables defined for the project with username and token which are needed to push the changes to the master branch. When GitLab CI receives commit with message [skip ci], the pipeline is not triggered (we don’t want to run it again when we publish changes related to our kustomize manifests)



stage: deploy-dev

image: alpine:3.8

before_script:

- apk add --no-cache git curl bash

- curl -s "

- mv kustomize /usr/local/bin/

- git remote set-url origin https://${CI_USERNAME}:${CI_PUSH_TOKEN}

- git config --global user.email "

- git config --global user.name "GitLab CI/CD"

script:

- git checkout -B master

- cd deployment/dev

- kustomize edit set image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

- cat kustomization.yaml

- git commit -am '[skip ci] DEV image update'

- git push origin master

only:

- master deploy-dev:stage: deploy-devimage: alpine:3.8before_script:- apk add --no-cache git curl bash- curl -s " https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh " | bash- mv kustomize /usr/local/bin/- git remote set-url origin https://${CI_USERNAME}:${CI_PUSH_TOKEN} @gitlab .com/andrew.kaczynski/gitops-webapp.git- git config --global user.email " gitlab@gitlab.com - git config --global user.name "GitLab CI/CD"script:- git checkout -B master- cd deployment/dev- kustomize edit set image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA- cat kustomization.yaml- git commit -am '[skip ci] DEV image update'- git push origin masteronly:- master

Finally, we have a deployment to prod environment. It is very similar to the previous one but requires manual action before it starts.



stage: deploy-prod

image: alpine:3.8

before_script:

- apk add --no-cache git curl bash

- curl -s "

- mv kustomize /usr/local/bin/

- git remote set-url origin https://${CI_USERNAME}:${CI_PUSH_TOKEN}

- git config --global user.email "

- git config --global user.name "GitLab CI/CD"

script:

- git checkout -B master

- git pull origin master

- cd deployment/prod

- kustomize edit set image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

- cat kustomization.yaml

- git commit -am '[skip ci] PROD image update'

- git push origin master

only:

- master

when: manual deploy-prod:stage: deploy-prodimage: alpine:3.8before_script:- apk add --no-cache git curl bash- curl -s " https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh " | bash- mv kustomize /usr/local/bin/- git remote set-url origin https://${CI_USERNAME}:${CI_PUSH_TOKEN} @gitlab .com/andrew.kaczynski/gitops-webapp.git- git config --global user.email " gitlab@gitlab.com - git config --global user.name "GitLab CI/CD"script:- git checkout -B master- git pull origin master- cd deployment/prod- kustomize edit set image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA- cat kustomization.yaml- git commit -am '[skip ci] PROD image update'- git push origin masteronly:- masterwhen: manual

This is the full pipeline definition. I hope you see how easy is to create a pipeline in GitLab when compare to Jenkins :) That’s why I love it!