
Terraform configuration example that creates Kubernetes cluster (Bare Metal) on AWS EC2. Creates Ingress with NodePort. IP addresses Ingress nodes.
This template creates the following EC2 instances:
- 1 manager
- 2 workers
- 2 ingresses
variables.tf
variable "REGION" {
default = "us-east-1"
}
variable "PROJECT_NAME" {
default = "artem_k8s"
}
variable "SSH_USER" {
default = "ubuntu"
}
variable "SSH_KEY_NAME" {
default = "artem.gatchenko"
}
variable "SSH_KEY_PATH" {
default = "/home/artem/.ssh/id_rsa"
}
variable "VPC_SUBNET" {
default = "192.168.1.0/24"
}
variable "INSTANCE_TYPE" {
default = "t2.micro"
}
variable "WORKER_NUMBER" {
default = "2"
}
main.tf
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "vpc"
region = "${var.REGION}"
}
vpc.tf
// CREATE VPC
resource "aws_vpc" "vpc" {
cidr_block = "${var.VPC_SUBNET}"
enable_dns_hostnames = "true"
enable_dns_support = "true"
tags {
Name = "${var.PROJECT_NAME}"
}
}
// CREATE GATEWAY
resource "aws_internet_gateway" "vpc" {
vpc_id = "${aws_vpc.vpc.id}"
tags {
Name = "${var.PROJECT_NAME}"
}
}
// CREATE ROUTE TABLE
resource "aws_route_table" "vpc" {
vpc_id = "${aws_vpc.vpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.vpc.id}"
}
tags {
Name = "${var.PROJECT_NAME}"
}
}
// CREATE SUBNET
resource "aws_subnet" "vpc" {
vpc_id = "${aws_vpc.vpc.id}"
cidr_block = "${var.VPC_SUBNET}"
map_public_ip_on_launch = "true"
tags {
Name = "${var.PROJECT_NAME}"
}
}
resource "aws_route_table_association" "vpc" {
subnet_id = "${aws_subnet.vpc.id}"
route_table_id = "${aws_route_table.vpc.id}"
}
// CREATE SECURITY GROUP
resource "aws_security_group" "vpc" {
vpc_id = "${aws_vpc.vpc.id}"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow input SSH"
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow input HTTP"
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow input HTTPS"
}
ingress {
from_port = 0
to_port = 0
protocol = "-1"
self = true
description = "Allow triffic between instances in SG"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all ouput traffic"
}
tags {
Name = "${var.PROJECT_NAME}"
Description = "${var.PROJECT_NAME}"
}
}
instance_master.tf
// CREATE INSTANCE FOR KUBERNETES MASTER
resource "aws_instance" "master" {
ami = "ami-0ac019f4fcb7cb7e6"
instance_type = "${var.INSTANCE_TYPE}"
key_name = "${var.SSH_KEY_NAME}"
vpc_security_group_ids = ["${aws_security_group.vpc.id}"]
subnet_id = "${aws_subnet.vpc.id}"
associate_public_ip_address = true
source_dest_check = false
connection {
type = "ssh"
user = "${var.SSH_USER}"
private_key = "${file("${var.SSH_KEY_PATH}")}"
}
provisioner "file" {
source = "k8s.sh"
destination = "/tmp/k8s.sh"
}
provisioner "file" {
source = "ingress-nginx.yml"
destination = "/tmp/ingress-nginx.yml"
}
provisioner "remote-exec" {
inline = [
"sudo chmod +x /tmp/k8s.sh",
"sudo /tmp/k8s.sh",
"sudo kubeadm init --apiserver-advertise-address=${aws_instance.master.private_ip} --pod-network-cidr=10.244.0.0/16 --ignore-preflight-errors SystemVerification --ignore-preflight-errors NumCPU",
"mkdir -p $HOME/.kube",
"sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config",
"sudo chown $(id -u):$(id -g) $HOME/.kube/config",
"echo 'source <(kubectl completion bash)' >> $HOME/.bashrc",
"kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml"
]
}
provisioner "local-exec" {
command = "ssh -o StrictHostKeyChecking=no ubuntu@${aws_instance.master.public_ip} -i ${var.SSH_KEY_PATH} 'kubeadm token create --print-join-command' > token"
}
provisioner "local-exec" {
command = "if [ -e ip_ingresses ]; then rm -rf ip_ingresses; fi"
}
tags {
Name = "${var.PROJECT_NAME}_master"
}
}
instance_workers.tf
// CREATE INSTANCE FOR KUBERNETES WORKERS
resource "aws_instance" "worker" {
depends_on = ["aws_instance.master"]
count = "${var.WORKER_NUMBER}"
ami = "ami-0ac019f4fcb7cb7e6"
instance_type = "${var.INSTANCE_TYPE}"
key_name = "${var.SSH_KEY_NAME}"
vpc_security_group_ids = ["${aws_security_group.vpc.id}"]
subnet_id = "${aws_subnet.vpc.id}"
associate_public_ip_address = true
source_dest_check = false
connection {
type = "ssh"
user = "${var.SSH_USER}"
private_key = "${file("${var.SSH_KEY_PATH}")}"
}
provisioner "file" {
source = "k8s.sh"
destination = "/tmp/k8s.sh"
}
provisioner "file" {
source = "token"
destination = "/tmp/token"
}
provisioner "remote-exec" {
inline = [
"sleep 15",
"sudo chmod +x /tmp/k8s.sh",
"sudo /tmp/k8s.sh",
"sudo `cat /tmp/token` --ignore-preflight-errors=SystemVerification"
]
}
tags {
Name = "${var.PROJECT_NAME}_worker"
}
}
instance_ingresses.tf
// CREATE INSTANCE FOR KUBERNETES WORKERS
resource "aws_instance" "ingress" {
depends_on = ["aws_instance.master"]
# depends_on = [
# "aws_instance.master",
# "aws_instance.ingress",
# ]
count = "2"
ami = "ami-0ac019f4fcb7cb7e6"
instance_type = "${var.INSTANCE_TYPE}"
key_name = "${var.SSH_KEY_NAME}"
vpc_security_group_ids = ["${aws_security_group.vpc.id}"]
subnet_id = "${aws_subnet.vpc.id}"
associate_public_ip_address = true
source_dest_check = false
connection {
type = "ssh"
user = "${var.SSH_USER}"
private_key = "${file("${var.SSH_KEY_PATH}")}"
}
provisioner "local-exec" {
command = "echo ${self.private_ip} >> ip_ingresses"
}
provisioner "file" {
source = "k8s.sh"
destination = "/tmp/k8s.sh"
}
provisioner "file" {
source = "token"
destination = "/tmp/token"
}
provisioner "remote-exec" {
inline = [
"sleep 15",
"sudo chmod +x /tmp/k8s.sh",
"sudo /tmp/k8s.sh",
"sudo `cat /tmp/token` --ignore-preflight-errors=SystemVerification"
]
}
tags {
Name = "${var.PROJECT_NAME}_ingress"
}
}
ingress.tf
resource "null_resource" "ingress" {
triggers = {
instance_id = "${aws_instance.master.id}"
}
connection {
type = "ssh"
user = "${var.SSH_USER}"
host = "${aws_instance.master.public_ip}"
agent = false
private_key = "${file("${var.SSH_KEY_PATH}")}"
}
provisioner "file" {
source = "ip_ingresses"
destination = "/tmp/ip_ingresses"
}
provisioner "remote-exec" {
inline = [
"sleep 15",
"kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml",
"IP_INGRESS_1=`sed -n 1p /tmp/ip_ingresses`",
"IP_INGRESS_2=`sed -n 2p /tmp/ip_ingresses`",
"sed -i \"s/IP_INGRESS_1/$IP_INGRESS_1/g\" /tmp/ingress-nginx.yml",
"sed -i \"s/IP_INGRESS_2/$IP_INGRESS_2/g\" /tmp/ingress-nginx.yml",
"INGRESS_NAME_1=ip-$(cat /tmp/ip_ingresses | sed -n 1p | sed 's/\\./-/g')",
"INGRESS_NAME_2=ip-$(cat /tmp/ip_ingresses | sed -n 2p | sed 's/\\./-/g')",
"kubectl label node $INGRESS_NAME_1 node-role.kubernetes.io/ingress=ingress",
"kubectl label node $INGRESS_NAME_2 node-role.kubernetes.io/ingress=ingress",
"kubectl apply -f /tmp/ingress-nginx.yml"
]
}
depends_on = [
"aws_instance.master",
"aws_instance.ingress",
]
}
ingress-nginx.yml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
externalIPs:
- IP_INGRESS_1
- IP_INGRESS_2
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
k8s.sh
#!/bin/bash # THIS SCRIPT FOR INSTALL K8S. SCRIPT FOR UBUNTU # INSTALL DOCKER apt update apt install -y apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" apt update apt install -y docker-ce systemctl enable docker systemctl start docker # INSTALL KUBERNETES curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - cat <<EOF >/etc/apt/sources.list.d/kubernetes.list deb http://apt.kubernetes.io/ kubernetes-xenial main EOF apt update apt install -y kubelet kubeadm kubectl systemctl enable kubelet systemctl start kubelet
output.tf
output "aws-k8s-ingresses-ip" {
value = "${aws_instance.ingress.*.public_ip}"
}
Download all one archive can be here.
How to run the Terraform template:
terraform init terraform plan terraform apply