Bird API written in Golang [Code to Containerization, CI/CD Pipelines, Deployed AWS Infra with Kubernetes and Monitoring ]

Bird API written in Golang [Code to Containerization, CI/CD Pipelines, Deployed AWS Infra with Kubernetes and Monitoring ]

Step 0: System Design and How the API interact

Source Code :

https://github.com/Gatete-Bruno/lifinance-devops-challenge?tab=readme-ov-file

Overview of the Source Code:

  • K8s-Bootstrapp: Contains various YAML files and scripts for bootstrapping and managing the Kubernetes cluster, including installation, node setup, updates, and SSH management. The structure is well-labeled, making it easy to understand each script’s purpose.

  • bird and birdImage: Each directory is dedicated to an API (bird and birdImage). They include Dockerfiles for containerization, Makefiles for build automation, Go modules (go.mod), and main application files. The separation of these two APIs into distinct folders makes it easier to manage them independently.

  • bird-chart: Contains Helm chart files for deploying the bird API. With distinct templates (deployment.yaml, service.yaml) and values (values.yaml), it helps streamline Kubernetes resource management.

  • k8s-manifests: Manages Kubernetes deployment manifests for both APIs (bird-api-deployment.yaml and bird-image-deployment.yaml). Keeping this separate from the Helm chart helps differentiate between Helm-based deployments and manual Kubernetes manifests.

  • observability: Focuses on monitoring and storage, including Longhorn storage, Prometheus, and service monitors. It’s good that observability is handled separately.

  • terraform: Contains your Infrastructure as Code (IaC) for managing AWS resources (IAM, EC2 instances, VPC). Keeping it isolated helps with infrastructure management

How the Bird API works:

1. Bird API (bird/main.go)

This part of the application is responsible for providing information about different birds.

  • When a user sends a request to the Bird API, the system randomly selects a bird fact (name and description) from a third-party API: https://freetestapi.com/api/v1/birds.

  • The Bird API does two things:

    • Gets a bird fact: It asks the third-party API for a random bird's name and description (like "Eagle" or "Parrot").

    • Gets an image: It uses the bird's name to ask another API (the Bird Image API) to return an image related to that bird. If the image can't be found, a default "missing bird" image is shown.

  • After gathering both the bird's name, description, and image, it combines them into a response and sends it back to the user.

Example:

You ask the API for bird info, and it might return:

{
  "name": "Eagle",
  "description": "A powerful bird of prey with sharp vision.",
  "image": "http://link-to-eagle-image.com"
}

If something goes wrong (like if the API can’t find the bird or image), it will send back a funny default bird with this image:
https://www.pokemonmillennium.net/wp-content/uploads/2015/11/missingno.png.

2. Bird Image API (birdImage/main.go)

This part of the application is responsible for finding and sending an image for a specific bird.

  • When the Bird API asks for an image, the Bird Image API searches for it on Unsplash, a photo-sharing website.

    • It sends a request to Unsplash using the bird’s name (e.g., "Eagle") and asks Unsplash for a photo related to that bird.
  • The Bird Image API then processes the Unsplash response and extracts the URL of the image.

  • If it finds the bird image, it sends the image URL back to the Bird API.

  • If it can't find the bird image or there's an error, it sends back a default image (the same "missing bird" image).

Example:

The Bird Image API searches for an "Eagle" image on Unsplash and returns the URL to that image. If it can't find one, it returns the default "missing bird" image.

System Design :

1. GitHub (Source Control)

  • Purpose: Stores the codebase for the Bird API. It acts as the central repository where all changes, pull requests, and branches are managed.

  • Key Technology: GitHub

2. GitHub Actions (CI/CD Pipeline)

  • Purpose: Automates the process of building, testing, and deploying the Bird API. GitHub Actions is triggered by changes in the GitHub repository (e.g., push or pull request).

  • Key Steps:

    • Build: Docker images are built for both the bird and birdImage APIs.

    • Test: Runs unit and integration tests to verify the code quality.

    • Deploy: Once tests pass, the Docker image is pushed to DockerHub and later deployed to Kubernetes.

  • Key Technology: GitHub Actions

3. Docker (Containerization)

  • Purpose: Packages the Bird API and Bird Image API into Docker containers, ensuring consistency across different environments (development, testing, and production).

  • Key Technology: Docker

4. Helm (Kubernetes Package Manager)

  • Purpose: Automates the deployment and management of Kubernetes applications. Helm packages (charts) are used to define, install, and upgrade Kubernetes deployments, including the Bird API.

  • Key Technology: Helm

5. Kubernetes (Container Orchestration)

  • Purpose: Manages the containerized applications in a production-grade environment. It ensures high availability, scalability, and fault tolerance for the Bird API and Bird Image API.

  • Key Components:

    • Pods: The smallest unit in Kubernetes that runs the Bird API containers.

    • Service (SVC): Load balances traffic across the Pods to ensure seamless access to the API.

  • Key Technology: Kubernetes

6. Terraform (Infrastructure as Code)

  • Purpose: Provisions and manages AWS infrastructure required to run the Kubernetes cluster. Terraform defines resources such as VPCs, subnets, security groups, and EC2 instances.

  • Key Technology: Terraform

7. AWS (Cloud Infrastructure)

  • Purpose: AWS is the cloud provider where the Kubernetes cluster is deployed. It provides the virtual machines (EC2 instances) and networking infrastructure (VPC, subnets) required to run the applications.

  • Key AWS Services:

    • EC2: Virtual machines where the Kubernetes nodes run.

    • VPC: Virtual network for the infrastructure.

    • Subnets and Security Groups: Manage the networking and security of the EC2 instances.

  • Key Technology: AWS (EC2, VPC, Security Groups)

8. Ansible (Configuration Management)

  • Purpose: Ansible automates configuration management tasks, such as provisioning and managing EC2 instances. It ensures that the instances are properly configured to run Kubernetes.

  • Key Technology: Ansible

9. Kubernetes Cluster (On AWS)

  • Purpose: The Kubernetes cluster is deployed on EC2 instances within AWS. This cluster orchestrates the running of the Bird API and Bird Image API.

  • Key Components:

    • Master Node: Manages the Kubernetes control plane (API server, scheduler, controller manager).

    • Worker Nodes: Run the application Pods (Bird API and Bird Image API).

    • Service: Exposes the Pods and ensures that traffic is routed correctly to the applications.

10. Pods and Services (Running the Bird API)

  • Purpose: Each Pod contains one or more containers running the Bird API or Bird Image API. Kubernetes manages the lifecycle and scaling of these Pods.

  • Key Technology: Pods (within Kubernetes)

Step 1: Run the App Locally:

The first important step is to change this file so that the service where the api is exposed is able to receive the endpoint from the Bird APi

how to change it, go to the directory and edit the file main.go

bruno@Batman-2 lifinance-devops-challenge % cd bird/      
bruno@Batman-2 bird % ls
Dockerfile      Makefile        getBird         go.mod          main.go
bruno@Batman-2 bird % vi main.go

change this line in the file to this , possibly line 27

res, err := http.Get(fmt.Sprintf("http://localhost:4200?birdName=%s", url.QueryEscape(birdName)))

To run the birdImage API locally, follow these steps:

1. Build the API

Since the Makefile provides a target to build the API, you can use the following command to compile the code:

make birdImage

2. Run the built executable

After building, you will have an executable named getBirdImage. Run the executable with:

./getBirdImage

This will start the server on port 4200 as specified in the main.go file:

http.ListenAndServe(":4200", nil)

3. Test the API

You can now test the API by sending a request to it. Use curl to make a request:

  • Without specifying a bird name (this will return the default image):

      curl "http://localhost:4200/"
    

o run the bird API, follow these steps, similar to what we did for the birdImage API:

1. Build the API

Run the following command to build the bird API using the Makefile:

make bird

This will generate an executable named getBird.

2. Run the built executable

Once the build is complete, run the executable with:

./getBird

This will start the server on port 4201, as specified in the main.go file:

http.ListenAndServe(":4201", nil)

3. Test the API

To test the API, use curl to make a request to the bird API:

curl "http://localhost:4201/"

This request should return a random bird fact and an image. The getBirdImage function in your code sends a request to the birdImage API (which is running on localhost:4200) to retrieve an image for the bird.

  • Ensure that the birdImage API is already running on localhost:4200 before running the bird API, as the bird API fetches bird images from it.

Step 2: Containerize the App Using Docker:

Same thing as in step 1 is to change this file so that the service where docker is exposed is able to receive the endpoint from the Bird APi

how to change it, go to the directory and edit the file main.go

bruno@Batman-2 lifinance-devops-challenge % cd bird/      
bruno@Batman-2 bird % ls
Dockerfile      Makefile        getBird         go.mod          main.go
bruno@Batman-2 bird % vi main.go

change this line in the file to this , possibly line 27

res, err := http.Get(fmt.Sprintf("http://localhost:4200?birdName=%s", url.QueryEscape(birdName)))

To Dockerize both the Bird API and Bird Image API, you'll need to create a Dockerfile for each API in their respective directories. Here's how you can structure the Dockerfiles.

for bird dir

# syntax=docker/dockerfile:1
FROM golang:1.22-alpine AS builder

# Set the working directory
WORKDIR /app

# Copy go.mod and go.sum files
COPY go.mod ./

# Download dependencies
RUN go mod download

# Copy the rest of the application code
COPY . .

# Build the bird API
RUN go build -o bird-api .

# Use the latest minimal Alpine image for the final deployment
FROM alpine:latest

WORKDIR /root/
COPY --from=builder /app/bird-api .
CMD ["./bird-api"]
EXPOSE 4201

for birdImage dir

# syntax=docker/dockerfile:1
FROM golang:1.22-alpine AS builder

# Set the working directory
WORKDIR /app

# Copy go.mod and go.sum files
COPY go.mod ./

# Download dependencies
RUN go mod download

# Copy the rest of the application code
COPY . .

# Build the birdImage API
RUN go build -o bird-image-api .

# Use the latest minimal Alpine image for the final deployment
FROM alpine:latest

WORKDIR /root/
COPY --from=builder /app/bird-image-api .
CMD ["./bird-image-api"]
EXPOSE 4200

In this case, Most importnantly for the containers to communicate, they need to be on the same docker network , so this is what we do :

# Create a network (only once)
docker network create bird-net

# Run both containers on the network
docker run -d --name bird-image-api --network bird-net -p 4200:4200 bruno74t/bird-image-api
docker run -d --name bird-api --network bird-net -p 4201:4201 bruno74t/bird-api

Be mindfull that i have arleady logged into my docker hub for container registry .

Output

Step 3: Ci/CD Pipeline using Github Actions

To setup Ci/CD pipline, we will be using github actions, in this case :

bruno@Batman-2 workflows % ls
docker-ci.yml
bruno@Batman-2 workflows % 

bruno@Batman-2 workflows % cat docker-ci.yml 
name: CI/CD for Bird APIs

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    services:
      docker:
        image: docker:20.10.7
        options: --privileged
        ports:
          - 2375:2375
        env:
          DOCKER_HOST: tcp://localhost:2375

    steps:
      # Step 1: Checkout the code
      - name: Checkout Code
        uses: actions/checkout@v2

      # Step 2: Set up Docker Buildx
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      # Step 3: Log into Docker Hub
      - name: Log into Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD_SYMBOLS_ALLOWED }}" | docker login --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin

      # Step 4: Build and push bird API image
      - name: Build and Push Bird API Docker Image
        run: |
          docker build -t ${{ secrets.DOCKER_USERNAME }}/bird-api:v.1.0.2 ./bird
          docker push ${{ secrets.DOCKER_USERNAME }}/bird-api:v.1.0.2

      # Step 5: Build and push birdImage API image
      - name: Build and Push BirdImage API Docker Image
        run: |
          docker build -t ${{ secrets.DOCKER_USERNAME }}/bird-image-api:v.1.0.2 ./birdImage
          docker push ${{ secrets.DOCKER_USERNAME }}/bird-image-api:v.1.0.2
bruno@Batman-2 workflows %

Once the Source Code is pushed to Github build process will start and the image will be pushed to my docker hub registrty

Step 4: Build AWS infrastructure

our working directory is terraform for this activity
bruno@Batman-2 terraform % tree
.
├── iam-output.tf
├── instanct.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── vpc.tf

1 directory, 5 files
bruno@Batman-2 terraform %

The Source Code is on my Github repo

https://github.com/Gatete-Bruno/lifinance-devops-challenge/tree/main/terraform

Requirments:

  • AWS account

  • An AWS user account with elevated privileges to setup these resources

  • SSH Key to access the EC2 instances

  • AWS CLI configured using AWS configure

  • Terraform installed on the Local machine

terraform init
terraform validate
terraform plan
terraform apply

Once you type in yes resources will be provisioned !

These are some of the resources provisioned by terraform leveraging Infrastructure as Code.

Step 5: Bootsrap Kubernetes Cluster. Using Kubekey

I am using ansible to automate the provisioning of the Cluster :

Requirements:

  • SSH keys for the local machine established on the EC2 instances

  • public key for the EC2 instances installed on each so that they can communicate together

To test the connectivity for the local machine and the EC2 Instances we go ahead to use a ping.yaml file

Then after we will edit a few important files ,

bruno@Batman-2 Kubeky-Bootstrapping-Ansible % cat config-sample.yaml 
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
  name: sample
spec:
  hosts: 
  - {name: master1, address: 54.91.229.69, internalAddress: 10.0.1.151, user: kato}
  - {name: node1, address: 3.91.27.241, internalAddress: 10.0.1.5, user: kato}
  - {name: node2, address: 3.88.157.155, internalAddress: 10.0.1.196, user: kato}

  roleGroups:
    etcd:
    - master1
    control-plane:
    - master1
    worker:
    - node1
    - node2
  controlPlaneEndpoint:
    ## Internal loadbalancer for apiservers 
    # internalLoadbalancer: haproxy

    domain: lb.kubesphere.local
    address: ""
    port: 6443
  kubernetes:
    version: v1.23.10
    clusterName: cluster.local
    autoRenewCerts: true
    containerManager: docker
  etcd:
    type: kubekey
  network:
    plugin: calico
    kubePodsCIDR: 10.233.64.0/18
    kubeServiceCIDR: 10.233.0.0/18
    ## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
    multusCNI:
      enabled: false
  registry:
    privateRegistry: ""
    namespaceOverride: ""
    registryMirrors: []
    insecureRegistries: []
  addons: []

bruno@Batman-2 Kubeky-Bootstrapping-Ansible %

I added the IP addresses and the user for authentication purposes

Once that is done, we will run this file that contains all imported playbooks

bruno@Batman-2 Kubeky-Bootstrapping-Ansible % cat setup-kubernetes-cluster.yaml 
---
# - name: Add all nodes to known hosts
#   import_playbook: 00-add-to-ssh-known-hosts.yaml

- name: Update nodes
  import_playbook: 01-update-servers.yaml

- name: Install required modules
  import_playbook: 02-install-requirements.yaml

- name: Disable swap on all nodes
  import_playbook: 03-disable-swap.yaml

- name: Download & setup kubekey
  import_playbook: 04-download-kubekey.yaml

- name: Install k8
  import_playbook: 05-install-kubernetes.yaml
bruno@Batman-2 Kubeky-Bootstrapping-Ansible %

On completion, the cluster should be up and running

Step 6: Deploy Containerized Application

Two Approaches:

  1. Build Kubernetes Manifest Files

https://github.com/Gatete-Bruno/lifinance-devops-challenge/tree/main/k8s-manifests

Approach 2:

Using Helm to Package the application:

https://github.com/Gatete-Bruno/lifinance-devops-challenge/tree/main/bird-chart

I opted for option two, because it is easier to package the application and make changes :

Install Helm

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Initialize a Helm Chart

helm create bird-chart

Customize the chart dir and edit the files

bruno@Batman-2 bird-chart % tree
.
├── Chart.yaml
├── charts
├── templates
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   └── service.yaml
└── values.yaml

3 directories, 5 files
bruno@Batman-2 bird-chart %

Package and Install the Helm Chart

# Package the chart
helm package bird-chart

# Install the chart in your Kubernetes cluster
helm install bird-api-release bird-chart/

Verify the Installation

After the Helm chart is installed, you can verify the resources:

[root@master1 lifinance-devops-challenge]# ls
README.md  bird  bird-chart  bird-chart-0.1.0.tgz  birdImage  k8s-manifests  observability  terraform
[root@master1 lifinance-devops-challenge]# kubectl get all
NAME                                                   READY   STATUS    RESTARTS   AGE
pod/bird-api-release-bird-api-549bc4d-f75v8            1/1     Running   0          14h
pod/bird-api-release-bird-api-549bc4d-t6gl2            1/1     Running   0          14h
pod/bird-api-release-bird-image-api-797b664f65-jpl9s   1/1     Running   0          14h
pod/bird-api-release-bird-image-api-797b664f65-nkzsr   1/1     Running   0          14h

NAME                                              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/bird-api-release-bird-api-service         NodePort    10.233.48.70    <none>        80:32493/TCP   14h
service/bird-api-release-bird-image-api-service   NodePort    10.233.14.223   <none>        80:31708/TCP   14h
service/kubernetes                                ClusterIP   10.233.0.1      <none>        443/TCP        16h

NAME                                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/bird-api-release-bird-api         2/2     2            2           14h
deployment.apps/bird-api-release-bird-image-api   2/2     2            2           14h

NAME                                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/bird-api-release-bird-api-549bc4d            2         2         2       14h
replicaset.apps/bird-api-release-bird-image-api-797b664f65   2         2         2       14h
[root@master1 lifinance-devops-challenge]#

Since i used NodePort , i have access to the application via the port

Step 7: Observability for the Two API

We will need to install promtheus and grafana, using Helm , but before we begin , we will install Longhorn, why, to hand our pvc and pv

how to install Longhorn using Helm

What Longhorn Does

Longhorn is a cloud-native distributed block storage system for Kubernetes. It provides persistent storage for your applications running in Kubernetes clusters and can dynamically provision storage volumes to meet the needs of your PVCs.

Steps to Use Longhorn to Solve the Issue

  1. Install Longhorn

    If you haven’t already installed Longhorn, you can install it using Helm:

     helm repo add longhorn https://charts.longhorn.io
     helm repo update
     helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace
    
  2. Configure Longhorn

    After installation, access the Longhorn UI to configure storage settings. You can typically access it via a LoadBalancer or NodePort service exposed in the longhorn-system namespace.

     kubectl get svc -n longhorn-system
    

    Look for the Longhorn UI service and access it using the provided external IP or NodePort.

  3. Create and Attach Storage Classes

    Define a storage class for Longhorn if it hasn’t been created by default. Here’s an example:

     apiVersion: storage.k8s.io/v1
     kind: StorageClass
     metadata:
       name: longhorn
     provisioner: driver.longhorn.io
     volumeBindingMode: WaitForFirstConsumer
    

    Apply the storage class:

     kubectl apply -f longhorn-storage-class.yaml
    

Reinstall Prometheus and Grafana

If you are using Helm, you can easily reinstall Prometheus and Grafana using the Prometheus and Grafana Helm charts.

  1. Reinstall Prometheus:
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring
  1. Reinstall Grafana:
helm install grafana grafana/grafana -n monitoring

Ensure that the correct Helm repositories are added:

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

Step 3: Verify the installation

You can check the status of the newly installed components:

kubectl get all -n monitoring

with a few metrics we can have a virtualization :

Step 8: Scaling the Bird-API App

These two options we can consider here:

  1. Update the values.yaml for scaling (Since we are using HELM)

  2. Horizontal Pod Autoscaler (HPA)

Option 1: Update Values file

To scale your application running on Kubernetes seamlessly, you can leverage Helm to update the number of replicas for each deployment. Since you're using Helm, the scaling process can be done by updating the values.yaml file and then redeploying the Helm chart.

Here's a step-by-step guide to scaling your application:

1. Update the values.yaml for scaling

In your values.yaml, you already have the replicas set for both birdApi and birdImageApi. To scale, simply increase the replica count for each:

birdApi:
  image: "bruno74t/bird-api:v.1.0.2"
  replicas: 4  # Increased from 2 to 4
  nodePort: 32493

birdImageApi:
  image: "bruno74t/bird-image-api:v.1.0.2"
  replicas: 4  # Increased from 2 to 4
  nodePort: 31708

You can adjust the number of replicas to suit your desired scale.

2. Apply the updated Helm chart

After modifying the values.yaml file, you can redeploy the Helm chart with the updated replica counts.

helm upgrade bird-api-release ./bird-chart

This command will update your Helm release with the new configuration and scale your application to the desired number of replicas.

3. Verify the scaling

After the Helm chart is upgraded, verify that the scaling has taken place by checking the status of your pods and deployments:

kubectl get deployments
kubectl get pods

You should see the increased number of pods for both birdApi and birdImageApi reflecting the new replica counts.

Option 2: HPA

The Horizontal Pod Autoscaler (HPA) in Kubernetes is a feature that automatically adjusts the number of replicas of a deployment, replication controller, or stateful set based on observed CPU, memory, or custom metrics. This provides an automated way to scale applications based on the current workload, ensuring that your application can handle increased traffic or reduced traffic without manual intervention.

Here's an in-depth explanation of how the HPA works and how you can implement it in your Kubernetes cluster.

1. How HPA Works

The Horizontal Pod Autoscaler controller periodically checks the resource usage (like CPU or memory) of the pods in a deployment and adjusts the number of running replicas to match the desired performance goals. The HPA can scale up (increase the number of replicas) or scale down (reduce the number of replicas) based on the configured thresholds.

HPA relies on Kubernetes Metrics API, which collects resource usage data from the running pods. You can also use custom metrics or external metrics to trigger scaling events, but for now, we'll focus on CPU utilization.

2. Prerequisites for HPA

Before setting up HPA, you need to ensure the following:

  • Metrics Server Installed: The Metrics Server is required for HPA to collect resource usage information like CPU and memory. You can install it by running:

      kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
    
  • Resource Requests and Limits: HPA requires that your deployments specify CPU (or memory) resource requests and limits. For example, in your deployment.yaml file, you need to specify these values:

      resources:
        requests:
          cpu: "100m"  # The pod requests 100 milli-CPU (10% of a CPU)
        limits:
          cpu: "500m"  # The pod can use up to 500 milli-CPU (50% of a CPU)
    

3. Configuring Horizontal Pod Autoscaler

Once the prerequisites are in place, you can create a Horizontal Pod Autoscaler to monitor your application's CPU usage and adjust the number of replicas accordingly.

Here’s how to configure the HPA:

a. Command-Line HPA Setup

You can create an HPA using a single command that specifies the target deployment, the metric (CPU usage), and the desired minimum and maximum number of replicas.

For example, to create an HPA for your bird-api deployment that maintains CPU usage at 50%, with a minimum of 2 replicas and a maximum of 10 replicas:

kubectl autoscale deployment bird-api-release-bird-api --cpu-percent=50 --min=2 --max=10

Explanation:

  • bird-api-release-bird-api: This is the deployment you want to scale.

  • --cpu-percent=50: This tells the HPA to scale the deployment if the average CPU usage across all pods exceeds 50% of the requested CPU.

  • --min=2: This sets the minimum number of pods to 2, even if the CPU usage is low.

  • --max=10: This sets the maximum number of pods to 10, even if the CPU usage is high.

b. YAML-Based HPA Configuration

You can also create an HPA using a YAML configuration file. Here’s an example YAML for your bird-api deployment:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: bird-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: bird-api-release-bird-api  # Target deployment to scale
  minReplicas: 2  # Minimum number of replicas
  maxReplicas: 10  # Maximum number of replicas
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50  # Target average CPU usage (percentage)

Once you create this YAML file, you can apply it with:

kubectl apply -f bird-api-hpa.yaml

4. How HPA Makes Scaling Decisions

  • Metrics Collection: The HPA controller queries the Metrics Server (or custom/external metrics) to get the current CPU usage of all pods in the target deployment.

  • Scaling Up: If the average CPU usage across all pods exceeds the target threshold (e.g., 50%), the HPA will increase the number of replicas to distribute the load more evenly.

  • Scaling Down: If the average CPU usage is below the target threshold, the HPA will reduce the number of replicas to avoid overprovisioning resources.

  • Cooldown Period: The HPA doesn't immediately scale up or down in response to transient changes in metrics. It has a cooldown period, typically 30 seconds to 1 minute, to prevent rapid, unnecessary scaling.

5. Monitoring HPA

To check the status of your HPA, you can use the following command:

kubectl get hpa

It will show you the current state of the HPA, including the current and desired number of replicas, as well as the observed CPU utilization.

Example output:

NAME                   REFERENCE                         TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
bird-api-hpa           Deployment/bird-api-release-bird-api   60%/50%   2         10        6          2m

6. Scaling Based on Custom Metrics (Optional)

While CPU is the most common metric used for scaling, HPA can also scale based on memory, custom metrics, or even external metrics (e.g., requests per second). Here’s an example of scaling based on memory:

metrics:
- type: Resource
  resource:
    name: memory
    target:
      type: Utilization
      averageUtilization: 75  # Scale when memory usage is above 75%

7. Best Practices for HPA

  • Set appropriate resource requests and limits: Ensure your deployments have proper resource requests and limits, as HPA relies on these to calculate utilization.

  • Choose sensible min and max replicas: Setting the right min and max replica counts prevents your application from being under or over-provisioned.

  • Monitor your HPA: Regularly check your HPA’s performance to ensure that it's scaling as expected. Tools like Prometheus and Grafana can provide detailed insights into how your application scales over time.

  • Test with load: To see how HPA behaves under real-world conditions, perform load tests on your application and monitor how the replicas adjust.

Step 9: Security Best Practices

Enhance Terraform Provisioning Part:

bruno@Batman-2 terraform % tree
.
├── iam-output.tf
├── instanct.tf
└── vpc.tf

1 directory, 3 files
AreaBest PracticeExplanation
SSH AccessRestrict SSH access to trusted IP rangesInstead of allowing SSH from 0.0.0.0/0, specify trusted IPs for better security.
Public ExposureLimit access to Kubernetes API (port 6443) and NodePort services (30000-32767)Restrict these ports to trusted IPs rather than exposing them to the public internet.
Kubelet API AccessRestrict access to Kubelet API (ports 10250-10255)Ensure Kubelet ports are accessible only by trusted IPs to prevent unauthorized access.
Longhorn ServicesLimit access to Longhorn services (ports 9500-9509)Avoid exposing these ports to the public internet; use a more restricted IP range.
Security GroupsRestrict outbound traffic in security groupsInstead of allowing all outbound traffic (0.0.0.0/0), restrict outbound traffic to necessary services and ports.
Use Private SubnetsDeploy sensitive resources in private subnetsKeep sensitive infrastructure away from public exposure by placing them in private subnets.
IAM RolesApply least-privilege IAM rolesGrant EC2 instances only the permissions they need to perform their specific tasks, such as accessing S3 or CloudWatch.
System UpdatesAutomate system updates and patchingInclude automatic security updates in the instance's user data section to keep systems secure and up to date.
Logging and MonitoringEnable VPC Flow Logs or AWS CloudTrail for traffic monitoringTrack traffic and actions within the infrastructure to detect unusual activity and improve visibility.
Data EncryptionEncrypt sensitive data in transitEnsure encryption for Kubernetes control plane communication, API traffic, and other sensitive data transmissions.
Security MonitoringSet up security tools like AWS GuardDutyUse AWS security monitoring services to detect threats and monitor suspicious activity across the infrastructure.

Enhancing Security on the Kubernetes Cluster

Security MeasurePurposeOutcome
Ingress Controller with SSL/TLSSecure access with HTTPS using certificatesEncrypted, secure access to your app
Network Policies (Ingress/Egress)Control traffic flow into and out of your clusterReduced attack surface
RBACLimit access and privileges in the clusterMinimized access risks
Secrets ManagementSecure sensitive data such as API keysEncrypted and restricted access to sensitive info
Pod Security Standards (PSS)Ensure pods run securely (e.g., as non-root users)Prevent privilege escalation
Secure API AccessRestrict and monitor access to the Kubernetes APIImproved control over the control plane
Ingress with DNS and CertificatesAutomate DNS and certificates for secure and easy accessSimplified management and secure access