Cloud Native 14 min read

GitOps Workflow Example with Argo CD, GitLab CI, and Kubernetes

This article provides a step‑by‑step guide to implementing a GitOps workflow on a Kubernetes cluster using Argo CD and GitLab CI, covering Argo CD installation, Helm deployment, application configuration, CI/CD pipeline definition, and deployment to dev and prod environments.

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

GitOps is a declarative continuous‑delivery approach that stores the desired state of a Kubernetes cluster in a Git repository. This guide demonstrates a complete GitOps workflow using Argo CD as the delivery engine and GitLab CI for building and publishing container images.

Argo CD installation : After ensuring a reachable Kubernetes cluster, install the ingress-nginx controller, then add the Argo CD Helm chart and install it with a custom values.yaml that enables an NGINX ingress.

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
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 the Helm release is ready, verify the pods are running and retrieve the initial admin password:

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

GitLab project configuration : Create a GitLab repository for a simple Go web application, then define CI/CD variables (e.g., CI_REGISTRY , CI_REGISTRY_IMAGE , CI_REGISTRY_USER , CI_REGISTRY_PASSWORD , CI_USERNAME , CI_PASSWORD ) in the project settings.

Argo CD application manifests : Two Application CRDs are created—one for the development namespace 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

Apply the manifests:

kubectl apply -f gitops-demo-app.yaml

GitLab CI pipeline : The .gitlab-ci.yml defines four stages— build , publish , deploy-dev , and deploy-prod . The build stage compiles the Go binary, the publish stage builds a Docker image with Kaniko and pushes it to the registry, and the deploy stages update the Kustomize manifests and push the changes back to Git.

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

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: 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: 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: 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

When a developer pushes changes to the master branch, the pipeline builds a new image, updates the Kustomize manifests, and Argo CD automatically syncs the changes to the dev namespace. A manual trigger can promote the same image to the prod namespace.

Both environments are exposed via Ingress; adding the hostnames to /etc/hosts allows local browsers to reach http://webapp.dev.k8s.local/ and http://webapp.prod.k8s.local/ . Updating the source code (e.g., changing the welcome message) and committing triggers the pipeline, resulting in a live update of the running application.

References: Weaveworks GitOps overview, Argo CD documentation, GitLab CI/CD YAML reference, a medium article on GitOps with Argo CD, and the demo repository 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.