From 5c5bf41afeab35387e8e619f00432c704018709b Mon Sep 17 00:00:00 2001 From: Ewnetu Bayuh Lakew Date: Mon, 15 Mar 2021 09:41:04 +0100 Subject: [PATCH] Terraform support for UpCloud (#7360) * terraform support for UpCloud * terraform support for UpCloud * terraform support for UpCloud * terraform support for UpCloud * terraform support for UpCloud * terraform support for UpCloud * terraform support for UpCloud * Updates to README.md and main.tf files * formatting and updating readme * added a .terraform_validate CI job * fixed format issue * added sample inventory * added symbolic link to group_vars * added missing tf variables and minor fixes * added text formatting * minor formatting fixes --- .gitlab-ci/terraform.yml | 14 +++ contrib/terraform/upcloud/README.md | 106 ++++++++++++++++++ .../terraform/upcloud/cluster-settings.tfvars | 58 ++++++++++ contrib/terraform/upcloud/main.tf | 55 +++++++++ .../modules/kubernetes-cluster/main.tf | 66 +++++++++++ .../modules/kubernetes-cluster/output.tf | 14 +++ .../modules/kubernetes-cluster/variables.tf | 25 +++++ .../modules/kubernetes-cluster/versions.tf | 10 ++ contrib/terraform/upcloud/output.tf | 8 ++ .../upcloud/sample-inventory/cluster.tfvars | 56 +++++++++ .../upcloud/sample-inventory/group_vars | 1 + .../terraform/upcloud/templates/inventory.tpl | 17 +++ contrib/terraform/upcloud/variables.tf | 35 ++++++ contrib/terraform/upcloud/versions.tf | 10 ++ 14 files changed, 475 insertions(+) create mode 100644 contrib/terraform/upcloud/README.md create mode 100644 contrib/terraform/upcloud/cluster-settings.tfvars create mode 100644 contrib/terraform/upcloud/main.tf create mode 100644 contrib/terraform/upcloud/modules/kubernetes-cluster/main.tf create mode 100644 contrib/terraform/upcloud/modules/kubernetes-cluster/output.tf create mode 100644 contrib/terraform/upcloud/modules/kubernetes-cluster/variables.tf create mode 100644 contrib/terraform/upcloud/modules/kubernetes-cluster/versions.tf create mode 100644 contrib/terraform/upcloud/output.tf create mode 100644 contrib/terraform/upcloud/sample-inventory/cluster.tfvars create mode 120000 contrib/terraform/upcloud/sample-inventory/group_vars create mode 100644 contrib/terraform/upcloud/templates/inventory.tpl create mode 100644 contrib/terraform/upcloud/variables.tf create mode 100644 contrib/terraform/upcloud/versions.tf diff --git a/.gitlab-ci/terraform.yml b/.gitlab-ci/terraform.yml index bd5adb4d4..7484efe2e 100644 --- a/.gitlab-ci/terraform.yml +++ b/.gitlab-ci/terraform.yml @@ -108,6 +108,13 @@ tf-0.13.x-validate-vsphere: PROVIDER: vsphere CLUSTER: $CI_COMMIT_REF_NAME +tf-0.13.x-validate-upcloud: + extends: .terraform_validate + variables: + TF_VERSION: 0.13.5 + PROVIDER: upcloud + CLUSTER: $CI_COMMIT_REF_NAME + tf-0.14.x-validate-openstack: extends: .terraform_validate variables: @@ -142,6 +149,13 @@ tf-0.14.x-validate-vsphere: PROVIDER: vsphere CLUSTER: $CI_COMMIT_REF_NAME +tf-0.14.x-validate-upcloud: + extends: .terraform_validate + variables: + TF_VERSION: 0.14.3 + PROVIDER: upcloud + CLUSTER: $CI_COMMIT_REF_NAME + # tf-packet-ubuntu16-default: # extends: .terraform_apply # variables: diff --git a/contrib/terraform/upcloud/README.md b/contrib/terraform/upcloud/README.md new file mode 100644 index 000000000..dcce236fe --- /dev/null +++ b/contrib/terraform/upcloud/README.md @@ -0,0 +1,106 @@ +# Kubernetes on UpCloud with Terraform + +Provision a Kubernetes cluster on [UpCloud](https://upcloud.com/) using Terraform and Kubespray + +## Overview + +The setup looks like following + +```text + Kubernetes cluster ++-----------------------+ +| +--------------+ | +| | +--------------+ | +| | | | | +| | | Master/etcd | | +| | | node(s) | | +| +-+ | | +| +--------------+ | +| ^ | +| | | +| v | +| +--------------+ | +| | +--------------+ | +| | | | | +| | | Worker | | +| | | node(s) | | +| +-+ | | +| +--------------+ | ++-----------------------+ +``` + +## Requirements + +* Terraform 0.13.0 or newer + +## Quickstart + +NOTE: Assumes you are at the root of the kubespray repo. + +For authentication in your cluster you can use the environment variables. + +```bash +export TF_VAR_UPCLOUD_USERNAME=username +export TF_VAR_UPCLOUD_PASSWORD=password +``` + +To allow API access to your UpCloud account, you need to allow API connections by visiting [Account-page](https://hub.upcloud.com/account) in your UpCloud Hub. + +Copy the cluster configuration file. + +```bash +CLUSTER=my-upcloud-cluster +cp -r inventory/sample inventory/$CLUSTER +cp contrib/terraform/upcloud/cluster-settings.tfvars inventory/$CLUSTER/ +export ANSIBLE_CONFIG=ansible.cfg +cd inventory/$CLUSTER +``` + +Edit `cluster-settings.tfvars` to match your requirement. + +Run Terraform to create the infrastructure. + +```bash +terraform init ../../contrib/terraform/upcloud +terraform apply --var-file cluster-settings.tfvars \ + -state=tfstate-$CLUSTER.tfstate \ + ../../contrib/terraform/upcloud/ +``` + +You should now have a inventory file named `inventory.ini` that you can use with kubespray. +You can use the inventory file with kubespray to set up a cluster. + +It is a good idea to check that you have basic SSH connectivity to the nodes. You can do that by: + +```bash +ansible -i inventory.ini -m ping all +``` + +You can setup Kubernetes with kubespray using the generated inventory: + +```bash +ansible-playbook -i inventory.ini ../../cluster.yml -b -v +``` + +## Teardown + +You can teardown your infrastructure using the following Terraform command: + +```bash +terraform destroy --var-file cluster-settings.tfvars \ + -state=tfstate-$CLUSTER.tfstate \ + ../../contrib/terraform/upcloud/ +``` + +## Variables + +* `hostname`: A valid domain name, e.g. example.com. The maximum length is 128 characters. +* `template_name`: The name or UUID of a base image +* `username`: a user to access the nodes +* `ssh_public_keys`: List of public SSH keys to install on all machines +* `zone`: The zone where to run the cluster +* `machines`: Machines to provision. Key of this object will be used as the name of the machine + * `node_type`: The role of this node *(master|worker)* + * `cpu`: number of cpu cores + * `mem`: memory size in MB + * `disk_size`: The size of the storage in GB diff --git a/contrib/terraform/upcloud/cluster-settings.tfvars b/contrib/terraform/upcloud/cluster-settings.tfvars new file mode 100644 index 000000000..08bf5dacf --- /dev/null +++ b/contrib/terraform/upcloud/cluster-settings.tfvars @@ -0,0 +1,58 @@ + +# See: https://developers.upcloud.com/1.3/5-zones/ +zone = "fi-hel1" +username = "ubuntu" + +inventory_file = "inventory.ini" + +# A valid domain name, e.g. host.example.com. The maximum length is 128 characters. +hostname = "example.com" + +# Set the operating system using UUID or exact name +template_name = "Ubuntu Server 20.04 LTS (Focal Fossa)" + +ssh_public_keys = [ + # Put your public SSH key here + "ssh-rsa public key 1", + "ssh-rsa public key 2", +] + +#check list of available plan https://developers.upcloud.com/1.3/7-plans/ +machines = { + "master-0" : { + "node_type" : "master", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + }, + "worker-0" : { + "node_type" : "worker", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + }, + "worker-1" : { + "node_type" : "worker", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + }, + "worker-2" : { + "node_type" : "worker", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + } +} diff --git a/contrib/terraform/upcloud/main.tf b/contrib/terraform/upcloud/main.tf new file mode 100644 index 000000000..8ddd46542 --- /dev/null +++ b/contrib/terraform/upcloud/main.tf @@ -0,0 +1,55 @@ + +terraform { + required_version = ">= 0.13.0" +} +provider "upcloud" { + # Your UpCloud credentials are read from environment variables: + username = var.UPCLOUD_USERNAME + password = var.UPCLOUD_PASSWORD +} + +module "kubernetes" { + source = "./modules/kubernetes-cluster" + + zone = var.zone + hostname = var.hostname + + template_name = var.template_name + username = var.username + + machines = var.machines + + ssh_public_keys = var.ssh_public_keys +} + +# +# Generate ansible inventory +# + +data "template_file" "inventory" { + template = file("${path.module}/templates/inventory.tpl") + + vars = { + connection_strings_master = join("\n", formatlist("%s ansible_user=ubuntu ansible_host=%s etcd_member_name=etcd%d", + keys(module.kubernetes.master_ip), + values(module.kubernetes.master_ip), + range(1, length(module.kubernetes.master_ip) + 1))) + connection_strings_worker = join("\n", formatlist("%s ansible_user=ubuntu ansible_host=%s", + keys(module.kubernetes.worker_ip), + values(module.kubernetes.worker_ip))) + list_master = join("\n", formatlist("%s", + keys(module.kubernetes.master_ip))) + list_worker = join("\n", formatlist("%s", + keys(module.kubernetes.worker_ip))) + } +} + +resource "null_resource" "inventories" { + provisioner "local-exec" { + command = "echo '${data.template_file.inventory.rendered}' > ${var.inventory_file}" + } + + triggers = { + template = data.template_file.inventory.rendered + } +} diff --git a/contrib/terraform/upcloud/modules/kubernetes-cluster/main.tf b/contrib/terraform/upcloud/modules/kubernetes-cluster/main.tf new file mode 100644 index 000000000..6a79af720 --- /dev/null +++ b/contrib/terraform/upcloud/modules/kubernetes-cluster/main.tf @@ -0,0 +1,66 @@ + +resource "upcloud_server" "master" { + for_each = { + for name, machine in var.machines : + name => machine + if machine.node_type == "master" + } + + hostname = "${each.key}.${var.hostname}" + cpu = each.value.cpu + mem = each.value.mem + zone = var.zone + + template { + storage = var.template_name + size = each.value.disk_size + } + + # Network interfaces + network_interface { + type = "public" + } + + network_interface { + type = "utility" + } + # Include at least one public SSH key + login { + user = var.username + keys = var.ssh_public_keys + create_password = false + + } + +} + + +resource "upcloud_server" "worker" { + for_each = { + for name, machine in var.machines : + name => machine + if machine.node_type == "worker" + } + + hostname = "${each.key}.${var.hostname}" + cpu = each.value.cpu + mem = each.value.mem + zone = var.zone + + template { + storage = var.template_name + size = each.value.disk_size + } + + # Network interfaces + network_interface { + type = "public" + } + + # Include at least one public SSH key + login { + user = var.username + keys = var.ssh_public_keys + create_password = false + } +} diff --git a/contrib/terraform/upcloud/modules/kubernetes-cluster/output.tf b/contrib/terraform/upcloud/modules/kubernetes-cluster/output.tf new file mode 100644 index 000000000..2661ac013 --- /dev/null +++ b/contrib/terraform/upcloud/modules/kubernetes-cluster/output.tf @@ -0,0 +1,14 @@ + +output "master_ip" { + value = { + for instance in upcloud_server.master : + instance.hostname => instance.network_interface[0].ip_address + } +} + +output "worker_ip" { + value = { + for instance in upcloud_server.worker : + instance.hostname => instance.network_interface[0].ip_address + } +} diff --git a/contrib/terraform/upcloud/modules/kubernetes-cluster/variables.tf b/contrib/terraform/upcloud/modules/kubernetes-cluster/variables.tf new file mode 100644 index 000000000..5b130ad10 --- /dev/null +++ b/contrib/terraform/upcloud/modules/kubernetes-cluster/variables.tf @@ -0,0 +1,25 @@ +variable "zone" { + type = string +} + +variable "hostname"{ + default ="example.com" +} + +variable "template_name"{} + +variable "username"{} + +variable "machines" { + description = "Cluster machines" + type = map(object({ + node_type = string + cpu = string + mem = string + disk_size = number + })) +} + +variable "ssh_public_keys" { + type = list(string) +} diff --git a/contrib/terraform/upcloud/modules/kubernetes-cluster/versions.tf b/contrib/terraform/upcloud/modules/kubernetes-cluster/versions.tf new file mode 100644 index 000000000..ffe5d32b1 --- /dev/null +++ b/contrib/terraform/upcloud/modules/kubernetes-cluster/versions.tf @@ -0,0 +1,10 @@ + +terraform { + required_providers { + upcloud = { + source = "UpCloudLtd/upcloud" + version = "~>2.0.0" + } + } + required_version = ">= 0.13" +} diff --git a/contrib/terraform/upcloud/output.tf b/contrib/terraform/upcloud/output.tf new file mode 100644 index 000000000..f269c755e --- /dev/null +++ b/contrib/terraform/upcloud/output.tf @@ -0,0 +1,8 @@ + +output "master_ip" { + value = module.kubernetes.master_ip +} + +output "worker_ip" { + value = module.kubernetes.worker_ip +} diff --git a/contrib/terraform/upcloud/sample-inventory/cluster.tfvars b/contrib/terraform/upcloud/sample-inventory/cluster.tfvars new file mode 100644 index 000000000..0a324045e --- /dev/null +++ b/contrib/terraform/upcloud/sample-inventory/cluster.tfvars @@ -0,0 +1,56 @@ +# See: https://developers.upcloud.com/1.3/5-zones/ +zone = "fi-hel1" +username = "ubuntu" + +inventory_file = "inventory.ini" + +# A valid domain name, e.g. host.example.com. The maximum length is 128 characters. +hostname = "example.com" + +# Set the operating system using UUID or exact name +template_name = "Ubuntu Server 20.04 LTS (Focal Fossa)" +ssh_public_keys = [ + # Put your public SSH key here + "ssh-rsa I-did-not-read-the-docs", + "ssh-rsa I-did-not-read-the-docs 2", +] + +check list of available plan https://developers.upcloud.com/1.3/7-plans/ +machines = { + "master-0" : { + "node_type" : "master", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + }, + "worker-0" : { + "node_type" : "worker", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + }, + "worker-1" : { + "node_type" : "worker", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + }, + "worker-2" : { + "node_type" : "worker", + #number of cpu cores + "cpu" : "2", + #memory size in MB + "mem" : "4096" + # The size of the storage in GB + "disk_size" : 250 + } +} diff --git a/contrib/terraform/upcloud/sample-inventory/group_vars b/contrib/terraform/upcloud/sample-inventory/group_vars new file mode 120000 index 000000000..0d5106205 --- /dev/null +++ b/contrib/terraform/upcloud/sample-inventory/group_vars @@ -0,0 +1 @@ +../../../../inventory/sample/group_vars/ \ No newline at end of file diff --git a/contrib/terraform/upcloud/templates/inventory.tpl b/contrib/terraform/upcloud/templates/inventory.tpl new file mode 100644 index 000000000..26d65e67b --- /dev/null +++ b/contrib/terraform/upcloud/templates/inventory.tpl @@ -0,0 +1,17 @@ + +[all] +${connection_strings_master} +${connection_strings_worker} + +[kube-master] +${list_master} + +[etcd] +${list_master} + +[kube-node] +${list_worker} + +[k8s-cluster:children] +kube-master +kube-node diff --git a/contrib/terraform/upcloud/variables.tf b/contrib/terraform/upcloud/variables.tf new file mode 100644 index 000000000..60941d572 --- /dev/null +++ b/contrib/terraform/upcloud/variables.tf @@ -0,0 +1,35 @@ + +variable "zone" { + description = "The zone where to run the cluster" +} + +variable "hostname" { + default = "example.com" +} + +variable "template_name" {} + +variable "username" {} + +variable "machines" { + description = "Cluster machines" + type = map(object({ + node_type = string + cpu = string + mem = string + disk_size = number + })) +} + +variable "ssh_public_keys" { + description = "List of public SSH keys which are injected into the VMs." + type = list(string) +} + +variable "inventory_file" { + description = "Where to store the generated inventory file" +} + +variable "UPCLOUD_USERNAME" {} + +variable "UPCLOUD_PASSWORD" {} diff --git a/contrib/terraform/upcloud/versions.tf b/contrib/terraform/upcloud/versions.tf new file mode 100644 index 000000000..c91089879 --- /dev/null +++ b/contrib/terraform/upcloud/versions.tf @@ -0,0 +1,10 @@ + +terraform { + required_providers { + upcloud = { + source = "UpCloudLtd/upcloud" + version = "~>2.0.0" + } + } + required_version = ">= 0.13" +}