Skip to main content

Installing ArgoCD on Kubernetes Cluster with Terraform

· 11 min read
Abdulmalik
AppSec Engineer

There are many tools for handling complex architecture of deploying changes of your applications from the build stage to your cluster, most times the term and process of archiving this is called GitOps only if GitHub is being used as the single source of truth in the scenario.

And out of many of these tools, ArgoCD is one of the best that you can use, it's also open source, and that's what I am writing about here.

Prerequisites:

  • A Running Kubernetes Cluster provisioned using terraform
  • Kubectl is installed and your kubeconfig file is set too(~/.kube/config).
  • Your deployments yaml file in your GitHub repository, you can git clone mine, gitops-argocd

Install Argo CD to your cluster

Using the following terraform code, you can deploy argocd into your existing kubernetes cluster, but before you apply this code using terraform apply --auto-approve, you need to create a folder named argocd and save the below configurations as install.yaml in the argocd folder you just created.

The below argocd configurations is not good for production, because this isn't implementation for HA(High Availability), you can check argocd HA docs to set production-ready config values.

install.yaml
# redis-ha:
# enabled: true

controller:
replicas: 1

server:
replicas: 1
service:
type: LoadBalancer

repoServer:
replicas: 1

applicationSet:
replicaCount: 1
# replicaCount: 2
argocd.tf
resource "kubernetes_namespace" "argocd" {
metadata {
name = "argocd"
}
}

resource "helm_release" "argocd" {
name = "argocd"
chart = "argo-cd"
repository = "https://argoproj.github.io/argo-helm"
version = "6.7.11"
timeout = "1500"
namespace = kubernetes_namespace.argocd.id
values = [data.template_file.argo-values.rendered]
}

resource "null_resource" "password" {
provisioner "local-exec" {
working_dir = "./argocd"
command = "kubectl -n argocd-staging get secret argocd-initial-admin-secret -o jsonpath={.data.password} | base64 -d > argocd-login.txt"
}
}

resource "null_resource" "del-argo-pass" {
depends_on = [null_resource.password]
provisioner "local-exec" {
command = "kubectl -n argocd-staging delete secret argocd-initial-admin-secret"
}
}

}

Here is the terraform code for the variable used, as well as the provider used here, copy and paste this into your variable.tf file and provider.tf file

variable.tf
variable "eks-name" {
type = string
default = "my-cluster"
}

variable "env" {
default = "staging"
}

provider.tf

terraform {
required_version = ">= 1.0"
required_providers {
kubectl = {
source = "alekc/kubectl"
version = "2.0.4"
}
}

Once you have applied the above terraform codes, Argocd will be deployed in your argocd namespace and load balancer to access argocd server via UI, will be generated too, you can find it by running kubectl get svc -n argocd

Also, the generated password to access your argocd server will be stored in the argocd-login.txt file for you, and you will notice the del-argo-pass null_resource in the terraform code.

What that section of the code does is that, after you have gotten the come-along argocd server password, it's security-wise to delete the secret from our cluster, it's mentioned in the argocd docs too.

Argo CD Application

And here is the Application yaml file, the following yaml will help you create an application on your argocd server.

create application.yaml file and copy the below yaml codes there and apply using kubectl apply -f ./argocd/application.yaml and you are good to go.

./argocd/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argo-test-app
namespace: argocd-staging
spec:
project: default
source:
repoURL: [email protected]:saintmalik/gitops-argocd.git
targetRevision: HEAD
path: dev
directory:
recurse: true
destination:
server: https://kubernetes.default.svc
namespaces: default
syncPolicy:
syncOptions:
- CreateNamespace=true
automated:
prune: true
selfHeal: true

What the Application fields mean:

  • source.repoURL: the Git repository holding your deployment manifests.
  • source.targetRevision: HEAD: track the latest commit.
  • source.path: dev: the folder inside the repo for this environment.
  • destination.server: https://kubernetes.default.svc: the target cluster. For the local cluster this is the default.
  • destination.namespace: default: the namespace to deploy into.
  • syncPolicy.automated.prune: true: ArgoCD deletes resources that are removed from Git.
  • syncPolicy.automated.selfHeal: true: ArgoCD reverts drift caused by manual changes.
  • syncPolicy.syncOptions.CreateNamespace=true: creates the destination namespace if it does not exist.

If everything goes well, your application will sync and deploy in minutes. If the repo is private, you will see an error like this instead.

ComparisonError
rpc error: code = Unknown desc = error creating SSH agent: "SSH agent requested but SSH_AUTH_SOCK not-specified"
Okay, thats all

So let's fix this.

Connect Argocd with private repo manually or declaratively

👉 Step 1 - Generate ssh keypairs

You will need to generate a passwordless SSH key pair, you can use either -P "", or using -N '' by, leaving the strings empty it will create our keypairs without the need for CLI interaction.

ssh-keygen -t ed25519 -C blog.saintmalik.me -N '' -f argo
Okay, thats all

This will generate two files for you, argo which holds the private ssh key and argo.pub which holds the public ssh key

👉 Step 2 - Add the ssh public key to your repository

Now go to your GitHub repository settings and navigate to the Deploy Keys and click Add deploy key to add the public SSH key you generated earlier, it's inside the argo.pub file.

Okay, thats all Okay, thats all

👉 Step 3 - Configure and connect your private repo

Now that you have added the private key to your repository, it's time for us to add the public key to your argocd server, so click the settings at the sidebar and hit the CONNECT REPO button and it should bring a screen just like the below image.

Okay, thats all

This is where you will add the private SSH key from the argo file you generated earlier, the repository URL should be added too in the format same as the one you are seeing in the screenshot and the Project selection should be default.

When you are done with the configuration, click on the CONNECT button and you should see a success message just like this.

Okay, thats all

If this shows success, then your deployment argo app should be synced and healthy already and it should look just like this

synced repo for app

When you click on the app, you should see more details about your deployment just like this

Okay, thats all
note

Only YAML files available in the dev folder of our repository will be deployed and luckily, I only have one, and that is the single deployment that you are seeing in the above screenshot

👉 Step 4 - Testing to see if everything works

So to confirm if all you have done works well, you can now alter the image in our deployment yaml file from our GitHub repository, so I will edit the yaml file from Github now

Okay, thats all

So I have changed the nginx image tag from nginx:1.14.2 to nginx:latest, so once I commit the changes, you can see it getting deployed real-time.

And here it is, it got deployed automatically, you see it created another replica and its deploying the new update and has terminated the existing pod created.

update deployment file

Deployment Comparison

first image
Okay, thats all
second image
Okay, thats all

Bonus step

You can also automate both steps 2 and 3 using Terraform, using terraform you can add the public key to your repository and also add the private key to your argocd server.

the catch here is that you need to add the GitHub provider to your Terraform code which also requires you to create a GitHub token.

provider.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
github = {
source = "integrations/github"
version = "~> 5.0"
}
}
}

provider "github" {
token = "your-github-token"
owner = "your-github-username"
}
argocd.tf
resource "github_repository_deploy_key" "argocd_repo_deploykey" {
title = "argocd-connect"
repository = "gitops"
key = "REPLACE THIS WITH THE PUBLIC KEY YOU GENERATED IN STEP 1"
read_only = false
}
argocd.tf
resource "kubernetes_secret_v1" "ssh_key" {
metadata {
name = "private-repo-ssh-key"
namespace = kubernetes_namespace.argocd.id
labels = {
"argocd.argoproj.io/secret-type" = "repository"
}
}

type = "Opaque"

data = {
"sshPrivateKey" = "REPLACE THIS WITH THE SSH PRIVATE KEY YOU GENERATED IN STEP 1"
"type" = "git"
"url" = "[email protected]:saintmalik/gitops-argocd.git"
"name" = "github"
"project" = "default"
}
}

Additionally, for faster syncing of your argocd deployment quicker when you make a new commit, you can add a webhook to your repository to trigger argocd to sync your application when there is a new commit.

This will help you avoid the 3-minute wait time for argocd to sync your application.

argocd.tf
data "kubernetes_service" "argocd_server" {
metadata {
name = "argocd-server"
namespace = "argocd"
}
}

resource "github_repository_webhook" "argocd" {
repository = "gitops"
configuration {
url = "https://${data.kubernetes_service.argocd_server.status.0.load_balancer.0.ingress.0.hostname}"
content_type = "json"
secret = "skrrskrrrii" //the secrets to avoid ddos if argo link is exposed, its just a random texts
insecure_ssl = true
}

active = true

events = ["push"]
}

Completion criterion

ArgoCD is fully set up when:

  1. The ArgoCD server is reachable via its load balancer or port-forward.
  2. You can log in with the initial admin password.
  3. An ArgoCD Application exists and is synced to a Git repository.
  4. If the repository is private, the SSH deploy key is added to the repo and the matching private key is stored as a Secret in the argocd namespace.
  5. Changing a manifest in Git and pushing it triggers a new sync (via webhook or the 3-minute poll).

If you run into issues, see the ArgoCD issues reference.

Till next time 🤞🏽

References


Comments