DEV Community

Cover image for Apache Web-Server Deployment on Terraform Deployed EKS Cluster with Secure Access, Observability, and Scalable Infrastructure
Dennis Tei-Muno for AWS Community Builders

Posted on • Originally published at Medium

1

Apache Web-Server Deployment on Terraform Deployed EKS Cluster with Secure Access, Observability, and Scalable Infrastructure

Featuring Namespaces, Role-Based Access Control(Cluster-Role and Cluster Rolebinding), Observability(Liveness and Readiness Probe) and a Kubernetes Apache Deployment with 5 replicas. This is my first ever Kubernetes on EKS and I can’t wait to build more!!! The GitHub repo for this project can be found here:
Repo-link
This project was also published on my public portfolio page over on Medium.com at:
Image description

Image description

Business Use Application/Relevance:

  • This project shows how a containerized apache web server can be deployed on an EKS provisioned kubernetes cluster and exposed to traffic. The cluster infrastructure is deployed infrastructure-as-code with terraform and then by setting local kubeconfig to the EKS cluster the apache deployment is made.

Challenges I Faced and How I Solved Them:
What service type to use to expose apache deployment. I realized after a lot of trying that a load balancer service would work best for me.
Security groups trouble. Be sure to let security groups include ports you’ve opened in your kubernetes manifests.

Part1: Setup GitHub Repository and Lock It Down

  • I will create a GitHub repository, and then lock down the repository:

Image description

Image description

  • I will create a dev branch:

Image description

  • I will clone the repository and edit with Visual Studio Code: Image description

Part2: Create Kubernetes Cluster Terraform Files and Deploy Infrastructure

  • In Visual Studio Code I will create the following files for my cluster(I used EKS Auto)

  • I will then open a terminal and run the commands:

export AWS_ACCESS_KEY_ID=<your-aws-access-key-id>
export AWS_SECRET_ACCESS_KEY=<your-secret-access-key>
terraform init
terraform fmt
terraform validate
terraform plan
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

My files:

main.tf:


provider "aws" {
  region = "us-east-1"
}


resource "aws_eks_cluster" "example" {
  name = "dtmcluster"

  access_config {
    authentication_mode = "API"
  }

  role_arn = aws_iam_role.cluster.arn
  version  = "1.32"

  bootstrap_self_managed_addons = false

  compute_config {
    enabled       = true
    node_pools    = ["general-purpose"]
    node_role_arn = aws_iam_role.node.arn
  }

  kubernetes_network_config {
    elastic_load_balancing {
      enabled = true
    }
  }

  storage_config {
    block_storage {
      enabled = true
    }
  }

  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true

    subnet_ids = [
      var.subnet_1,
      var.subnet_2,
      var.subnet_3,
    ]
  }

  # Ensure that IAM Role permissions are created before and deleted
  # after EKS Cluster handling. Otherwise, EKS will not be able to
  # properly delete EKS managed EC2 infrastructure such as Security Groups.
  depends_on = [
    aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
    aws_iam_role_policy_attachment.cluster_AmazonEKSComputePolicy,
    aws_iam_role_policy_attachment.cluster_AmazonEKSBlockStoragePolicy,
    aws_iam_role_policy_attachment.cluster_AmazonEKSLoadBalancingPolicy,
    aws_iam_role_policy_attachment.cluster_AmazonEKSNetworkingPolicy,
  ]
}

resource "aws_iam_role" "node" {
  name = "eks-auto-node-example"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = ["sts:AssumeRole"]
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      },
    ]
  })
}

resource "aws_iam_role_policy_attachment" "node_AmazonEKSWorkerNodeMinimalPolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy"
  role       = aws_iam_role.node.name
}

resource "aws_iam_role_policy_attachment" "node_AmazonEC2ContainerRegistryPullOnly" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly"
  role       = aws_iam_role.node.name
}

resource "aws_iam_role" "cluster" {
  name = "eks-cluster-example"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "sts:AssumeRole",
          "sts:TagSession"
        ]
        Effect = "Allow"
        Principal = {
          Service = "eks.amazonaws.com"
        }
      },
    ]
  })
}

resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSClusterPolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
  role       = aws_iam_role.cluster.name
}

resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSComputePolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSComputePolicy"
  role       = aws_iam_role.cluster.name
}

resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSBlockStoragePolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy"
  role       = aws_iam_role.cluster.name
}

resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSLoadBalancingPolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy"
  role       = aws_iam_role.cluster.name
}

resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSNetworkingPolicy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy"
  role       = aws_iam_role.cluster.name
}

Enter fullscreen mode Exit fullscreen mode

terraform.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.38"
    }
  }
  backend "s3" {
    bucket = "eks-backend-dtm"
    key    = "backend.hcl"
    region = "us-east-1"
  }
}
Enter fullscreen mode Exit fullscreen mode

.gitignore:

variables.tf
.terraform/
*.hcl
variables.tf file whose content I won’t show for security reasons:

Enter fullscreen mode Exit fullscreen mode

Image description

Part3: Connect To Cluster; Develop Kubernetes Files Locally and Then Apply Resources On Cluster

  • I will go my cluster in the AWS console and add the following add-ons to make my cluster even better:

Image description

  • I will navigate to add-ons and move to add some add-ons:

Image description

Image description

Image description

  • I will move to the next page and click on create recommended role:

Image description

Image description

Image description

  • I will move to the review page and add these add-ons:

Image description

  • I will then create an IAM Access Entry:

Image description

Image description

Image description

  • I have developed my kubernetes files: cluster-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: luitrole
rules:
- apiGroups:
  - ""
  resources:
  - '*'
  verbs:
  - '*'
Enter fullscreen mode Exit fullscreen mode

cluster-rolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: luitrolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: luitrole
subjects:
- kind: ServiceAccount
  name: luitsa
  namespace: luit
Enter fullscreen mode Exit fullscreen mode

deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: apache-deployment
  labels:
    app: apache
  namespace: luit
spec:
  replicas: 5
  selector:
    matchLabels:
      app: apache
  template:
    metadata:
      labels:
        app: apache
    spec:
      containers:
      - name: nginx
        image: public.ecr.aws/docker/library/httpd:latest
        command: ["sh", "-c", "touch /tmp/healthy && httpd-foreground"]
        livenessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80
Enter fullscreen mode Exit fullscreen mode

namespace.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: luit
Enter fullscreen mode Exit fullscreen mode

service-account.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: luitsa
  namespace: luit
Enter fullscreen mode Exit fullscreen mode

service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: luitservice
  namespace: luit
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30080
  selector:
    app: apache
  type: NodePort
Enter fullscreen mode Exit fullscreen mode
  • I will additionally open up port 30080 on my eks cluster security group:

Image description

Connecting To Cluster Via CLI

  • Connect to AWS CLI using the command:
aws configure
#Enter in information as required
Enter fullscreen mode Exit fullscreen mode
  • Connect to cluster using command:
aws  eks update-kubeconfig --name=<your-cluster-name>
Enter fullscreen mode Exit fullscreen mode
  • To check we are truly connected to the cluster I will try the command:
alias k=kubectl
k get all -A
Enter fullscreen mode Exit fullscreen mode

Image description

Image description

Image description

Part4: Test Kubernetes Deployment

  • I built out my files from earlier. I will create the resources one-by-one in the order that makes sense:
k create -f namespace.yaml
k create -f service-account.yaml
k create -f cluster-role.yaml
k create -f cluster-rolebinding.yaml
#to give my serviceaccount an  IAM  Role that allows access  to  EFS(IAM Role for  Service Account)
sh irsa-oidc.sh
k create -f deployment.yaml
k create -f service.yaml
Enter fullscreen mode Exit fullscreen mode
  • Our apache deployment should be exposed by a load balancer service(which will create an AWS load balancer). To get the DNS for the load balancer I will use the command:
kubectl get svc -n luit
#Copy the external IP and paste it  into your browser
Enter fullscreen mode Exit fullscreen mode

Image description

Cleaning Up
I will push my tracked changes to my github repo and merge with the main branch:

Image description

Image description

  • I will navigate to my GitHub repo on the main branch and initiate a merge request:

Image description

Image description

Image description

Image description

  • I will clean up my resources using the command;
terraform destroy -auto-approve
Enter fullscreen mode Exit fullscreen mode

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

👋 Kindness is contagious

Explore this compelling article, highly praised by the collaborative DEV Community. All developers, whether just starting out or already experienced, are invited to share insights and grow our collective expertise.

A quick “thank you” can lift someone’s spirits—drop your kudos in the comments!

On DEV, sharing experiences sparks innovation and strengthens our connections. If this post resonated with you, a brief note of appreciation goes a long way.

Get Started