Integrating Kustomize with ArgoCD for GitOps

Automating deployments across multiple environments with ApplicationSet

Featured image

Image Reference Link



Overview

In my previous post, I covered how to efficiently manage Kubernetes resources using Kustomize. In this article, I’ll demonstrate how to integrate Kustomize with ArgoCD to establish an automated GitOps-based deployment environment.

ArgoCD is a declarative continuous delivery tool that automatically synchronizes the state of your Kubernetes cluster with manifest definitions stored in Git repositories. By combining it with Kustomize, you can easily manage environment-specific YAML overlays while automating deployments through GitOps practices.

In this tutorial, we’ll cover how to use the ApplicationSet resource to automatically create and deploy multiple applications across dev, qa, and production environments, providing a comprehensive understanding of the core concepts behind GitOps strategies for maintaining environment-specific resources.


Kustomize + ArgoCD ApplicationSet

For this practical exercise, we’ll be using the argocd-applicationset-nginx-deployment example from the following GitHub repository: https://github.com/somaz94/kustomize-study


Directory Structure

The project has the following structure:

.
├── README.md
├── app
│   └── nginx
│       ├── base
│       │   ├── kustomization.yaml
│       │   ├── nginx-deployment.yaml
│       │   ├── nginx-ingress.yaml
│       │   └── nginx-service.yaml
│       └── overlays
│           ├── dev
│           │   ├── ingress.yaml
│           │   ├── kustomization.yaml
│           │   └── replica-count.yaml
│           ├── prod
│           │   ├── ingress.yaml
│           │   ├── kustomization.yaml
│           │   └── replica-count.yaml
│           └── qa
│               ├── ingress.yaml
│               ├── kustomization.yaml
│               └── replica-count.yaml
├── appsets
│   └── nginx-applicationset.yaml
└── github-secret
    └── github-secret.yaml


Deployment Workflow

The deployment workflow consists of three main steps:

  1. Creating GitHub Repository Secret and Project in ArgoCD
  2. Writing and uploading templates
  3. Deploying the ApplicationSet to ArgoCD


1. Creating GitHub Repository Secret and Project in ArgoCD

First, we need to create a secret for accessing the GitHub repository:

# github-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: github-secret
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  type: git
  url: git@github.com:somaz94/kustomize-study.git
  sshPrivateKey: |
    # sshPrivateKey Example
    -----BEGIN OPENSSH PRIVATE KEY-----           
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
    ...
    0zbXo5J4Yb6oMAAAAUZ2VuaXVzNTcxMUBnbWFpbC5jb20BAgMEBQYH
    -----END OPENSSH PRIVATE KEY-----

Deploy the secret:

kubectl apply -f github-secret.yaml -n argocd

After deploying the secret, verify that the GitHub repository is registered in ArgoCD and create a new project.


3. Deploying the ApplicationSet to ArgoCD

Now, let’s deploy the ApplicationSet to ArgoCD:

# nginx-applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: nginx-appicationset
spec:
  generators:
    - matrix:
        generators:
          - list:
              elements:
                - cluster: master-node.somaz.link
                  url: https:// # ClusterIP or URI or Domain
                  overlay: dev # This new field specifies the overlay to use
                  values:
                    environment: dev
                    project: nginx
                - cluster: master-node.somaz.link
                  url: https:// 
                  overlay: qa
                  values:
                    environment: qa
                    project: nginx
                - cluster: master-node.somaz.link
                  url: https:// 
                  overlay: prod
                  values:
                    environment: prod
                    project: nginx
          - git:
              repoURL: git@github.com:somaz94/kustomize-study.git
              revision: HEAD
              directories:
                - path: argocd-applicationset-nginx-deployment/app/nginx/overlays/ # Use the new overlay field
  template:
    metadata:
      name: '-'
    spec:
      project: ''
      source:
        repoURL: git@github.com:somaz94/kustomize-study.git
        targetRevision: HEAD
        path: ''
        kustomize:
          namePrefix: '-'
      destination:
        server: ''
        namespace: '-'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true
          - ApplyOutOfSyncOnly=true

Deploy the ApplicationSet:

kubectl apply -f nginx-applicationset.yaml -n argocd


Verifying the Deployment

After deploying the ApplicationSet, you can verify that the resources have been created in each environment:

k get po,svc,ingress -n dev-nginx
NAME                                       READY   STATUS    RESTARTS   AGE
pod/dev-nginx-deployment-8d545c96d-2w7sw   1/1     Running   0          88s

NAME                        TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/dev-nginx-service   ClusterIP   10.233.21.0   <none>        80/TCP    88s

NAME                                          CLASS    HOSTS                        ADDRESS        PORTS   AGE
ingress.networking.k8s.io/dev-nginx-ingress   <none>   dev-nginx.fgn.nerdystar.io   10.10.100.22   80      88s

k get po,svc,ingress -n qa-nginx
NAME                                      READY   STATUS    RESTARTS   AGE
pod/qa-nginx-deployment-8d545c96d-4bd8t   1/1     Running   0          93s
pod/qa-nginx-deployment-8d545c96d-mxchm   1/1     Running   0          93s

NAME                       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/qa-nginx-service   ClusterIP   10.233.62.87   <none>        80/TCP    94s

NAME                                         CLASS    HOSTS                       ADDRESS        PORTS   AGE
ingress.networking.k8s.io/qa-nginx-ingress   <none>   qa-nginx.fgn.nerdystar.io   10.10.100.22   80      93s

k get po,svc,ingress -n prod-nginx
NAME                                        READY   STATUS    RESTARTS   AGE
pod/prod-nginx-deployment-8d545c96d-45z4t   1/1     Running   0          96s
pod/prod-nginx-deployment-8d545c96d-jspm5   1/1     Running   0          96s
pod/prod-nginx-deployment-8d545c96d-qf97w   1/1     Running   0          96s

NAME                         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/prod-nginx-service   ClusterIP   10.233.17.172   <none>        80/TCP    97s

NAME                                           CLASS    HOSTS                         ADDRESS        PORTS   AGE
ingress.networking.k8s.io/prod-nginx-ingress   <none>   prod-nginx.fgn.nerdystar.io   10.10.100.22   80      96s


Advanced Use Cases

Wrapping Helm Charts with Kustomize

In real-world scenarios, it’s often difficult to use Helm Charts as-is. One effective approach is to wrap Helm Charts with Kustomize to add environment-specific overlays.

For example, you can include Helm-generated resources in the base directory and override the values.yaml in overlays/dev:

# base/kustomization.yaml
helmCharts:
  - name: nginx
    repo: https://charts.bitnami.com/bitnami
    version: 13.2.18
    releaseName: nginx
    namespace: default
    valuesFile: values.yaml

The environment-specific values.yaml files are then managed separately in the overlay directories.


CI/CD Integration Example

The core of GitOps in a Kustomize + ArgoCD structure is the “Git push → automatic deployment” flow.

Here’s an example using GitHub Actions to trigger ArgoCD CLI app sync when a commit occurs on the main branch:

name: ArgoCD Sync Trigger

on:
  push:
    branches:
      - main

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Sync ArgoCD App
        run: |
          argocd login $ARGOCD_SERVER --username admin --password $ARGOCD_PASSWORD --insecure
          argocd app sync nginx-dev
        env:
          ARGOCD_SERVER: argocd.example.com
          ARGOCD_PASSWORD: $

With this setup, developers can experience the complete GitOps flow where deployment is automated simply by committing to Git.


Fine-Tuning Sync Policies

The syncPolicy section offers various options beyond the automated prune and selfHeal settings. These options provide more granular control over the synchronization strategy:

syncPolicy:
  automated:
    prune: true
    selfHeal: true
  syncOptions:
    - CreateNamespace=true                # Automatically create namespaces
    - ApplyOutOfSyncOnly=true            # Apply only resources that have changed
    - RespectIgnoreDifferences=true      # Respect ignoreDifferences settings
    - Validate=false                     # Skip resource validation
    - PrunePropagationPolicy=foreground  # Configure deletion strategy

By properly combining these options, you can design a flexible GitOps pipeline that aligns with your team’s deployment strategy.


Operational Optimization Tips

ArgoCD Ignore Differences Setting

Some resources might always be marked as OutOfSync by ArgoCD due to fields that change automatically during operation, such as metadata.annotations in Pod templates.

In such cases, you can use the ignoreDifferences option to ignore these changes:

spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/template/metadata/annotations
    - group: ""
      kind: Service
      jsonPointers:
        - /spec/clusterIP

This prevents unnecessary Sync indicators or unintended resource updates.


Conclusion: The First Step in GitOps Automation

By integrating ArgoCD with Kustomize, you can implement a GitOps strategy that goes beyond manual deployments, where “environment-specific resources declared in Git → automatic synchronization” becomes the norm.

The ApplicationSet resource is particularly useful for automatically creating numerous applications according to patterns, making it highly advantageous for large microservice architectures or multi-environment operations.

Additionally, Kustomize’s base/overlay structure reduces manifest duplication and improves maintainability. By configuring your CI pipeline to have ArgoCD automatically reflect the results built through Kustomize, you can evolve into a true GitOps workflow.

While this tutorial focused on nginx deployment, in actual project environments, this serves as a core automation foundation that enables efficient operation of dozens to hundreds of resources.



References