How to deploy a Linode instance with Kubernetes using Terraform Part 1
In this post, I wanted to showcase deploying a Kubernetes cluster on Linode Cloud. Typically, I would opt for Azure Cloud, but I decided to take advantage of Linode's free $100 credit to try it out. This was actually my first-time deploying Kubernetes and I think it worked out pretty well.
To illustrate, I plan to set up one Linode instance which will be the master node known as the Control Plane Node. This is the core of the Kubernetes service and orchestration of application nodes. This is used to control and manage worker nodes. On top of that, I will also deploy a worker node which is the application workload.
Now you might ask, what is Kubernetes? Well, it's an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications. With this approach, it allows us to build fault-tolerant application workloads.
Why would you use this over virtual machines? It provides efficient, scalable, and portable infrastructure for running applications and makes it easy to manage complex applications as you can just rebuild the config due to its declarative nature.
I have to admit, there is a certain level of complexity involved in understanding some of the concepts here. However, as you continue to engage with the material, your understanding will deepen. With that in mind, I will endeavour to provide a detailed explanation of each line.
With all that being said, let's begin! I will separate these posts into different parts as there is a lot to cover. Part 1 will be setting up the environment and configuring the terraform code.
Requirements | Description |
---|---|
Terraform | Infrastructure as code software tool |
VS Code | Source code editor to create the terraform files |
Linode Account | Cloud Hosting Provider |
Chocolatey (Optional) | Windows Package Manager |
1. Environment Setup
If you haven't already done so, you'll need to install terraform. I recommend using Chocolatey Chocolatey Software | Chocolatey - The package manager for Windows as this is a great package manager for Windows. This is only available for windows but if you're using Linux, then you can use just APT (Advanced Package Tool).
- To install via PowerShell, use this command. I copied this from the official page
- Once installed, use the below command to install terraform
choco install terraform
- Now that terraform is installed, check and confirm by typing in "terraform". You may need to restart or re-open the console
Next install VS code via the following command
Ok great, now that we have terraform and visual studio code installed. We can start writing up the code.
2. Create terraform Files
First, we'll need to create the files. This file will contain most of the code to create the Linode instance
File | Purpose |
---|---|
main.tf | Build out the Linode instance |
terraform.tfvars | Input parameters for the Linode instance |
variables.tf | Used to define default variables |
terraform {
required_providers {
linode = {
source = "linode/linode"
version = "1.30.0"
}
}
}
provider "linode" {
token = var.linode_token
}
resource "tls_private_key" "ssh" {
algorithm = "RSA"
rsa_bits = "4096"
}
resource "local_file" "private_key" {
content = tls_private_key.ssh.private_key_pem
filename = "linode.pem"
file_permission = "0600"
}
resource "linode_volume" "data_volume" {
label = "data_volume"
size = 30
region = var.region
}
resource "linode_instance" "master" {
label = "k8s-master"
region = var.region
type = var.linode_type
disk {
label = "boot"
size = 50000
image = "linode/ubuntu20.04"
root_pass = var.root_password
authorized_keys = [chomp(tls_private_key.ssh.public_key_openssh)]
}
config {
label = "boot-existing-volume"
kernel = "linode/latest-64bit"
devices {
sda { disk_label = "boot" }
sdb { volume_id = linode_volume.data_volume.id }
}
}
}
variable "linode_token" {
description = "Your Linode API Personal Access Token. (required)"
}
variable "cluster_name" {
type = string
description = "The name of the Kubernetes cluster."
}
variable "region" {
type = string
description = "The region where the Linode instance should be created."
default = "ap-southeast"
}
variable "linode_type" {
type = string
description = "The type of Linode instance to create."
default = "g6-standard-4"
}
variable "kubeadm_token" {
type = string
description = "The kubeadm join token used to join worker nodes to the cluster."
}
variable "root_password" {
type = string
default = "p@assw0rd!@#$%"
}
variable "private_key_filename" {
type = string
default = "privatekey"
}
linode_token = "yourtoken"
cluster_name = "valheim-lke-cluster"
k8s_version = "1.25"
region = "ap-southeast"
linode_type = "g6-standard-2"
kubeadm_token = "abcdef.0123456789abcdef"
I'll briefly explain what each block does.
The main.tf
is the primary file where you define the resources that you want to create or manage in your infrastructure.
"terraform" {} block is configuring a provider for Linode in a Terraform configuration file.
"linode" {} block is used to define the specific cloud infrastructure provider that Terraform will use to provision and manage resources. The token
parameter is used to authenticate with the Linode API using an access token.You will need to generate a Linode API token. See Manage Personal Access Tokens | Linode
"tls_private_key" block will be used to generate the private key and public key to access the VM securely.
"local_file" block will create a local file from the private key block. Used to authenticate with linode instance. Permissions set to 600 for write and read for the owner.
"linode_instance" block will create the instance in Linode Cloud. This will create a ubuntu20.04 image using a local file for authentication from the "local_file" block
The variables.tf
file allows for a more flexible and reusable configuration, as they allow users to provide specific values. This will mean no hard-coded values within the main.tf code.
The variables.tf
file typically contains variable declarations in the form of variable
blocks, which specify the name, type, and default value (if any) of the input variable. One thing to note in my variables file. I have my password in the variable "root_password". This is not a good way to store your root password as anyone with access can see your password.
One common practice is to store the password in a secure vault, but an alternative option is to use environment variables. By utilizing environment variables, the password is not stored in version control and is only accessed at runtime. As an example, I have put the password in variables.tf for testing.
The terraform.tfvars
file is used to specify the input variables required by the Terraform configuration. We use this in this config to define the values without hard coding it in the main terraform config.
In this config, we are going to define the name and version of the cluster. Set the region to South East. The hardware we are going to use will be g6-standard-2 which consists of 2 VCPUs, 4 GB RAM. And lastly the kubeadm token. This token will be updated in the next part.
cluster_name = "valheim-lke-cluster"
k8s_version = "1.25"
region = "ap-southeast"
linode_type = "g6-standard-2"
kubeadm_token = "abcdef.0123456789abcdef"
3. Terraform Initialization
This next part is the easy part. We'll check whether our configuration is working. In the current directory in VS Code, open up the console using Ctrl+Shift+` and then type the below command. This will install the providers we've defined in our main.tf
terraform init
Next, we need to check the plan. This is a detailed plan of what your infrastructure would look like when you run terraform apply. This will show you what changes are expected, created or destroyed, and what errors are in your configuration. Since we have a tfvars file, we need to pass the flag -var-file
terraform plan -var-file="terraform.tfvars
Below is the expected result
PS C:\repo\linode> terraform plan -var-file="terraform.tfvars"
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# linode_instance.master will be created
+ resource "linode_instance" "master" {
+ backups = (known after apply)
+ backups_enabled = (known after apply)
+ boot_config_label = (known after apply)
+ booted = (known after apply)
+ host_uuid = (known after apply)
+ id = (known after apply)
+ ip_address = (known after apply)
+ ipv4 = (known after apply)
+ ipv6 = (known after apply)
+ label = "k8s-master"
+ private_ip_address = (known after apply)
+ region = "ap-southeast"
+ resize_disk = false
+ shared_ipv4 = (known after apply)
+ specs = (known after apply)
+ status = (known after apply)
+ swap_size = (known after apply)
+ type = "g6-standard-2"
+ watchdog_enabled = true
+ config {
+ kernel = "linode/latest-64bit"
+ label = "boot-existing-volume"
+ root_device = (known after apply)
+ run_level = "default"
+ virt_mode = "paravirt"
+ devices {
+ sda {
+ disk_id = (known after apply)
+ disk_label = "boot"
}
+ sdb {
+ disk_id = (known after apply)
+ volume_id = (known after apply)
}
}
}
+ disk {
+ authorized_keys = (known after apply)
+ filesystem = (known after apply)
+ id = (known after apply)
+ image = "linode/ubuntu20.04"
+ label = "boot"
+ read_only = (known after apply)
+ root_pass = (sensitive value)
+ size = 50000
+ stackscript_data = (sensitive value)
+ stackscript_id = (known after apply)
}
}
# linode_volume.data_volume will be created
+ resource "linode_volume" "data_volume" {
+ filesystem_path = (known after apply)
+ id = (known after apply)
+ label = "data_volume"
+ linode_id = (known after apply)
+ region = "ap-southeast"
+ size = 30
+ status = (known after apply)
}
# local_file.private_key will be created
+ resource "local_file" "private_key" {
+ content = (sensitive value)
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0600"
+ filename = "linode.pem"
+ id = (known after apply)
}
# tls_private_key.ssh will be created
+ resource "tls_private_key" "ssh" {
+ algorithm = "RSA"
+ ecdsa_curve = "P224"
+ id = (known after apply)
+ private_key_openssh = (sensitive value)
+ private_key_pem = (sensitive value)
+ private_key_pem_pkcs8 = (sensitive value)
+ public_key_fingerprint_md5 = (known after apply)
+ public_key_fingerprint_sha256 = (known after apply)
+ public_key_openssh = (known after apply)
+ public_key_pem = (known after apply)
+ rsa_bits = 4096
}
Plan: 4 to add, 0 to change, 0 to destroy.
Then finally, we apply the plan. Simply type the below command. This will take less than a couple of minutes to deploy
terraform apply -var-file="terraform.tfvars
4. Verify your Linode configuration by logging into the Linode dashboard
And there we have it. We have successfully deployed a Linode Instance! Please stay tuned for the upcoming section of the configuration where we will be setting up and configuring the Kubernetes cluster
Continue to Parts 2 & 3
Found this article useful? Why not buy Phi a coffee to show the your appreciation.