Cloud Native 15 min read

GitOps Workflow with Argo CD and GitLab CI on Kubernetes

This article demonstrates a complete GitOps workflow on Kubernetes using Argo CD and GitLab CI, covering installation, configuration of Argo CD, GitLab project setup, Helm deployment, and a multi‑stage CI/CD pipeline that automatically builds, publishes Docker images, and synchronizes applications in dev and prod environments.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
GitOps Workflow with Argo CD and GitLab CI on Kubernetes

In the current cloud‑native landscape, GitOps is increasingly adopted as a continuous delivery model. This guide provides a hands‑on example that combines Argo CD and GitLab CI to implement a full GitOps workflow.

Introduction

The workflow consists of two core components: Argo CD , a declarative GitOps continuous delivery tool for Kubernetes, and GitLab CI , a lightweight CI/CD system tightly integrated with GitLab.

Argo CD Installation

Prerequisite: a reachable Kubernetes cluster. An ingress-nginx controller is installed to expose the Argo CD dashboard. The following commands install Argo CD via Helm:

$ kubectl create ns argocd
$ helm repo add argo https://argoproj.github.io/argo-helm
$ helm install argocd -n argocd argo/argo-cd --values values.yaml

The values.yaml used for the Helm install configures an Ingress with TLS termination:

server:
  ingress:
    enabled: true
    annotations:
      kubernetes.io/ingress.class: "nginx"
      nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
      nginx.ingress.kubernetes.io/ssl-passthrough: "true"
      nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    hosts:
    - argocd.k8s.local

After installation, add an entry for argocd.k8s.local to /etc/hosts pointing to the Ingress node. The default admin password is the name of the Argo CD server pod, obtained with:

$ kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2

GitLab Project Configuration

A simple Go web application (source: github.com/cnych/gitops-webapp-demo ) is used as the example. The project is hosted on a self‑installed GitLab instance reachable at git.k8s.local . Required CI/CD variables are added in GitLab Settings → CI/CD → Variables:

CI_REGISTRY – Docker registry URL (https://index.docker.io/v1/)

CI_REGISTRY_IMAGE – Image name (cnych/gitops-webapp)

CI_REGISTRY_USER – Docker Hub username (cnych)

CI_REGISTRY_PASSWORD – Docker Hub password

CI_PASSWORD – Git repository access password

CI_USERNAME – Git repository access username

Argo CD Configuration

Two Application CRDs are defined – one for the development environment and one for production. They point to the Git repository and the respective deployment/dev or deployment/prod directories.

# gitops-demo-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: http://git.k8s.local/course/gitops-webapp.git
    targetRevision: HEAD
    path: deployment/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    automated:
      prune: true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app-prod
  namespace: argocd
spec:
  project: default
  source:
    repoURL: http://git.k8s.local/course/gitops-webapp.git
    targetRevision: HEAD
    path: deployment/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: prod
  syncPolicy:
    automated:
      prune: true

Applying the manifest creates the two applications:

$ kubectl apply -f gitops-demo-app.yaml
application.argoproj.io/web-app-dev created
application.argoproj.io/web-app-prod created

GitLab CI Pipeline

The pipeline is defined in .gitlab-ci.yml with four stages: build , publish , deploy-dev , and deploy-prod .

stages:
  - build
  - publish
  - deploy-dev
  - deploy-prod

Build stage compiles the Go binary inside a golang:1.13.1 image and stores the artifact.

build:
  stage: build
  image:
    name: golang:1.13.1
  script:
    - go build -o main main.go
  artifacts:
    paths:
      - main
  variables:
    CGO_ENABLED: 0

Publish stage uses a Kaniko executor to build a Docker image and push it to the registry. It runs only on the master branch.

publish:
  stage: publish
  image:
    name: cnych/kaniko-executor:v0.22.0
    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

Deploy‑dev stage updates the deployment/dev kustomization with the new image tag, commits the change, and pushes it back to GitLab.

deploy-dev:
  stage: deploy-dev
  image: cnych/kustomize:v1.0
  before_script:
    - git remote set-url origin http://$CI_USERNAME:[email protected]/course/gitops-webapp.git
    - git config --global user.email "[email protected]"
    - 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_SHORT_SHA
    - git commit -am '[skip ci] DEV image update'
    - git push origin master
  only:
    - master

Deploy‑prod stage is similar but marked when: manual so production updates require a manual trigger.

deploy-prod:
  stage: deploy-prod
  image: cnych/kustomize:v1.0
  before_script:
    - git remote set-url origin http://$CI_USERNAME:[email protected]/course/gitops-webapp.git
    - git config --global user.email "[email protected]"
    - 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_SHORT_SHA
    - git commit -am '[skip ci] PROD image update'
    - git push origin master
  only:
    - master
  when: manual

Usage

Both dev and prod applications are exposed via Ingresses at http://webapp.dev.k8s.local/ and http://webapp.prod.k8s.local/ . Adding these hostnames to /etc/hosts allows local testing. After modifying the Go source (e.g., changing the welcome message) and pushing to master , the pipeline builds a new image, updates the kustomization files, and Argo CD automatically syncs the changes, resulting in the updated message being served.

Production deployments are triggered manually from the GitLab pipeline UI, after which Argo CD synchronizes the prod environment.

References

https://www.weave.works/technologies/gitops/

https://argoproj.github.io/argo-cd/

https://docs.gitlab.com/ee/ci/yaml/

https://medium.com/@andrew.kaczynski/gitops-in-kubernetes-argo-cd-and-gitlab-ci-cd-5828c8eb34d6

https://github.com/cnych/gitops-webapp-demo

CI/CDKubernetesGitLab CIGitOpsHelmArgo CD
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.