Skip to content

Commit 4ea1310

Browse files
committed
Add Terraform config files to deploy to EKS Cluster
2 parents d070480 + 1d6f606 commit 4ea1310

7 files changed

Lines changed: 268 additions & 11 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
steps:
1212
- name: Checkout code
1313
uses: actions/checkout@v4
14-
14+
1515
- name: Log in to secrets
1616
uses: docker/login-action@v3
1717
with:
@@ -36,36 +36,68 @@ jobs:
3636
${{ secrets.DOCKERHUB_USERNAME }}/server:${{ github.run_number }}
3737
${{ secrets.DOCKERHUB_USERNAME }}/server:latest
3838
39-
deploy:
39+
provision:
4040
runs-on: ubuntu-latest
4141
needs: build-and-push
42+
if: contains(github.event.commits[0].modified, 'infra/')
43+
44+
steps:
45+
- name: Checkout code
46+
uses: actions/checkout@v4
47+
48+
- name: Configure AWS Credentials
49+
uses: aws-actions/configure-aws-credentials@v2
50+
with:
51+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
52+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
53+
aws-region: ${{ secrets.AWS_REGION }}
54+
55+
- name: Setup Terraform
56+
uses: hashicorp/setup-terraform@v2
57+
58+
- name: Terraform Init
59+
run: terraform -chdir=infra init
60+
61+
- name: Terraform Plan
62+
run: terraform -chdir=infra plan
63+
64+
- name: Terraform Apply
65+
run: terraform -chdir=infra apply -auto-approve
66+
deploy:
67+
runs-on: ubuntu-latest
68+
needs: provision
4269

4370
steps:
4471
- name: Checkout code
4572
uses: actions/checkout@v4
4673

74+
- name: Configure AWS Credentials
75+
uses: aws-actions/configure-aws-credentials@v2
76+
with:
77+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
78+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
79+
aws-region: ${{ secrets.AWS_REGION }}
80+
4781
- name: Set up kubectl
4882
uses: azure/setup-kubectl@v3
4983

5084
- name: Configure kubeconfig
5185
run: |
52-
mkdir -p ~/.kube
53-
echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config
54-
chmod 600 ~/.kube/config
86+
aws eks update-kubeconfig --region ${{ secrets.AWS_REGION }} --name ${{ secrets.EKS_CLUSTER_NAME }}
5587
5688
- name: Update image tags
5789
run: |
5890
sed -i "s|image: ${{ secrets.DOCKERHUB_USERNAME }}/server:.*|image: ${{ secrets.DOCKERHUB_USERNAME }}/server:${{ github.run_number }}|" k8s/backend/deployment.yaml
5991
sed -i "s|image: ${{ secrets.DOCKERHUB_USERNAME }}/client:.*|image: ${{ secrets.DOCKERHUB_USERNAME }}/client:${{ github.run_number }}|" k8s/frontend/deployment.yaml
6092
61-
- name: Deploy to k3s
93+
- name: Deploy to EKS Cluster
6294
run: |
6395
kubectl apply -f k8s/postgres/secret.yaml
6496
kubectl apply -f k8s/postgres/deployment.yaml
6597
kubectl apply -f k8s/backend/deployment.yaml
6698
kubectl apply -f k8s/frontend/deployment.yaml
67-
kubectl rollout status deployment/backend --timeout=500s
68-
kubectl rollout status deployment/frontend --timeout=500s
99+
kubectl rollout status deployment/backend --timeout=300s
100+
kubectl rollout status deployment/frontend --timeout=300s
69101
70102
- name: Verify deployment
71103
run: |

.gitignore

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,22 @@
22
server/jspm_packages/
33
server/web_modules/
44

5-
.env
5+
.env
6+
7+
# local .terraform directory
8+
infra/.terraform/*
9+
infra/bootstrap/.terraform/*
10+
11+
# tf state files
12+
infra/*.tfstate
13+
infra/bootstrap/*.tfstate.*
14+
infra/bootstrap/*.tfstate
15+
infra/bootstrap/*.tfstate.*
16+
17+
# tf variable files
18+
infra/*.tfvars
19+
infra/bootstrap/*.tfvars
20+
21+
# tf lock files
22+
infra/.terraform.lock.hcl
23+
infra/bootstrap/.terraform.lock.hcl

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
# 📋 PERN Task Manager — GitHub Actions CI/CD Pipeline
22

33
![CI/CD](https://img.shields.io/badge/CI%2FCD-GitHub_Actions-2088FF?style=for-the-badge&logo=githubactions&logoColor=white)
4+
![Kubernetes](https://img.shields.io/badge/Kubernetes-326CE5?style=for-the-badge&logo=kubernetes&logoColor=white)
45
![Docker](https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white)
56
![AWS](https://img.shields.io/badge/AWS_EC2-FF9900?style=for-the-badge&logo=amazonaws&logoColor=white)
67
![PostgreSQL](https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge&logo=postgresql&logoColor=white)
78
![React](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)
89
![Node.js](https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)
910
![Nginx](https://img.shields.io/badge/Nginx-009639?style=for-the-badge&logo=nginx&logoColor=white)
1011

11-
A full-stack PERN (PostgreSQL, Express, React, Node.js) task manager app deployed to AWS EC2 via a fully automated CI/CD pipeline using GitHub Actions and Docker.
12+
A full-stack PERN (PostgreSQL, Express, React, Node.js) task manager app deployed to Kubernetes via a fully automated CI/CD pipeline using GitHub Actions, kind, and K3s.
1213

13-
> **v2 of this project** — previously deployed with Jenkins. Migrated to GitHub Actions to remove the need for a separate CI server. See [task-manager-cicd-pipeline](https://github.com/kithupag/task-manager-cicd-pipeline) for the Jenkins version.
14+
> **v3 of this project** — previously deployed with docker compose. Migrated to Kubernetes to move beyond single-host deployments. See [task-manager-cicd-pipeline](https://github.com/kithupag/task-manager-cicd-pipeline) for the Jenkins version.
1415
1516
---
1617

infra/backend.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
terraform {
2+
backend "s3" {
3+
bucket = "myapp-terraform-state-kithupag"
4+
key = "eks/terraform.tfstate"
5+
region = "ap-southeast-1"
6+
dynamodb_table = "terraform-state-lock"
7+
encrypt = true
8+
}
9+
}

infra/bootstrap/main.tf

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
provider "aws" {
2+
region = "ap-southeast-1"
3+
}
4+
5+
resource "aws_s3_bucket" "terraform_state" {
6+
bucket = "myapp-terraform-state-kithupag"
7+
8+
lifecycle {
9+
prevent_destroy = true
10+
}
11+
12+
tags = {
13+
Name = "Terraform State"
14+
}
15+
}
16+
17+
resource "aws_s3_bucket_versioning" "terraform_state" {
18+
bucket = aws_s3_bucket.terraform_state.id
19+
versioning_configuration {
20+
status = "Enabled"
21+
}
22+
}
23+
24+
resource "aws_dynamodb_table" "basic-dynamodb-table" {
25+
name = "terraform-state-lock"
26+
hash_key = "LockID"
27+
billing_mode = "PAY_PER_REQUEST"
28+
29+
attribute {
30+
name = "LockID"
31+
type = "S"
32+
}
33+
34+
tags = {
35+
Name = "dynamodb-table"
36+
}
37+
}

infra/main.tf

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 6.0"
6+
}
7+
}
8+
required_version = ">= 1.3.0"
9+
}
10+
11+
provider "aws" {
12+
region = var.region
13+
}
14+
15+
resource "aws_vpc" "myapp_vpc" {
16+
cidr_block = var.vpc_cidr_block
17+
tags = {
18+
Name = "${var.env_prefix}-vpc"
19+
}
20+
}
21+
22+
23+
resource "aws_subnet" "myapp-subnet-1" {
24+
vpc_id = aws_vpc.myapp_vpc.id
25+
cidr_block = var.subnet_cidr_block
26+
availability_zone = var.avail_zone
27+
map_public_ip_on_launch = true
28+
29+
tags = {
30+
Name = "${var.env_prefix}-subnet-1"
31+
}
32+
}
33+
34+
resource "aws_subnet" "myapp-subnet-2" {
35+
vpc_id = aws_vpc.myapp_vpc.id
36+
cidr_block = var.subnet_cidr_block_2
37+
availability_zone = var.avail_zone_2
38+
map_public_ip_on_launch = true
39+
40+
tags = {
41+
Name = "${var.env_prefix}-subnet-2"
42+
}
43+
}
44+
45+
46+
resource "aws_internet_gateway" "myapp_igw" {
47+
vpc_id = aws_vpc.myapp_vpc.id
48+
49+
tags = {
50+
Name = "${var.env_prefix}-igw"
51+
}
52+
}
53+
54+
resource "aws_default_route_table" "main_rtb" {
55+
default_route_table_id = aws_vpc.myapp_vpc.default_route_table_id
56+
57+
route {
58+
cidr_block = "0.0.0.0/0"
59+
gateway_id = aws_internet_gateway.myapp_igw.id
60+
}
61+
62+
tags = {
63+
Name = "${var.env_prefix}-main-rtb"
64+
}
65+
}
66+
67+
module "eks" {
68+
source = "terraform-aws-modules/eks/aws"
69+
version = "~> 21.0"
70+
71+
name = "myapp-eks-cluster"
72+
kubernetes_version = "1.33"
73+
74+
endpoint_public_access = true
75+
enable_cluster_creator_admin_permissions = true
76+
77+
eks_managed_node_groups = {
78+
dev_nodes = {
79+
ami_type = "AL2023_x86_64_STANDARD"
80+
instance_types = ["t3.small"]
81+
min_size = 1
82+
max_size = 3
83+
desired_size = 2
84+
}
85+
}
86+
87+
vpc_id = aws_vpc.myapp_vpc.id
88+
subnet_ids = [aws_subnet.myapp-subnet-1.id, aws_subnet.myapp-subnet-2.id]
89+
90+
tags = {
91+
environment = "dev"
92+
Terraform = "true"
93+
}
94+
}
95+
96+
resource "aws_security_group" "myapp_sg" {
97+
name = "myapp-sg"
98+
vpc_id = aws_vpc.myapp_vpc.id
99+
100+
ingress {
101+
from_port = 443
102+
to_port = 443
103+
protocol = "TCP"
104+
cidr_blocks = ["0.0.0.0/0"]
105+
}
106+
107+
ingress {
108+
from_port = 30080
109+
to_port = 30080
110+
protocol = "TCP"
111+
cidr_blocks = ["0.0.0.0/0"]
112+
}
113+
114+
egress {
115+
from_port = 0
116+
to_port = 0
117+
protocol = "-1"
118+
cidr_blocks = ["0.0.0.0/0"]
119+
}
120+
121+
tags = {
122+
Name = "${var.env_prefix}-sg"
123+
}
124+
}
125+
126+
output "eks_cluster_name" {
127+
value = module.eks.cluster_name
128+
}
129+
130+
output "eks_cluster_endpoint" {
131+
value = module.eks.cluster_endpoint
132+
}

infra/variables.tf

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
variable "vpc_cidr_block" {
2+
default = "10.0.0.0/16"
3+
}
4+
variable "subnet_cidr_block" {
5+
default = "10.0.10.0/24"
6+
}
7+
variable "subnet_cidr_block_2" {
8+
default = "10.0.20.0/24"
9+
}
10+
variable "avail_zone" {
11+
default = "ap-southeast-1a"
12+
}
13+
variable "avail_zone_2" {
14+
default = "ap-southeast-1b"
15+
}
16+
variable "env_prefix" {
17+
default = "dev"
18+
}
19+
variable "my_ip" {
20+
default = "43.250.242.220/32"
21+
}
22+
variable "instance_type" {
23+
default = "t3.small"
24+
}
25+
26+
variable "region" {
27+
default = "ap-southeast-1"
28+
}

0 commit comments

Comments
 (0)