diff --git a/SwitchEngines/README.md b/SwitchEngines/README.md index 3ddccb3c8200ff928974c128db8d92e8c36a5cef..2094cc841f82140f0d50a84a01de2e4c9abdcb97 100644 --- a/SwitchEngines/README.md +++ b/SwitchEngines/README.md @@ -721,7 +721,9 @@ unless the correct [user-data merge strategy is specified](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). -### Task #9: Provisioning KinD with with Terraform ### +### Task #9: Provisioning KinD with Cloud-init ### + +**Goal:** provision an application software stack with Terraform and Cloud-init. **Please, complete the [K8s Lab tutorial](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-k8s) @@ -748,3 +750,126 @@ on the instance as done during the K8s Lab: 1. Write a minimal KinD configuration file for 2 worker nodes. 1. Provision the cluster. 1. Deploy the matallb load balancer service. + + +### Task #10: Handling Kubernetes deployments ### + +**Goal:** deploy a Kubernetes-based micro-service application with Terraform +and KinD. + +So far, we have seen how to provision a minimal infrastructure an install a +K8s/KinD SW stack. Now, the main issue with KinD is that it's not designed to +be remotely controlled, which complicates service deployment operations: +indeed, Terraform is normally installed and called from a workstation. There +are two possibilities: + +a. Use TF's special *resource-less provisioners* within the same TF plan to + spawn operations on the target infrastructure after its provisioning. + +a. Use TF's official Kubernetes provider with an auxiliary TF plan + exploiting and SSH tunnel to the instance, after its provisioning by the + main TF plan. + + +#### Task #10.1 Resource-less provisioning #### + +The TF [resource-less +provisioner](https://developer.hashicorp.com/terraform/language/resources/provisioners/null_resource) +approach is considered "last-resort", because it is not very flexible and +can't easily implement the *desired state* paradigm. Indeed, some other +configuration management solutions, like Ansible, are surely better suited for +this task. Nonetheless, it is interesting in its capability of handling +generic operations. + +We have seen that a TF *resource* is an entity backed up by a *provider*, that +is, a mechanism that processes the resource declaration and invokes a given +provisioning API. Terraform provides also special resources, not bound to any +provider (no backend API), which can be used to encapsulate provisioners for +local or remote operations. + +In our case, the workflow, implemented within the same TF plan, is the the +following: + +1. Provision an instance with the OpenStack provider. +1. Provision extra system configuration and packages with Cloud-init. +1. Provision, or rather deploy, a K8s service via `file` and `remote-exec` + provisioners. + + +You shall extend your `main.tf` plan recipe with: + +1. At least one `terraform_data` resource declaration that encapsulate all the service + deployment logic: transferring files and sending commands. +1. Inside your `terraform_data` declaration: + - a [`connection` + block](https://developer.hashicorp.com/terraform/language/resources/provisioners/connection) + of type SSH with the credentials of the user `terraform` as defined in + one of the tasks above; + - as many [file provisioner](https://developer.hashicorp.com/terraform/language/resources/provisioners/file) blocks as needed to transfer the KinD and + MetalLB confguration and deployment files; + - at least one [remote-exec + provisioner](https://developer.hashicorp.com/terraform/language/resources/provisioners/remote-exec) + which implements a script of shell commands to move around files, create + a KinD cluster and, finally, deploy your MetalLB service as done in [Lab K8s](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-k8s/-/blob/main/README.md#part-5-actually-deploying-an-application). + +The solution might be something like: + +``` hcl +resource "terraform_data" "kind_cluster" { + connection { + type = "ssh" + user = ... + private_key = ... + host = ... + } + + provisioner "file" { + source = ... + destination = ... + } + + provisioner "file" { + ... + } + + provisioner "remote-exec" { + inline = [ + "shell command", + ..., + ] + } +} + +``` + +:bulb: Please mind that: + +- The provisioner blocks are processed in the order of appearance. +- For a maximum of flexibility, it is advisable to write 2 +`terraform_data` blocks: one for KinD and another for MetalLB. This way you +can test the creation of the two resources separately. + +:warning: You will probably hit some thorny issues: + +- Depending on how you code the `terraform_data` block(s), the resource(s) + might get processed too soon or in the wrong order. So, be sure of using + *variable references* so as to induce indirect dependencies. You might also + need to explicitly declare a `depends_on` list. +- Before doing anything, you must wait for Cloud-init to finish its tasks: + this can take several minutes (at least 4-5). +- KinD and K8s are very complex and, during set up, might fail in unexpected + ways, chiefly because of system/network issues. Thus, it is wise to wait a + bit (at least 20-30 seconds) after each `kind/kubectl` command. + + +#### Task #10.2 Auxiliary K8s plan provisioning #### + +:warning: **Under construction!** + +As you have you seen, the resource-less provisioning approach is ridden with +pitfalls. A better, more programmatic way is to employ an auxiliary TF plan +based on the [official TF Kubernetes +provider](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs) +and exploit an [SSH tunnel to convey K8s API +calls](https://gitedu.hesge.ch/lsds/teaching/bachelor/cloud-and-deployment/lab-k8s/-/blob/main/README.md#controlling-your-cluster-from-a-remote-console) +to the remote KinD instance. diff --git a/SwitchEngines/main.tf b/SwitchEngines/main.tf index a326d5e525a609488541b6fd522d2499f46456d6..c24c491bf1d4437934e823c8ba3415e59a3ae7f1 100644 --- a/SwitchEngines/main.tf +++ b/SwitchEngines/main.tf @@ -40,13 +40,26 @@ resource "openstack_compute_instance_v2" "app_server" { # Option #3 (*preferred*). Cloud-init with user data. Access with user # specified in the cloud-init YAML file. Key pair is not visible in Horizon - # @@ KEY PAIR CONFIGURATION @@ + # @@ CLOUD-INIT: KEY PAIR CONFIGURATION @@ + + + # @Task #8.1 + # @@ CLOUD-INIT: DEFAULT USER CONFIGURATION @@ + + # @Task #9.1 + # @@ CLOUD-INIT: KIND PROVISIONING @@ } -# @Task #7. Network configuration. +# @Task #7. Network configuration. # @@ SEC GROUP CONFIGURATION @@ - # @@ SEC GROUP RULES CONFIGURATION: ICMP, TCP/22 @@ - # @@ FIP + PORT + ASSOCIATION CONFIGURATION @@ + + +# @Task #8.2 +# @@ INTEGRATE CLOUD-INIT WITH TERRAFORM @@ + + +# @Task #10.1 +# @@ KIND/K8S SERVICE DEPLOYMENT BY RESOURCE-LESS PROVISIONING @@