Production-Grade Kubernetes Cluster Deployment with IaC & CI/CD
This project demonstrates a complete DevOps pipeline for deploying and managing a production-ready AWS EKS cluster using Infrastructure as Code (Terraform) and automated CI/CD workflows (GitHub Actions). The solution includes cluster provisioning, application deployment, monitoring setup, and automated testing.
Complete EKS cluster defined in Terraform with modular, reusable components
Automated deployment workflows with GitHub Actions for infrastructure and applications
IAM roles, RBAC, network policies, and secrets management
Prometheus, Grafana, and CloudWatch integration for comprehensive monitoring
# Install required tools brew install terraform kubectl aws-cli helm # Configure AWS CLI aws configure # Verify installations terraform version kubectl version --client aws sts get-caller-identity
Create a modular Terraform configuration for the EKS cluster:
eks-terraform-pipeline/
├── terraform/
│ ├── environments/
│ │ ├── dev/
│ │ │ ├── main.tf
│ │ │ ├── variables.tf
│ │ │ └── terraform.tfvars
│ │ └── prod/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── terraform.tfvars
│ ├── modules/
│ │ ├── vpc/
│ │ │ ├── main.tf
│ │ │ ├── variables.tf
│ │ │ └── outputs.tf
│ │ ├── eks/
│ │ │ ├── main.tf
│ │ │ ├── variables.tf
│ │ │ ├── outputs.tf
│ │ │ └── irsa.tf
│ │ └── node-groups/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── backend.tf
├── .github/
│ └── workflows/
│ ├── terraform-plan.yml
│ ├── terraform-apply.yml
│ └── app-deploy.yml
├── k8s/
│ ├── namespaces/
│ ├── deployments/
│ └── services/
└── monitoring/
├── prometheus/
└── grafana/
backend.tf - Remote State Configuration:
terraform {
backend "s3" {
bucket = "your-terraform-state-bucket"
key = "eks/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
modules/eks/main.tf - EKS Module:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.0"
cluster_name = var.cluster_name
cluster_version = var.cluster_version
vpc_id = var.vpc_id
subnet_ids = var.private_subnets
eks_managed_node_groups = {
general = {
desired_size = 2
min_size = 1
max_size = 4
instance_types = ["t3.medium"]
labels = {
role = "general"
}
}
}
manage_aws_auth_configmap = true
cluster_addons = {
coredns = {
most_recent = true
}
kube-proxy = {
most_recent = true
}
vpc-cni = {
most_recent = true
}
}
}
.github/workflows/terraform-plan.yml - PR Validation:
name: Terraform Plan
on:
pull_request:
paths:
- 'terraform/**'
env:
TF_VERSION: '1.5.0'
AWS_REGION: 'us-west-2'
jobs:
terraform-plan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Init
run: |
cd terraform/environments/dev
terraform init
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Validate
run: |
cd terraform/environments/dev
terraform validate
- name: Terraform Plan
run: |
cd terraform/environments/dev
terraform plan -out=tfplan
- name: Comment PR
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const plan = fs.readFileSync('terraform/environments/dev/tfplan', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '### Terraform Plan Results\n\n' + plan
});
.github/workflows/terraform-apply.yml - Infrastructure Deployment:
name: Terraform Apply
on:
push:
branches:
- main
paths:
- 'terraform/**'
jobs:
terraform-apply:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init
run: |
cd terraform/environments/prod
terraform init
- name: Terraform Apply
run: |
cd terraform/environments/prod
terraform apply -auto-approve
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --region us-west-2 --name production-eks-cluster
- name: Deploy Monitoring Stack
run: |
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
kubectl create namespace monitoring || true
helm upgrade --install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--set grafana.adminPassword=${{ secrets.GRAFANA_PASSWORD }}
.github/workflows/app-deploy.yml - Deploy Sample Application:
name: Deploy Application
on:
push:
branches:
- main
paths:
- 'app/**'
- 'k8s/**'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: sample-app
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG ./app
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --region us-west-2 --name production-eks-cluster
- name: Deploy to EKS
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
kubectl set image deployment/sample-app \
sample-app=$ECR_REGISTRY/sample-app:$IMAGE_TAG \
-n default
kubectl rollout status deployment/sample-app -n default
Example RBAC Configuration:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: default subjects: - kind: ServiceAccount name: pod-reader namespace: default roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
Validation Commands:
# Check cluster status kubectl get nodes kubectl get pods --all-namespaces # Verify application deployment kubectl get deployments kubectl get services # Check monitoring kubectl get pods -n monitoring # Test application endpoint kubectl port-forward svc/sample-app 8080:80 curl http://localhost:8080 # View Grafana dashboard kubectl port-forward -n monitoring svc/prometheus-grafana 3000:80
The complete project repository includes:
README.md - Comprehensive documentationterraform/ - Infrastructure as Code.github/workflows/ - CI/CD pipelinesk8s/ - Kubernetes manifestsmonitoring/ - Observability configurationsscripts/ - Utility scripts for operationsdocs/ - Architecture diagrams and runbooksThis project showcases proficiency in: