Skip to main content

AWS Elastic Kubernetes Service EKS (Managed Kubernetes Services): Deploy EKS Cluster via eksctl; Example Deployment with TLS Encryption with AWS Certificate Manager & Load Balancer Service

1284 words·
Kubernetes Kubernetes Cluster AWS EKS Kubectl AWS Certificate Manager (ACM) AWS Elastic Load Balancer (ELB) Route 53
Kubernetes-Cluster - This article is part of a series.
Part 4: This Article

Amazon Elastic Kubernetes Service or Amazon Elastic Container Service for Kubernetes.

Prerequisites: Docker image
#

The following Docker container lists the hostname of the container. This is very helpful the test the scaling of a Kubernetes pod, as it lists the different hostnames when it’s curled.

Node.js
#

  • Create the following file: app.js
const http = require('http');
const os = require('os');

console.log("Kubernetes testing");

var handler = function(request, response) {
  console.log("Received request from " + request.connection.remoteAddress);
  response.writeHead(200);
  response.end("Container runs on: " + os.hostname() + "\n");
};

var www = http.createServer(handler);
www.listen(8080);

Dockerfile
#

FROM node:lts-alpine3.19
Copy app.js /app.js
EXPOSE 8080
ENTRYPOINT ["node", "app.js"]

Build & Push the Docker Image
#

# Build the image
docker build -t jueklu/container-2 .

# Push the image to the registry
docker push jueklu/container-2

Test the Image
#

# Run the container
docker run -d --name container-2 -p 8080:8080 jueklu/container-2

# Use curl to check the container host
curl localhost:8080
# Optional: Open container terminal
docker exec -it container-2 /bin/ash

# Verify the container hostname
hostname

Prerequisites: Packages
#

AWS CLIv2
#

Install AWS CLIv2
#

# Install AWS CLI version 2
sudo apt install curl zip -y &&
cd /tmp &&
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" &&
unzip awscliv2.zip &&
sudo ./aws/install
# Verify / check version
aws --version

IAM User & Policies
#

Create an IAM user, create access keys for the user and add the following managed policy AdministratorAccess. In a production environment only the least necessary permissions should be granted!

Configure AWS CLIv2
#

# Add the IAM user access key, secret access key & define the default region
aws configure

Install Eksctl
#

Eksctl is a command-line tool for creating and managing Kubernetes clusters on EKS.

curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp &&
sudo mv /tmp/eksctl /usr/local/bin
# Verify / check version
eksctl version

Install Kubectl
#

# Install Kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" &&
chmod +x kubectl &&
sudo mv kubectl /usr/local/bin/
# Verify installation
kubectl version --client  

EKS Cluster
#

Create Cluster
#

# Create an EKS cluster: Default region
eksctl create cluster \
  --name my-cluster \
  --nodegroup-name linux-nodes \
  --node-type t3.micro \
  --nodes 3

# Create an EKS cluster: Define Kubernetes version & region
eksctl create cluster \
  --name my-cluster \
  --version 1.29 \
  --region us-east-1 \
  --nodegroup-name linux-nodes \
  --node-type t3.micro \
  --nodes 3

More Options:

  • --node-volume-size 20 Define the volume size of the worker nodes

Note: Creating the necessary CloudFormation resources can take up to 10 - 20 minutes.

List Resources in AWS Management Console
#

# EKS Cluster
https://eu-central-1.console.aws.amazon.com/eks/

# Cloudformation stack
https://eu-central-1.console.aws.amazon.com/cloudformation

List Nodes
#

# List nodes
kubectl get nodes

# Shell output:
NAME                                              STATUS   ROLES    AGE   VERSION
ip-192-168-17-98.eu-central-1.compute.internal    Ready    <none>   14m   v1.29.0-eks-5e0fdde
ip-192-168-55-216.eu-central-1.compute.internal   Ready    <none>   14m   v1.29.0-eks-5e0fdde
ip-192-168-71-245.eu-central-1.compute.internal   Ready    <none>   14m   v1.29.0-eks-5e0fdde
# List nodes: More details
kubectl get nodes -o wide

# Shell output:
NAME                                              STATUS   ROLES    AGE   VERSION               INTERNAL-IP      EXTERNAL-IP    OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
ip-192-168-17-98.eu-central-1.compute.internal    Ready    <none>   14m   v1.29.0-eks-5e0fdde   192.168.17.98    3.72.87.1      Amazon Linux 2   5.10.213-201.855.amzn2.x86_64   containerd://1.7.11
ip-192-168-55-216.eu-central-1.compute.internal   Ready    <none>   14m   v1.29.0-eks-5e0fdde   192.168.55.216   3.75.229.65    Amazon Linux 2   5.10.213-201.855.amzn2.x86_64   containerd://1.7.11
ip-192-168-71-245.eu-central-1.compute.internal   Ready    <none>   13m   v1.29.0-eks-5e0fdde   192.168.71.245   52.28.67.132   Amazon Linux 2   5.10.213-201.855.amzn2.x86_64   containerd://1.7.11

List & Delete Clusters
#

# List all cluster: Define region
eksctl get cluster --region eu-central-1

# Shell output:
NAME            REGION          EKSCTL CREATED
my-cluster      eu-central-1    True
# Delete Cluster: Define name & region
eksctl delete cluster --name my-cluster --region eu-central-1

Create HTTP Deployment
#

Deploy Pod & Load Balancer
#

# Create a Deployment
kubectl create deployment my-container --image=jueklu/container-2

# Create a load balancer service for the deployment
kubectl expose deployment my-container --type=LoadBalancer --port=8080 --target-port=8080 --name my-container-http

Scale the Deployment
#

# Scale the Deployment
kubectl scale deployment my-container --replicas=3
# List the deployed pods
kubectl get pods

# Shell output:
NAME                            READY   STATUS    RESTARTS   AGE
my-container-69f894487d-jfbzn   1/1     Running   0          97s
my-container-69f894487d-rw9xz   1/1     Running   0          115s
my-container-69f894487d-zc86s   1/1     Running   0          97s

List External IP
#

# List load balancer service details: List external IP
kubectl get svc my-container-http

# Shell output:
NAME                TYPE           CLUSTER-IP       EXTERNAL-IP                                                                  PORT(S)          AGE
my-container-http   LoadBalancer   10.100.145.106   a6f48ac532c55486b88dc421a8671a91-1612496629.eu-central-1.elb.amazonaws.com   8080:32477/TCP   3m29s

Test the Deployment
#

The AWS Elastic Load Balancer (ELB) URL randomly hit different pods and should output the different container hostnames when it’s curled:

# Test the deployment
curl a6f48ac532c55486b88dc421a8671a91-1612496629.eu-central-1.elb.amazonaws.com:8080

# Shell output:
Container runs on: my-container-69f894487d-jfbzn

# Test the deployment
curl a6f48ac532c55486b88dc421a8671a91-1612496629.eu-central-1.elb.amazonaws.com:8080

# Shell output:
Container runs on: my-container-69f894487d-zc86s

# Test the deployment
curl a6f48ac532c55486b88dc421a8671a91-1612496629.eu-central-1.elb.amazonaws.com:8080

# Shell output:
Container runs on: my-container-69f894487d-rw9xz

Delete the Deployment
#

# Delete the Deployment
kubectl delete deployment my-container

# Delete the load balancer service
kubectl delete svc my-container-http

Create HTTPS Deployment
#

Note: For this deployment I use a wildcard certificate *.jklug.work from the AWS certificate manager.

Deploy Pod: CLI
#

# Create a Deployment
kubectl create deployment my-container --image=jueklu/container-2

Note: Kubernetes automatically uses the deployment name as a key for a label named “app”.

# Scale the Deployment
kubectl scale deployment my-container --replicas=3
# List the deployed pods
kubectl get pods

# Shell output:
NAME                            READY   STATUS    RESTARTS   AGE
my-container-69f894487d-pcqbj   1/1     Running   0          59m
my-container-69f894487d-w2dsb   1/1     Running   0          2m27s
my-container-69f894487d-wsf7m   1/1     Running   0          2m27s

Deploy Pod: YML
#

vi my-container.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-container
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-container
  template:
    metadata:
      labels:
        app: my-container
    spec:
      containers:
      - name: container-2
        image: jueklu/container-2
        ports:
        - containerPort: 8080
# Apply the deployment
kubectl apply -f my-container.yml

LoadBalancer Service
#

# Create yml file
vi loadbalancer.yml
apiVersion: v1
kind: Service
metadata:
  name: my-container-https
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:...
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
spec:
  selector:
    app: my-container
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8080
  type: LoadBalancer
  • service.beta.kubernetes.io/aws-load-balancer-ssl-cert: Define the ARN of the TLS certificate from AWS Certificate Manager (ACM)
# Apply the deployment
kubectl apply -f loadbalancer.yml

List External DNS Name
#

Note: The “EXTERNAL-IP” / DNS name of a load balancer service is listed as “” it’s meaning there is a problem with the configuration. It should only take a view second till the extern IP is shown.

# List load balancer service details: List DNS name
kubectl get svc my-container-https

# Shell output:
NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP                                                              PORT(S)                      AGE
my-container-https   LoadBalancer   10.100.41.226   abcfad6f62e1a42bba95c282eea0c998-672012549.us-east-1.elb.amazonaws.com   80:31022/TCP,443:32030/TCP   37s

Create CNAME DNS Entry
#

Manually
#

Create a Route 53 DNS entry with the following values:

  • Record name: For example eks.jklug.work

  • Record type: CNAME

  • Value: abcfad6f62e1a42bba95c282eea0c998-672012549.us-east-1.elb.amazonaws.com

  • TTL: 300

AWS CLI
#

# Identify the hosted zone ID
aws route53 list-hosted-zones

# Shell output:
{
    "HostedZones": [
        {
            "Id": "/hostedzone/...",
            "Name": "jklug.work.",
            "CallerReference": "...",
            "Config": {
                "Comment": "",
                "PrivateZone": false
            },
            "ResourceRecordSetCount": 18
        },
    ]
}
  • Create a JSON file for the DNS record
vi cname-record.json
{
  "Comment": "Create CNAME record for custom domain",
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "eks.jklug.work",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [
          {
            "Value": "abcfad6f62e1a42bba95c282eea0c998-672012549.us-east-1.elb.amazonaws.com"
          }
        ]
      }
    }
  ]
}
  • Action: "UPSERT" Create the record if it doesn’t exist or update it if it does.
# Create the record
aws route53 change-resource-record-sets --hosted-zone-id /hostedzone/... --change-batch file://cname-record.json

Test the Deployment
#

The AWS Elastic Load Balancer (ELB) URL randomly hit different pods and should output the different container hostnames when it’s curled:

# Test the deployment
curl eks.jklug.work

# Shell output:
Container runs on: my-container-69f894487d-pcqbj

# Test the deployment
curl eks.jklug.work

# Shell output:
Container runs on: my-container-69f894487d-wsf7m

Delete the Deployment
#

# Delete the Deployment
kubectl delete deployment my-container

# Delete the load balancer service
kubectl delete svc my-container-https

Troubleshooting
#

# List logs
kubectl get events --sort-by=.metadata.creationTimestamp

# List load balancer details
aws elb describe-load-balancers --region us-east-1

# Retrieves headers / TLS details
curl -Iv https://abcfad6f62e1a42bba95c282eea0c998-672012549.us-east-1.elb.amazonaws.com

Links #

# Install AWS CLIv2
https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html

# Install AWS Eksctl
https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-eksctl.html


# Create Cluster
https://eksctl.io/usage/creating-and-managing-clusters/

# Create Cluster
https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html
Kubernetes-Cluster - This article is part of a series.
Part 4: This Article