Kubernetes Cluster on Multi-Cloud using Terraform and Ansible

Kubernetes

Kubernetes Cluster

https://www.linkedin.com/pulse/configure-kubernetes-cluster-using-ansible-role-aditya-raj/

Terraform

Ansible

Prerequisites

Install Terraform:

$ sudo yum install -y yum-utils
$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
$ sudo yum -y install terraform

Install Ansible:

pip3 install ansible

Authenticating to AWS

aws configure --profile aditya
# Configure the AWS Provider
provider "aws" {
region = "ap-south-1"
profile = "aditya"
}
https://registry.terraform.io/providers/hashicorp/aws/latest/docs

Authenticating to Azure

az login
az account list
az account set –subscription =” SUBSCRIPTION_ID"
# Configure the Microsoft Azure Provider
provider "azurerm" {
features {}
}
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/azure_cli
# Create (and display) an SSH key
resource "tls_private_key" "k8s_ssh" {
algorithm = "RSA"
rsa_bits = 4096
}
# Create local key
resource "local_file" "keyfile" {
content = tls_private_key.k8s_ssh.private_key_pem
filename = "terraform_key.pem"
file_permission = "0400"
}
# Terraform AWS provider
provider "aws" {
profile = "aditya"
region = "ap-south-1"
}
# Provides EC2 key pair
resource "aws_key_pair" "terraformkey" {
key_name = "terraform_key"
public_key = tls_private_key.k8s_ssh.public_key_openssh
}
# Create VPC
resource "aws_vpc" "k8s_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames=true
enable_dns_support =true
tags = {
Name = "K8S VPC"
}
}
# Create Subnet
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.k8s_vpc.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-south-1a"
tags = {
Name = "Public Subnet"
}
}
# Create Internet Gateway
resource "aws_internet_gateway" "k8s_gw" {
vpc_id = aws_vpc.k8s_vpc.id
tags = {
Name = "K8S GW"
}
}
# Create Routing table
resource "aws_route_table" "k8s_route" {
vpc_id = aws_vpc.k8s_vpc.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.k8s_gw.id
}

tags = {
Name = "K8S Route"
}
}
# Associate Routing table
resource "aws_route_table_association" "k8s_asso" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.k8s_route.id
}
# Create security group
resource "aws_security_group" "allow_ssh_http" {
name = "Web_SG"
description = "Allow SSH and HTTP inbound traffic"
vpc_id = aws_vpc.k8s_vpc.id
ingress {
description = "Allow All"
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = [ "0.0.0.0/0" ]
}
ingress {
description = "Allow All"
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = [ "0.0.0.0/0" ]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "K8S SG"
}
}
# Launch EC2 instnace for Master Node
resource "aws_instance" "k8s" {
ami = "ami-010aff33ed5991201"
instance_type = "t2.micro"
key_name = aws_key_pair.terraformkey.key_name
associate_public_ip_address = true
subnet_id = aws_subnet.public_subnet.id
vpc_security_group_ids = [ aws_security_group.allow_ssh_http.id ]
tags = {
Name = "Master Node"
}
}
# Configure the Microsoft Azure Provider
provider "azurerm" {

features {}
}
variable "n" {
type = number
description = "No. of Worker Node"
}
# Create a resource group if it doesn't exist
resource "azurerm_resource_group" "myk8sgroup" {
name = "k8sResourceGroup"
location = "Central India"
tags = {
environment = "K8s Resource Group"
}
}
# Create virtual network
resource "azurerm_virtual_network" "myk8snetwork" {
name = "k8sVnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.myk8sgroup.location
resource_group_name = azurerm_resource_group.myk8sgroup.name
tags = {
environment = "K8s VN"
}
}
# Create subnet
resource "azurerm_subnet" "myk8ssubnet" {
name = "k8sSubnet"
resource_group_name = azurerm_resource_group.myk8sgroup.name
virtual_network_name = azurerm_virtual_network.myk8snetwork.name
address_prefixes = ["10.0.1.0/24"]
}
# Create public IPs
resource "azurerm_public_ip" "myk8spublicip" {
count = var.n
name = "k8sPublicIP${count.index}"
location = azurerm_resource_group.myk8sgroup.location
resource_group_name = azurerm_resource_group.myk8sgroup.name
allocation_method = "Dynamic"
tags = {
environment = "K8s Public IP"
}
}
# Create Network Security Group and rule
resource "azurerm_network_security_group" "myk8snsg" {
name = "k8sNetworkSecurityGroup"
location = azurerm_resource_group.myk8sgroup.location
resource_group_name = azurerm_resource_group.myk8sgroup.name
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
tags = {
environment = "K8s Security Group"
}
}
# Create network interface
resource "azurerm_network_interface" "myk8snic" {
count = var.n
name = "k8sNIC${count.index}"
location = azurerm_resource_group.myk8sgroup.location
resource_group_name = azurerm_resource_group.myk8sgroup.name
ip_configuration {
name = "myNicConfiguration${count.index}"
subnet_id = azurerm_subnet.myk8ssubnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = "${element(azurerm_public_ip.myk8spublicip.*.id, count.index)}"
}
tags = {
environment = "K8s NIC"
}
}
# Connect the security group to the network interface
resource "azurerm_network_interface_security_group_association" "myk8ssga" {
count = 2
network_interface_id = "${element(azurerm_network_interface.myk8snic.*.id, count.index)}"
network_security_group_id = azurerm_network_security_group.myk8snsg.id
}
# Create virtual machine for Worker node
resource "azurerm_linux_virtual_machine" "myk8svm" {
count = var.n
name = "k8sVM${count.index}"
location = azurerm_resource_group.myk8sgroup.location
resource_group_name = azurerm_resource_group.myk8sgroup.name
network_interface_ids = ["${element(azurerm_network_interface.myk8snic.*.id, count.index)}"]
size = "Standard_DS1_v2"
os_disk {
name = "myOsDisk${count.index}"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "RedHat"
offer = "RHEL"
sku = "8.2"
version = "latest"
}
computer_name = "WorkerNode${count.index}"
admin_username = "ansible"
disable_password_authentication = true
admin_ssh_key {
username = "ansible"
public_key = tls_private_key.k8s_ssh.public_key_openssh
}
tags = {
environment = "Worker_Node"
}
}
# Update Ansible inventory
resource "local_file" "ansible_host" {
depends_on = [
aws_instance.k8s
]
count = var.n
content = "[Master_Node]\n${aws_instance.k8s.public_ip}\n\n[Worker_Node]\n${join("\n", azurerm_linux_virtual_machine.myk8svm.*.public_ip_address)}"
filename = "inventory"
}
# Run Ansible playbook
resource "null_resource" "null1" {
depends_on = [
local_file.ansible_host
]
provisioner "local-exec" {
command = "sleep 60"
}
provisioner "local-exec" {
command = "ansible-playbook playbook.yml"
}
}# Print K8s Master and Worker node IP
output "Master_Node_IP" {
value = aws_instance.k8s.public_ip}
output "Worker_Node_IP" { value = join(", ", azurerm_linux_virtual_machine.myk8svm.*.public_ip_address)
}
- name: Configure K8s Master Node
hosts: Master_Node
remote_user: ec2-user
roles:
- role: kubernetes_master
- name: Configure K8s Worker Node
hosts: Worker_Node
remote_user: ansible
roles:
- role: kubernetes_worker
🔴 GitHub Links:✔️ kubernetes_master: https://lnkd.in/eg-v_36✔️ kubernetes_worker: https://lnkd.in/eqBVTbH🔴 Ansible Galaxy Links:✔️ kubernetes_master: https://lnkd.in/eBwQ928
✔️ kubernetes_worker: https://lnkd.in/eJJFAQn

📍Note:

kubeadm init --control-plane-endpoint {{ groups['Master_Node'][0] }}:6443 --pod-network-cidr=10.244.0.0/16 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem

Execute the terraform code:

terraform init
terraform plan
terraform apply

Infrastructure created by Terraform in AWS:

Infrastructure created by Terraform in Azure:

terraform destroy --auto-approve

Thank You for reading!! 😇😇

I'm passionate learner diving into the concepts of computing 💻