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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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
1 2 3 4 5 6 7 8 | provider "aws" { region = "us-east-1" } provider "aws" { alias = "vpc" region = "${var.REGION}" } |
vpc.tf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | // 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | // 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | // 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #!/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
1 2 3 | output "aws-k8s-ingresses-ip" { value = "${aws_instance.ingress.*.public_ip}" } |
Download all one archive can be here.
How to run the Terraform template:
1 2 3 | terraform init terraform plan terraform apply |