In the ever-evolving landscape of cloud infrastructure, the demand for streamlined and automated Kubernetes cluster provisioning has become paramount. Terraform, a versatile Infrastructure as Code (IaC) tool, coupled with ArgoCD, a declarative GitOps continuous delivery tool for Kubernetes, provides a powerful synergy to simplify the deployment and management of Kubernetes clusters.
Link to Github code repo here
Prerequisites:
- AWS Account: You must have an active AWS account. If you don't have one, you can sign up for an AWS account on the AWS website. You can create it here
- IAM User or Role: Create an IAM (Identity and Access Management) user or role in your AWS account with the necessary permissions to create and manage EKS clusters. At a minimum, the user or role should have permissions to create EKS clusters, EC2 instances, VPCs, and related resources.
- AWS CLI: Install and configure the AWS Command Line Interface (CLI) on your local machine. You'll use the AWS CLI to interact with your AWS account and configure your AWS credentials. You can download it here
- Terraform Installed: Install Terraform on your local machine. You can download Terraform from the official Terraform website and follow the installation instructions for your operating system here
Terraform
We would use a sample Terraform EKS code, that we discussed in our previous article, you can find more detailed description here
Since we would use helm to install ArgoCD application to created cluster in terraform let's update our provider.tf
file, we would add the following:
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.16.1"
}
helm = {
source = "hashicorp/helm"
version = ">= 2.8.0"
}
}
}
- This block declares the required Terraform providers and their versions. It specifies that the kubernetes provider should be at least version 2.16.1, and the helm provider should be at least version 2.8.0.
locals {
eks_endpoint = module.eks.cluster_endpoint
eks_ca_certificate = module.eks.cluster_certificate_authority_data
}
- The locals block defines local variables for the EKS cluster's endpoint (eks_endpoint) and the cluster's CA certificate (eks_ca_certificate). These variables are sourced from the outputs of an EKS module
provider "kubernetes" {
host = local.eks_endpoint
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["eks", "get-token", "--cluster-name", local.cluster_name]
command = "aws"
}
cluster_ca_certificate = base64decode(
local.eks_ca_certificate
)
}
- This block configures the kubernetes provider, specifying the EKS cluster's endpoint as the host. It employs the aws CLI to authenticate, obtaining the required credentials using the eks get-token command. The cluster's CA certificate is base64-decoded for secure communication.
provider "helm" {
kubernetes {
host = local.eks_endpoint
cluster_ca_certificate = base64decode(
local.eks_ca_certificate
)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["eks", "get-token", "--cluster-name", local.cluster_name]
command = "aws"
}
}
}
- The helm provider block configures Helm to interact with the EKS cluster. It specifies the EKS cluster's endpoint and CA certificate, similar to the Kubernetes provider. The aws CLI is used for authentication, obtaining the required credentials with the eks get-token command
Argocd.tf
To make things clear we would create a separate file for ArgoCD install - argocd.tf
:
resource "helm_release" "argocd" {
count = var.enable_argocd_helm_release ? 1 : 0
name = var.argocd_helm_release_name
namespace = var.argocd_k8s_namespace
repository = var.argocd_helm_repo
chart = var.argocd_helm_chart
version = var.argocd_helm_chart_version
timeout = var.argocd_helm_chart_timeout_seconds
create_namespace = true
}
Count
attribute is conditional, creating the ArgoCD Helm release only if the variable enable_argocd_helm_release is set to trueName
andNamespace
provides a customizable way to name the ArgoCD installation and define the Kubernetes namespacerepository
,chart
andversion
- defines the Helm chart details, including the repository URL, the chart name, and the desired version to deploy.timeout
- specifies the maximum time (in seconds) Terraform waits for the Helm chart to be deployed before timing out. It allows customization of the deployment timeout duration.create_namespace
- Indicates that Terraform should create the Kubernetes namespace specified invar.argocd_k8s_namespace
if it does not already exist. This ensures the designated namespace is available for the ArgoCD deployment.
Variables.tf
Since we using variables let's define them in variables.tf
:
variable "enable_argocd_helm_release" {
type = bool
default = true
description = "Enable/disable ArgoCD Helm chart deployment on DOKS"
}
variable "argocd_helm_repo" {
type = string
default = "https://argoproj.github.io/argo-helm"
description = "ArgoCD Helm chart repository URL"
}
variable "argocd_helm_chart" {
type = string
default = "argo-cd"
description = "argocd Helm chart name"
}
variable "argocd_helm_release_name" {
type = string
default = "argocd"
description = "argocd Helm release name"
}
variable "argocd_helm_chart_version" {
type = string
default = "5.16.14"
description = "ArgoCD Helm chart version to deploy"
}
variable "argocd_helm_chart_timeout_seconds" {
type = number
default = 300
description = "Timeout value for Helm chart install/upgrade operations"
}
variable "argocd_k8s_namespace" {
type = string
default = "argocd"
description = "Kubernetes namespace to use for the argocd Helm release"
}
Deployment
In order to initialize terraform and download modules run:
`terraform init`
You can also check which resources terraform is planning to create by running:
terraform plan
To provision resources run:
terraform apply
Testing
After Terraform applied you should see the following output:
Apply complete! Resources: 61 added, 0 changed, 0 destroyed.
Outputs:
connect_to_eks = "aws eks --region <YOUR_REGION> update-kubeconfig --name <CLUSTER_NAME> --profile default"
endpoint = "<CLUSTER_ENDPOINT>"
Execute command from connect_to_eks
output in order to generate kubeconfig file:
aws eks --region <YOUR_REGION> update-kubeconfig --name <CLUSTER_NAME> --profile default
Verify conectivity to the cluster with kubectl:
kubectl get no
You should see list of nodes:
NAME STATUS ROLES AGE VERSION
ip-10-0-1-9.ec2.internal Ready <none> 33m v1.27.7-eks-e71965b
ip-10-0-3-76.ec2.internal Ready <none> 34m v1.27.7-eks-e71965b
Now we can check if ArgoCD is up and running:
kubectl get po --namespace argocd
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 35m
argocd-applicationset-controller-7558f88dd6-rkmpj 1/1 Running 0 35m
argocd-dex-server-84bd778f5b-lqzz2 1/1 Running 2 (34m ago) 35m
argocd-notifications-controller-7bf8475f6b-w94k8 1/1 Running 0 35m
argocd-redis-5969b95845-pjc8c 1/1 Running 0 35m
argocd-repo-server-65d8dd55fb-g8q65 1/1 Running 0 35m
argocd-server-5b767bb4f-mqhbv 1/1 Running 0 35m
Now we can get ArgoCD admin password, run:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
Save outputed password, and use it to login to your app after running following command:
kubectl -n argocd port-forward svc/argocd-server 8080:80
Open your browser and navigate to localhost:8080
since we didn't install any cert you will see warning, click on proceed, use password outputed in a previous command and admin
username
Now let's deploy test application to see if it working corectly
After you logged in do the following:
- Click on
New App
- Type in fields :
Application Name
,Project Name
you can leaveSYNC POLICY
as Manual - In a SOURCE block you can add Github repo with a testapp
https://github.com/cloudtipss/ArgoCD-EKS-Terraform.git
and add pathtestapp
- Destination block -
https://kubernetes.default.svc
it would install the app on the same cluster where Argo is installed, if you would leave namespace field as default your app will be created in a default namespace - Click on Create
- Click on
Sync
andSynchronize
You should see the status of the app as Healthy and Synced. Now lets check if it is app and running:
kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-57d84f57dc-mgzpl 1/1 Running 0 2m27s
As you can see nginx application is up and running!
You can find source code in our Github repo