Deploying Kubernetes from Scratch with Terraform: A Step-by-Step Guide

Abhimanyubajaj
3 min readMar 19, 2023

--

In today’s rapidly evolving cloud-native ecosystem, Kubernetes has emerged as a prominent platform for orchestrating containerized applications. While managed Kubernetes services simplify deployment and management, some organizations prefer to have full control over their clusters by deploying them from scratch. In this tutorial, we will walk you through the process of deploying a Kubernetes cluster from scratch using Terraform, a popular Infrastructure-as-Code (IaC) tool.

Prerequisites:

  • Basic understanding of Kubernetes and its components
  • Familiarity with Terraform and IaC concepts
  • A cloud provider account (e.g., AWS, GCP, Azure)
  • Terraform CLI installed on your local machine

First, let’s create a script file to install all necessary components along with Calico for networking. The script file will look like this:

#!/bin/bash
# Install Docker
sudo yum update -y
sudo amazon-linux-extras install docker -y
sudo service docker start
sudo usermod -a -G docker ec2-user

# Install kubeadm, kubelet, and kubectl
sudo tee /etc/yum.repos.d/kubernetes.repo <<EOF2
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF2
sudo yum install -y kubelet kubeadm kubectl

# Initialize Kubernetes
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# Install Calico network plugin
kubectl apply -f https://docs.projectcalico.org/v3.16/manifests/calico.yaml

This script installs all the necessary components along with Calico for networking.

Next, have this script file in the same folder as your Terraform template, which will look like this:

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
##### Adding parameters
}
}

required_version = ">= 0.13"
}

provider "aws" {
region = "us-east-1" # Change this to your preferred region
profile = "XXXXXXX"
shared_config_files = ["/Users/XXXXX/.aws/config"]
}

data "aws_ami" "amazon_linux" {
most_recent = true

filter {
name = "name"
values = ["amzn2-ami-hvm-2.0.*-x86_64-gp2"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}
}

resource "aws_instance" "kubernetes" {
ami = data.aws_ami.amazon_linux.id # Amazon Linux 2
instance_type = "t2.medium"
key_name = "XXXXXXX"
vpc_security_group_ids = [aws_security_group.allow_web_kubernetes.id]
user_data = data.template_file.startup.rendered
root_block_device {
delete_on_termination = true
volume_type = "gp2"
volume_size = 20
}

tags = {
Name = "kubernetes"
}

}


data "template_file" "startup" {
template = file("script.sh")
}

resource "aws_security_group" "allow_web_kubernetes" {
name = "security_group_for_services_ec2_kubernetes"
description = "Allows access to Web Port"

#allow http

ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["XXXXXXX/32"]
}

# allow https

ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["XXXXXXXX/32"]
}

# allow SSH

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["XXXXXXXX/32"]
}

ingress {
from_port = 0
to_port = 0
protocol = "all"
cidr_blocks = ["XXXXXXXX/32"]
}


#all outbound

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Owner = "XXXXXX"
}
lifecycle {
create_before_destroy = true
}

}

To deploy a nginx pod on your single control plane node, you will need to set tolerations to bypass the taint applied to it. You can do this by running the following command:

kubectl run my-pod --image=nginx --overrides='
{
"apiVersion": "v1",
"spec": {
"tolerations": [
{
"key": "node-role.kubernetes.io/control-plane",
"operator": "Exists",
"effect": "NoSchedule"
}
]
}
}'

This command will deploy a new pod named “my-pod” with the nginx image and apply the tolerations specified in the overrides section. The toleration with key “node-role.kubernetes.io/control-plane” and effect “NoSchedule” will allow the pod to be scheduled on the single control plane node despite the taint applied to it.

With these steps, you should now be able to deploy and run pods on your Kubernetes cluster running on a single EC2 instance.

For more information or in case you get stuck, Feel free to connect with me on LinkedIn

https://www.linkedin.com/in/theabhibajaj/

--

--

Abhimanyubajaj

I solve problems. CKAD, CKA, Azure, AWS, GCP, Terraform Certified. Senior Software Engineer at Cisco.