diff --git a/contrib/terraform/aws/.gitignore b/contrib/terraform/aws/.gitignore index 28901e146c00f9876132ca1cff35186e7c7e6b6c..84fcb582197bef46cdb0e87098fb194856f97f52 100644 --- a/contrib/terraform/aws/.gitignore +++ b/contrib/terraform/aws/.gitignore @@ -1,2 +1,2 @@ *.tfstate* -inventory +.terraform diff --git a/contrib/terraform/aws/00-create-infrastructure.tf b/contrib/terraform/aws/00-create-infrastructure.tf deleted file mode 100755 index 09cfac37c2b6533af6909b67daf539e17dd2c98d..0000000000000000000000000000000000000000 --- a/contrib/terraform/aws/00-create-infrastructure.tf +++ /dev/null @@ -1,261 +0,0 @@ -variable "deploymentName" { - type = "string" - description = "The desired name of your deployment." -} - -variable "numControllers"{ - type = "string" - description = "Desired # of controllers." -} - -variable "numEtcd" { - type = "string" - description = "Desired # of etcd nodes. Should be an odd number." -} - -variable "numNodes" { - type = "string" - description = "Desired # of nodes." -} - -variable "volSizeController" { - type = "string" - description = "Volume size for the controllers (GB)." -} - -variable "volSizeEtcd" { - type = "string" - description = "Volume size for etcd (GB)." -} - -variable "volSizeNodes" { - type = "string" - description = "Volume size for nodes (GB)." -} - -variable "subnet" { - type = "string" - description = "The subnet in which to put your cluster." -} - -variable "securityGroups" { - type = "string" - description = "The sec. groups in which to put your cluster." -} - -variable "ami"{ - type = "string" - description = "AMI to use for all VMs in cluster." -} - -variable "SSHKey" { - type = "string" - description = "SSH key to use for VMs." -} - -variable "master_instance_type" { - type = "string" - description = "Size of VM to use for masters." -} - -variable "etcd_instance_type" { - type = "string" - description = "Size of VM to use for etcd." -} - -variable "node_instance_type" { - type = "string" - description = "Size of VM to use for nodes." -} - -variable "terminate_protect" { - type = "string" - default = "false" -} - -variable "awsRegion" { - type = "string" -} - -provider "aws" { - region = "${var.awsRegion}" -} - -variable "iam_prefix" { - type = "string" - description = "Prefix name for IAM profiles" -} - -resource "aws_iam_instance_profile" "kubernetes_master_profile" { - name = "${var.iam_prefix}_kubernetes_master_profile" - roles = ["${aws_iam_role.kubernetes_master_role.name}"] -} - -resource "aws_iam_role" "kubernetes_master_role" { - name = "${var.iam_prefix}_kubernetes_master_role" - assume_role_policy = <<EOF -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { "Service": "ec2.amazonaws.com"}, - "Action": "sts:AssumeRole" - } - ] -} -EOF -} - -resource "aws_iam_role_policy" "kubernetes_master_policy" { - name = "${var.iam_prefix}_kubernetes_master_policy" - role = "${aws_iam_role.kubernetes_master_role.id}" - policy = <<EOF -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": ["ec2:*"], - "Resource": ["*"] - }, - { - "Effect": "Allow", - "Action": ["elasticloadbalancing:*"], - "Resource": ["*"] - }, - { - "Effect": "Allow", - "Action": "s3:*", - "Resource": "*" - } - ] -} -EOF -} - -resource "aws_iam_instance_profile" "kubernetes_node_profile" { - name = "${var.iam_prefix}_kubernetes_node_profile" - roles = ["${aws_iam_role.kubernetes_node_role.name}"] -} - -resource "aws_iam_role" "kubernetes_node_role" { - name = "${var.iam_prefix}_kubernetes_node_role" - assume_role_policy = <<EOF -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { "Service": "ec2.amazonaws.com"}, - "Action": "sts:AssumeRole" - } - ] -} -EOF -} - -resource "aws_iam_role_policy" "kubernetes_node_policy" { - name = "${var.iam_prefix}_kubernetes_node_policy" - role = "${aws_iam_role.kubernetes_node_role.id}" - policy = <<EOF -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "s3:*", - "Resource": "*" - }, - { - "Effect": "Allow", - "Action": "ec2:Describe*", - "Resource": "*" - }, - { - "Effect": "Allow", - "Action": "ec2:AttachVolume", - "Resource": "*" - }, - { - "Effect": "Allow", - "Action": "ec2:DetachVolume", - "Resource": "*" - } - ] -} -EOF -} - -resource "aws_instance" "master" { - count = "${var.numControllers}" - ami = "${var.ami}" - instance_type = "${var.master_instance_type}" - subnet_id = "${var.subnet}" - vpc_security_group_ids = ["${var.securityGroups}"] - key_name = "${var.SSHKey}" - disable_api_termination = "${var.terminate_protect}" - iam_instance_profile = "${aws_iam_instance_profile.kubernetes_master_profile.id}" - root_block_device { - volume_size = "${var.volSizeController}" - } - tags { - Name = "${var.deploymentName}-master-${count.index + 1}" - } -} - -resource "aws_instance" "etcd" { - count = "${var.numEtcd}" - ami = "${var.ami}" - instance_type = "${var.etcd_instance_type}" - subnet_id = "${var.subnet}" - vpc_security_group_ids = ["${var.securityGroups}"] - key_name = "${var.SSHKey}" - disable_api_termination = "${var.terminate_protect}" - root_block_device { - volume_size = "${var.volSizeEtcd}" - } - tags { - Name = "${var.deploymentName}-etcd-${count.index + 1}" - } -} - - -resource "aws_instance" "minion" { - count = "${var.numNodes}" - ami = "${var.ami}" - instance_type = "${var.node_instance_type}" - subnet_id = "${var.subnet}" - vpc_security_group_ids = ["${var.securityGroups}"] - key_name = "${var.SSHKey}" - disable_api_termination = "${var.terminate_protect}" - iam_instance_profile = "${aws_iam_instance_profile.kubernetes_node_profile.id}" - root_block_device { - volume_size = "${var.volSizeNodes}" - } - tags { - Name = "${var.deploymentName}-minion-${count.index + 1}" - } -} - -output "kubernetes_master_profile" { - value = "${aws_iam_instance_profile.kubernetes_master_profile.id}" -} - -output "kubernetes_node_profile" { - value = "${aws_iam_instance_profile.kubernetes_node_profile.id}" -} - -output "master-ip" { - value = "${join(", ", aws_instance.master.*.private_ip)}" -} - -output "etcd-ip" { - value = "${join(", ", aws_instance.etcd.*.private_ip)}" -} - -output "minion-ip" { - value = "${join(", ", aws_instance.minion.*.private_ip)}" -} - - diff --git a/contrib/terraform/aws/01-create-inventory.tf b/contrib/terraform/aws/01-create-inventory.tf deleted file mode 100755 index ce56521810344e462fa5ae183fd7b00ea2ebd650..0000000000000000000000000000000000000000 --- a/contrib/terraform/aws/01-create-inventory.tf +++ /dev/null @@ -1,37 +0,0 @@ -variable "SSHUser" { - type = "string" - description = "SSH User for VMs." -} - -resource "null_resource" "ansible-provision" { - - depends_on = ["aws_instance.master","aws_instance.etcd","aws_instance.minion"] - - ##Create Master Inventory - provisioner "local-exec" { - command = "echo \"[kube-master]\" > inventory" - } - provisioner "local-exec" { - command = "echo \"${join("\n",formatlist("%s ansible_ssh_user=%s", aws_instance.master.*.private_ip, var.SSHUser))}\" >> inventory" - } - - ##Create ETCD Inventory - provisioner "local-exec" { - command = "echo \"\n[etcd]\" >> inventory" - } - provisioner "local-exec" { - command = "echo \"${join("\n",formatlist("%s ansible_ssh_user=%s", aws_instance.etcd.*.private_ip, var.SSHUser))}\" >> inventory" - } - - ##Create Nodes Inventory - provisioner "local-exec" { - command = "echo \"\n[kube-node]\" >> inventory" - } - provisioner "local-exec" { - command = "echo \"${join("\n",formatlist("%s ansible_ssh_user=%s", aws_instance.minion.*.private_ip, var.SSHUser))}\" >> inventory" - } - - provisioner "local-exec" { - command = "echo \"\n[k8s-cluster:children]\nkube-node\nkube-master\" >> inventory" - } -} diff --git a/contrib/terraform/aws/README.md b/contrib/terraform/aws/README.md index c7ede59ef6f95def8f4c89e4cb47063c28e447e1..03bc4e23eee0293c48d60d1ba1480112d8c066ce 100644 --- a/contrib/terraform/aws/README.md +++ b/contrib/terraform/aws/README.md @@ -2,27 +2,34 @@ **Overview:** -- This will create nodes in a VPC inside of AWS +This project will create: +* VPC with Public and Private Subnets in # Availability Zones +* Bastion Hosts and NAT Gateways in the Public Subnet +* A dynamic number of masters, etcd, and worker nodes in the Private Subnet + * even distributed over the # of Availability Zones +* AWS ELB in the Public Subnet for accessing the Kubernetes API from the internet -- A dynamic number of masters, etcd, and nodes can be created - -- These scripts currently expect Private IP connectivity with the nodes that are created. This means that you may need a tunnel to your VPC or to run these scripts from a VM inside the VPC. Will be looking into how to work around this later. +**Requirements** +- Terraform 0.8.7 or newer **How to Use:** -- Export the variables for your Amazon credentials: +- Export the variables for your AWS credentials or edit credentials.tfvars: ``` -export AWS_ACCESS_KEY_ID="xxx" -export AWS_SECRET_ACCESS_KEY="yyy" +export aws_access_key="xxx" +export aws_secret_key="yyy" +export aws_ssh_key_name="zzz" ``` - Update contrib/terraform/aws/terraform.tfvars with your data -- Run with `terraform apply` +- Run with `terraform apply -var-file="credentials.tfvars"` or `terraform apply` depending if you exported your AWS credentials + +- Once the infrastructure is created, you can run the kargo playbooks and supply inventory/hosts with the `-i` flag. -- Once the infrastructure is created, you can run the kubespray playbooks and supply contrib/terraform/aws/inventory with the `-i` flag. +**Architecture** -**Future Work:** +Pictured is an AWS Infrastructure created with this Terraform project distributed over two Availability Zones. -- Update the inventory creation file to be something a little more reasonable. It's just a local-exec from Terraform now, using terraform.py or something may make sense in the future. \ No newline at end of file + diff --git a/contrib/terraform/aws/create-infrastructure.tf b/contrib/terraform/aws/create-infrastructure.tf new file mode 100644 index 0000000000000000000000000000000000000000..14da95492558497c198d8b86ccdb1603b6f2f682 --- /dev/null +++ b/contrib/terraform/aws/create-infrastructure.tf @@ -0,0 +1,185 @@ +terraform { + required_version = ">= 0.8.7" +} + +provider "aws" { + access_key = "${var.AWS_ACCESS_KEY_ID}" + secret_key = "${var.AWS_SECRET_ACCESS_KEY}" + region = "${var.AWS_DEFAULT_REGION}" +} + +/* +* Calling modules who create the initial AWS VPC / AWS ELB +* and AWS IAM Roles for Kubernetes Deployment +*/ + +module "aws-vpc" { + source = "modules/vpc" + + aws_cluster_name = "${var.aws_cluster_name}" + aws_vpc_cidr_block = "${var.aws_vpc_cidr_block}" + aws_avail_zones="${var.aws_avail_zones}" + + aws_cidr_subnets_private="${var.aws_cidr_subnets_private}" + aws_cidr_subnets_public="${var.aws_cidr_subnets_public}" + +} + + +module "aws-elb" { + source = "modules/elb" + + aws_cluster_name="${var.aws_cluster_name}" + aws_vpc_id="${module.aws-vpc.aws_vpc_id}" + aws_avail_zones="${var.aws_avail_zones}" + aws_subnet_ids_public="${module.aws-vpc.aws_subnet_ids_public}" + aws_elb_api_port = "${var.aws_elb_api_port}" + k8s_secure_api_port = "${var.k8s_secure_api_port}" + +} + +module "aws-iam" { + source = "modules/iam" + + aws_cluster_name="${var.aws_cluster_name}" +} + +/* +* Create Bastion Instances in AWS +* +*/ +resource "aws_instance" "bastion-server" { + ami = "${var.aws_bastion_ami}" + instance_type = "${var.aws_bastion_size}" + count = "${length(var.aws_cidr_subnets_public)}" + associate_public_ip_address = true + availability_zone = "${element(var.aws_avail_zones,count.index)}" + subnet_id = "${element(module.aws-vpc.aws_subnet_ids_public,count.index)}" + + + vpc_security_group_ids = [ "${module.aws-vpc.aws_security_group}" ] + + key_name = "${var.AWS_SSH_KEY_NAME}" + + tags { + Name = "kubernetes-${var.aws_cluster_name}-bastion-${count.index}" + Cluster = "${var.aws_cluster_name}" + Role = "bastion-${var.aws_cluster_name}-${count.index}" + } +} + + +/* +* Create K8s Master and worker nodes and etcd instances +* +*/ + +resource "aws_instance" "k8s-master" { + ami = "${var.aws_cluster_ami}" + instance_type = "${var.aws_kube_master_size}" + + count = "${var.aws_kube_master_num}" + + + availability_zone = "${element(var.aws_avail_zones,count.index)}" + subnet_id = "${element(module.aws-vpc.aws_subnet_ids_private,count.index)}" + + + vpc_security_group_ids = [ "${module.aws-vpc.aws_security_group}" ] + + + iam_instance_profile = "${module.aws-iam.kube-master-profile}" + key_name = "${var.AWS_SSH_KEY_NAME}" + + + tags { + Name = "kubernetes-${var.aws_cluster_name}-master${count.index}" + Cluster = "${var.aws_cluster_name}" + Role = "master" + } +} + +resource "aws_elb_attachment" "attach_master_nodes" { + count = "${var.aws_kube_master_num}" + elb = "${module.aws-elb.aws_elb_api_id}" + instance = "${element(aws_instance.k8s-master.*.id,count.index)}" +} + + +resource "aws_instance" "k8s-etcd" { + ami = "${var.aws_cluster_ami}" + instance_type = "${var.aws_etcd_size}" + + count = "${var.aws_etcd_num}" + + + availability_zone = "${element(var.aws_avail_zones,count.index)}" + subnet_id = "${element(module.aws-vpc.aws_subnet_ids_private,count.index)}" + + + vpc_security_group_ids = [ "${module.aws-vpc.aws_security_group}" ] + + key_name = "${var.AWS_SSH_KEY_NAME}" + + + tags { + Name = "kubernetes-${var.aws_cluster_name}-etcd${count.index}" + Cluster = "${var.aws_cluster_name}" + Role = "etcd" + } + +} + + +resource "aws_instance" "k8s-worker" { + ami = "${var.aws_cluster_ami}" + instance_type = "${var.aws_kube_worker_size}" + + count = "${var.aws_kube_worker_num}" + + availability_zone = "${element(var.aws_avail_zones,count.index)}" + subnet_id = "${element(module.aws-vpc.aws_subnet_ids_private,count.index)}" + + vpc_security_group_ids = [ "${module.aws-vpc.aws_security_group}" ] + + iam_instance_profile = "${module.aws-iam.kube-worker-profile}" + key_name = "${var.AWS_SSH_KEY_NAME}" + + + tags { + Name = "kubernetes-${var.aws_cluster_name}-worker${count.index}" + Cluster = "${var.aws_cluster_name}" + Role = "worker" + } + +} + + + +/* +* Create Kargo Inventory File +* +*/ +data "template_file" "inventory" { + template = "${file("${path.module}/templates/inventory.tpl")}" + + vars { + public_ip_address_bastion = "${join("\n",formatlist("bastion ansible_ssh_host=%s" , aws_instance.bastion-server.*.public_ip))}" + connection_strings_master = "${join("\n",formatlist("%s ansible_ssh_host=%s",aws_instance.k8s-master.*.tags.Name, aws_instance.k8s-master.*.private_ip))}" + connection_strings_node = "${join("\n", formatlist("%s ansible_ssh_host=%s", aws_instance.k8s-worker.*.tags.Name, aws_instance.k8s-worker.*.private_ip))}" + connection_strings_etcd = "${join("\n",formatlist("%s ansible_ssh_host=%s", aws_instance.k8s-etcd.*.tags.Name, aws_instance.k8s-etcd.*.private_ip))}" + list_master = "${join("\n",aws_instance.k8s-master.*.tags.Name)}" + list_node = "${join("\n",aws_instance.k8s-worker.*.tags.Name)}" + list_etcd = "${join("\n",aws_instance.k8s-etcd.*.tags.Name)}" + elb_api_fqdn = "apiserver_loadbalancer_domain_name=\"${module.aws-elb.aws_elb_api_fqdn}\"" + elb_api_port = "loadbalancer_apiserver.port=${var.aws_elb_api_port}" + + } +} + +resource "null_resource" "inventories" { + provisioner "local-exec" { + command = "echo '${data.template_file.inventory.rendered}' > ../../../inventory/hosts" + } + +} diff --git a/contrib/terraform/aws/credentials.tfvars.example b/contrib/terraform/aws/credentials.tfvars.example new file mode 100644 index 0000000000000000000000000000000000000000..19420c5a7d619af797312580a6035350ef0b11a8 --- /dev/null +++ b/contrib/terraform/aws/credentials.tfvars.example @@ -0,0 +1,8 @@ +#AWS Access Key +AWS_ACCESS_KEY_ID = "" +#AWS Secret Key +AWS_SECRET_ACCESS_KEY = "" +#EC2 SSH Key Name +AWS_SSH_KEY_NAME = "" +#AWS Region +AWS_DEFAULT_REGION = "eu-central-1" diff --git a/contrib/terraform/aws/docs/aws_kargo.png b/contrib/terraform/aws/docs/aws_kargo.png new file mode 100644 index 0000000000000000000000000000000000000000..40245b845a5094d3cef4500d4f256031329a7b2e Binary files /dev/null and b/contrib/terraform/aws/docs/aws_kargo.png differ diff --git a/contrib/terraform/aws/modules/elb/main.tf b/contrib/terraform/aws/modules/elb/main.tf new file mode 100644 index 0000000000000000000000000000000000000000..270e0cb356401d79d0a43896a780ce897f953985 --- /dev/null +++ b/contrib/terraform/aws/modules/elb/main.tf @@ -0,0 +1,50 @@ +resource "aws_security_group" "aws-elb" { + name = "kubernetes-${var.aws_cluster_name}-securitygroup-elb" + vpc_id = "${var.aws_vpc_id}" + + tags { + Name = "kubernetes-${var.aws_cluster_name}-securitygroup-elb" + } +} + + +resource "aws_security_group_rule" "aws-allow-api-access" { + type = "ingress" + from_port = "${var.aws_elb_api_port}" + to_port = "${var.k8s_secure_api_port}" + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = "${aws_security_group.aws-elb.id}" +} + + +# Create a new AWS ELB for K8S API +resource "aws_elb" "aws-elb-api" { + name = "kubernetes-elb-${var.aws_cluster_name}" + subnets = ["${var.aws_subnet_ids_public}"] + security_groups = ["${aws_security_group.aws-elb.id}"] + + listener { + instance_port = "${var.k8s_secure_api_port}" + instance_protocol = "tcp" + lb_port = "${var.aws_elb_api_port}" + lb_protocol = "tcp" + } + + health_check { + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 3 + target = "HTTP:8080/" + interval = 30 + } + + cross_zone_load_balancing = true + idle_timeout = 400 + connection_draining = true + connection_draining_timeout = 400 + + tags { + Name = "kubernetes-${var.aws_cluster_name}-elb-api" + } +} diff --git a/contrib/terraform/aws/modules/elb/outputs.tf b/contrib/terraform/aws/modules/elb/outputs.tf new file mode 100644 index 0000000000000000000000000000000000000000..075c751e4eb642795493c7e51a84fa8918aae6e5 --- /dev/null +++ b/contrib/terraform/aws/modules/elb/outputs.tf @@ -0,0 +1,7 @@ +output "aws_elb_api_id" { + value = "${aws_elb.aws-elb-api.id}" +} + +output "aws_elb_api_fqdn" { + value = "${aws_elb.aws-elb-api.dns_name}" +} diff --git a/contrib/terraform/aws/modules/elb/variables.tf b/contrib/terraform/aws/modules/elb/variables.tf new file mode 100644 index 0000000000000000000000000000000000000000..c7f86847d1a85aa819c4f73b86bb0c7f5ad35e6d --- /dev/null +++ b/contrib/terraform/aws/modules/elb/variables.tf @@ -0,0 +1,28 @@ +variable "aws_cluster_name" { + description = "Name of Cluster" +} + +variable "aws_vpc_id" { + description = "AWS VPC ID" +} + +variable "aws_elb_api_port" { + description = "Port for AWS ELB" +} + +variable "k8s_secure_api_port" { + description = "Secure Port of K8S API Server" +} + + + +variable "aws_avail_zones" { + description = "Availability Zones Used" + type = "list" +} + + +variable "aws_subnet_ids_public" { + description = "IDs of Public Subnets" + type = "list" +} diff --git a/contrib/terraform/aws/modules/iam/main.tf b/contrib/terraform/aws/modules/iam/main.tf new file mode 100644 index 0000000000000000000000000000000000000000..88da00d90a336f6dec888795fde575d07d6bab09 --- /dev/null +++ b/contrib/terraform/aws/modules/iam/main.tf @@ -0,0 +1,138 @@ +#Add AWS Roles for Kubernetes + +resource "aws_iam_role" "kube-master" { + name = "kubernetes-${var.aws_cluster_name}-master" + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "sts:AssumeRole", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ] +} +EOF +} + +resource "aws_iam_role" "kube-worker" { + name = "kubernetes-${var.aws_cluster_name}-node" + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "sts:AssumeRole", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ] +} +EOF +} + +#Add AWS Policies for Kubernetes + +resource "aws_iam_role_policy" "kube-master" { + name = "kubernetes-${var.aws_cluster_name}-master" + role = "${aws_iam_role.kube-master.id}" + policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["ec2:*"], + "Resource": ["*"] + }, + { + "Effect": "Allow", + "Action": ["elasticloadbalancing:*"], + "Resource": ["*"] + }, + { + "Effect": "Allow", + "Action": ["route53:*"], + "Resource": ["*"] + }, + { + "Effect": "Allow", + "Action": "s3:*", + "Resource": [ + "arn:aws:s3:::kubernetes-*" + ] + } + ] +} +EOF +} + +resource "aws_iam_role_policy" "kube-worker" { + name = "kubernetes-${var.aws_cluster_name}-node" + role = "${aws_iam_role.kube-worker.id}" + policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "s3:*", + "Resource": [ + "arn:aws:s3:::kubernetes-*" + ] + }, + { + "Effect": "Allow", + "Action": "ec2:Describe*", + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": "ec2:AttachVolume", + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": "ec2:DetachVolume", + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": ["route53:*"], + "Resource": ["*"] + }, + { + "Effect": "Allow", + "Action": [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:GetRepositoryPolicy", + "ecr:DescribeRepositories", + "ecr:ListImages", + "ecr:BatchGetImage" + ], + "Resource": "*" + } + ] +} +EOF +} + + +#Create AWS Instance Profiles + +resource "aws_iam_instance_profile" "kube-master" { + name = "kube_${var.aws_cluster_name}_master_profile" + roles = ["${aws_iam_role.kube-master.name}"] +} + +resource "aws_iam_instance_profile" "kube-worker" { + name = "kube_${var.aws_cluster_name}_node_profile" + roles = ["${aws_iam_role.kube-worker.name}"] +} diff --git a/contrib/terraform/aws/modules/iam/outputs.tf b/contrib/terraform/aws/modules/iam/outputs.tf new file mode 100644 index 0000000000000000000000000000000000000000..a6ccf88474584433f5e62fee51c73bceb3ab9211 --- /dev/null +++ b/contrib/terraform/aws/modules/iam/outputs.tf @@ -0,0 +1,7 @@ +output "kube-master-profile" { + value = "${aws_iam_instance_profile.kube-master.name }" +} + +output "kube-worker-profile" { + value = "${aws_iam_instance_profile.kube-worker.name }" +} diff --git a/contrib/terraform/aws/modules/iam/variables.tf b/contrib/terraform/aws/modules/iam/variables.tf new file mode 100644 index 0000000000000000000000000000000000000000..690fbe75617e91c0c3aca663310aafcd241bab53 --- /dev/null +++ b/contrib/terraform/aws/modules/iam/variables.tf @@ -0,0 +1,3 @@ +variable "aws_cluster_name" { + description = "Name of Cluster" +} diff --git a/contrib/terraform/aws/modules/vpc/main.tf b/contrib/terraform/aws/modules/vpc/main.tf new file mode 100644 index 0000000000000000000000000000000000000000..c1ea1dc11a9050fa7ac211726e62a94297f5711f --- /dev/null +++ b/contrib/terraform/aws/modules/vpc/main.tf @@ -0,0 +1,138 @@ + +resource "aws_vpc" "cluster-vpc" { + cidr_block = "${var.aws_vpc_cidr_block}" + + #DNS Related Entries + enable_dns_support = true + enable_dns_hostnames = true + + tags { + Name = "kubernetes-${var.aws_cluster_name}-vpc" + } +} + + +resource "aws_eip" "cluster-nat-eip" { + count = "${length(var.aws_cidr_subnets_public)}" + vpc = true +} + + + +resource "aws_internet_gateway" "cluster-vpc-internetgw" { + vpc_id = "${aws_vpc.cluster-vpc.id}" + + tags { + Name = "kubernetes-${var.aws_cluster_name}-internetgw" + } +} + +resource "aws_subnet" "cluster-vpc-subnets-public" { + vpc_id = "${aws_vpc.cluster-vpc.id}" + count="${length(var.aws_avail_zones)}" + availability_zone = "${element(var.aws_avail_zones, count.index)}" + cidr_block = "${element(var.aws_cidr_subnets_public, count.index)}" + + tags { + Name = "kubernetes-${var.aws_cluster_name}-${element(var.aws_avail_zones, count.index)}-public" + } +} + +resource "aws_nat_gateway" "cluster-nat-gateway" { + count = "${length(var.aws_cidr_subnets_public)}" + allocation_id = "${element(aws_eip.cluster-nat-eip.*.id, count.index)}" + subnet_id = "${element(aws_subnet.cluster-vpc-subnets-public.*.id, count.index)}" + +} + +resource "aws_subnet" "cluster-vpc-subnets-private" { + vpc_id = "${aws_vpc.cluster-vpc.id}" + count="${length(var.aws_avail_zones)}" + availability_zone = "${element(var.aws_avail_zones, count.index)}" + cidr_block = "${element(var.aws_cidr_subnets_private, count.index)}" + + tags { + Name = "kubernetes-${var.aws_cluster_name}-${element(var.aws_avail_zones, count.index)}-private" + } +} + +#Routing in VPC + +#TODO: Do we need two routing tables for each subnet for redundancy or is one enough? + +resource "aws_route_table" "kubernetes-public" { + vpc_id = "${aws_vpc.cluster-vpc.id}" + route { + cidr_block = "0.0.0.0/0" + gateway_id = "${aws_internet_gateway.cluster-vpc-internetgw.id}" + } + tags { + Name = "kubernetes-${var.aws_cluster_name}-routetable-public" + } +} + +resource "aws_route_table" "kubernetes-private" { + count = "${length(var.aws_cidr_subnets_private)}" + vpc_id = "${aws_vpc.cluster-vpc.id}" + route { + cidr_block = "0.0.0.0/0" + gateway_id = "${element(aws_nat_gateway.cluster-nat-gateway.*.id, count.index)}" + } + tags { + Name = "kubernetes-${var.aws_cluster_name}-routetable-private-${count.index}" + } +} + +resource "aws_route_table_association" "kubernetes-public" { + count = "${length(var.aws_cidr_subnets_public)}" + subnet_id = "${element(aws_subnet.cluster-vpc-subnets-public.*.id,count.index)}" + route_table_id = "${aws_route_table.kubernetes-public.id}" + +} + +resource "aws_route_table_association" "kubernetes-private" { + count = "${length(var.aws_cidr_subnets_private)}" + subnet_id = "${element(aws_subnet.cluster-vpc-subnets-private.*.id,count.index)}" + route_table_id = "${element(aws_route_table.kubernetes-private.*.id,count.index)}" + +} + + +#Kubernetes Security Groups + +resource "aws_security_group" "kubernetes" { + name = "kubernetes-${var.aws_cluster_name}-securitygroup" + vpc_id = "${aws_vpc.cluster-vpc.id}" + + tags { + Name = "kubernetes-${var.aws_cluster_name}-securitygroup" + } +} + +resource "aws_security_group_rule" "allow-all-ingress" { + type = "ingress" + from_port = 0 + to_port = 65535 + protocol = "-1" + cidr_blocks= ["${var.aws_vpc_cidr_block}"] + security_group_id = "${aws_security_group.kubernetes.id}" +} + +resource "aws_security_group_rule" "allow-all-egress" { + type = "egress" + from_port = 0 + to_port = 65535 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = "${aws_security_group.kubernetes.id}" +} + + +resource "aws_security_group_rule" "allow-ssh-connections" { + type = "ingress" + from_port = 22 + to_port = 22 + protocol = "TCP" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = "${aws_security_group.kubernetes.id}" +} diff --git a/contrib/terraform/aws/modules/vpc/outputs.tf b/contrib/terraform/aws/modules/vpc/outputs.tf new file mode 100644 index 0000000000000000000000000000000000000000..950462a486e3d32790924f0583568c0e5db0364b --- /dev/null +++ b/contrib/terraform/aws/modules/vpc/outputs.tf @@ -0,0 +1,16 @@ +output "aws_vpc_id" { + value = "${aws_vpc.cluster-vpc.id}" +} + +output "aws_subnet_ids_private" { + value = ["${aws_subnet.cluster-vpc-subnets-private.*.id}"] +} + +output "aws_subnet_ids_public" { + value = ["${aws_subnet.cluster-vpc-subnets-public.*.id}"] +} + +output "aws_security_group" { + value = ["${aws_security_group.kubernetes.*.id}"] + +} diff --git a/contrib/terraform/aws/modules/vpc/variables.tf b/contrib/terraform/aws/modules/vpc/variables.tf new file mode 100644 index 0000000000000000000000000000000000000000..55adf2be315b7f0655f4a7b28bdafd94688b7b7d --- /dev/null +++ b/contrib/terraform/aws/modules/vpc/variables.tf @@ -0,0 +1,24 @@ +variable "aws_vpc_cidr_block" { + description = "CIDR Blocks for AWS VPC" +} + + +variable "aws_cluster_name" { + description = "Name of Cluster" +} + + +variable "aws_avail_zones" { + description = "AWS Availability Zones Used" + type = "list" +} + +variable "aws_cidr_subnets_private" { + description = "CIDR Blocks for private subnets in Availability zones" + type = "list" +} + +variable "aws_cidr_subnets_public" { + description = "CIDR Blocks for public subnets in Availability zones" + type = "list" +} diff --git a/contrib/terraform/aws/output.tf b/contrib/terraform/aws/output.tf new file mode 100644 index 0000000000000000000000000000000000000000..fbe74f2622c7fb697bbecffd9a05c6a738c7b7b7 --- /dev/null +++ b/contrib/terraform/aws/output.tf @@ -0,0 +1,20 @@ +output "bastion_ip" { + value = "${join("\n", aws_instance.bastion-server.*.public_ip)}" +} + +output "masters" { + value = "${join("\n", aws_instance.k8s-master.*.private_ip)}" +} + +output "workers" { + value = "${join("\n", aws_instance.k8s-worker.*.private_ip)}" +} + +output "etcd" { + value = "${join("\n", aws_instance.k8s-etcd.*.private_ip)}" +} + + +output "aws_elb_api_fqdn" { + value = "${module.aws-elb.aws_elb_api_fqdn}:${var.aws_elb_api_port}" +} diff --git a/contrib/terraform/aws/templates/inventory.tpl b/contrib/terraform/aws/templates/inventory.tpl new file mode 100644 index 0000000000000000000000000000000000000000..4140aa768e5a029234d815c5d1d56207140499d3 --- /dev/null +++ b/contrib/terraform/aws/templates/inventory.tpl @@ -0,0 +1,27 @@ +${connection_strings_master} +${connection_strings_node} +${connection_strings_etcd} + + +${public_ip_address_bastion} + +[kube-master] +${list_master} + + +[kube-node] +${list_node} + + +[etcd] +${list_etcd} + + +[k8s-cluster:children] +kube-node +kube-master + + +[k8s-cluster:vars] +${elb_api_fqdn} +${elb_api_port} diff --git a/contrib/terraform/aws/terraform.tfvars b/contrib/terraform/aws/terraform.tfvars index 012babc27f57c36f3202e017224848c235dce56b..214ef89db2fd84a6002f3fd0c237278dbf2a65c9 100644 --- a/contrib/terraform/aws/terraform.tfvars +++ b/contrib/terraform/aws/terraform.tfvars @@ -1,22 +1,32 @@ -deploymentName="test-kube-deploy" +#Global Vars +aws_cluster_name = "devtest" +aws_region = "eu-central-1" -numControllers="2" -numEtcd="3" -numNodes="2" +#VPC Vars +aws_vpc_cidr_block = "10.250.192.0/18" +aws_cidr_subnets_private = ["10.250.192.0/20","10.250.208.0/20"] +aws_cidr_subnets_public = ["10.250.224.0/20","10.250.240.0/20"] +aws_avail_zones = ["eu-central-1a","eu-central-1b"] -volSizeController="20" -volSizeEtcd="20" -volSizeNodes="20" +#Bastion Host +aws_bastion_ami = "ami-5900cc36" +aws_bastion_size = "t2.small" -awsRegion="us-west-2" -subnet="subnet-xxxxx" -ami="ami-32a85152" -securityGroups="sg-xxxxx" -SSHUser="core" -SSHKey="my-key" -master_instance_type="m3.xlarge" -etcd_instance_type="m3.xlarge" -node_instance_type="m3.xlarge" +#Kubernetes Cluster -terminate_protect="false" +aws_kube_master_num = 3 +aws_kube_master_size = "t2.medium" + +aws_etcd_num = 3 +aws_etcd_size = "t2.medium" + +aws_kube_worker_num = 4 +aws_kube_worker_size = "t2.medium" + +aws_cluster_ami = "ami-903df7ff" + +#Settings AWS ELB + +aws_elb_api_port = 443 +k8s_secure_api_port = 443 diff --git a/contrib/terraform/aws/variables.tf b/contrib/terraform/aws/variables.tf new file mode 100644 index 0000000000000000000000000000000000000000..90f356d4bf9e544757922226193c637eb404c11a --- /dev/null +++ b/contrib/terraform/aws/variables.tf @@ -0,0 +1,97 @@ +variable "AWS_ACCESS_KEY_ID" { + description = "AWS Access Key" +} + +variable "AWS_SECRET_ACCESS_KEY" { + description = "AWS Secret Key" +} + +variable "AWS_SSH_KEY_NAME" { + description = "Name of the SSH keypair to use in AWS." +} + +variable "AWS_DEFAULT_REGION" { + description = "AWS Region" +} + +//General Cluster Settings + +variable "aws_cluster_name" { + description = "Name of AWS Cluster" +} + + +//AWS VPC Variables + +variable "aws_vpc_cidr_block" { + description = "CIDR Block for VPC" +} + +variable "aws_avail_zones" { + description = "Availability Zones Used" + type = "list" +} + +variable "aws_cidr_subnets_private" { + description = "CIDR Blocks for private subnets in Availability zones1" + type = "list" +} + +variable "aws_cidr_subnets_public" { + description = "CIDR Blocks for public subnets in Availability zones1" + type = "list" +} + +//AWS EC2 Settings + +variable "aws_bastion_ami" { + description = "AMI ID for Bastion Host in chosen AWS Region" +} + +variable "aws_bastion_size" { + description = "EC2 Instance Size of Bastion Host" +} + +/* +* AWS EC2 Settings +* The number should be divisable by the number of used +* AWS Availability Zones without an remainder. +*/ +variable "aws_kube_master_num" { + description = "Number of Kubernetes Master Nodes" +} + +variable "aws_kube_master_size" { + description = "Instance size of Kube Master Nodes" +} + +variable "aws_etcd_num" { + description = "Number of etcd Nodes" +} + +variable "aws_etcd_size" { + description = "Instance size of etcd Nodes" +} + +variable "aws_kube_worker_num" { + description = "Number of Kubernetes Worker Nodes" +} + +variable "aws_kube_worker_size" { + description = "Instance size of Kubernetes Worker Nodes" +} + +variable "aws_cluster_ami" { + description = "AMI ID for Kubernetes Cluster" +} +/* +* AWS ELB Settings +* +*/ +variable "aws_elb_api_port" { + description = "Port for AWS ELB" +} + +variable "k8s_secure_api_port" { + description = "Secure Port of K8S API Server" +}