Benjamin Kušen
January 2, 2024

Infrastructure as Code: Create a Kubernetes Cluster With Terraform

In this article, we focus on employing HashiCorp's Terraform for the provisioning, deployment, and destruction of infrastructure resources.

Welcome to tour article dedicated to understanding and implementing Infrastructure as Code (IaC). This post is aimed at empowering developers with the ability to navigate IaC through comprehensive tutorials and practical code examples.

Infrastructure as Code (IaC), a key component of modern continuous integration pipelines, involves managing and provisioning cloud and IT resources via machine-readable definition files. It equips organizations with tools to systematically create, manage, and decommission computing resources by distinctly defining and declaring these resources in code.

Before proceeding, ensure that you have set up accounts with the concerned cloud providers and services, such as Google Cloud and Terraform Cloud. Armed with these prerequisites, join us in exploring the power of Terraform in creating a new Google Kubernetes Engine (GKE) cluster.

Prerequisites

Before diving into the intricacies of Infrastructure as Code and setting up a Kubernetes cluster with Terraform, it is essential to ensure that you have the following prerequisites in place:

A Google Cloud Platform (GCP) account

  • Google Cloud project
  • Local installation of the Google Cloud SDK CLI
  • Local installation of the Terraform CLI

A Terraform Cloud account

  • Terraform Cloud organization
  • Two new Terraform Cloud workspaces named iac_gke_cluster and iac_kubernetes_app. Select the "No VCS connection" option.
  • Enable local execution mode in both the iac_gke_cluster and iac_kubernetes_app workspaces
  • Create a new Terraform API token

A Docker Hub account

  • Local installation of the Docker client

Access to the Learn infrastructure as a code repo from GitHub

Git clone the repository onto your local machine

A CircleCI account

This tutorial utilizes the code found in the part01 folder of this repo. To begin, you first need to create GCP credentials and then set up Terraform.

Creating GCP Project Credentials

Creating GCP project credentials is a critical step in setting up your Infrastructure as Code toolset, as it grants you the ability to execute administrative actions.

To generate these credentials, follow these simple steps:

  • Visit the create service account key page.
  • Either select the default service account or create a new one.
  • Choose JSON as the key type.
  • Click on Create.
  • Save the JSON file in the ~/.config/gcloud/ directory. You may rename the file, if desired.

By completing these steps, you will successfully create your GCP project credentials, readying you for further Infrastructure as Code development.

How does HashiCorp Terraform work?

HashiCorp Terraform is an open-source tool designed to build, modify, and version infrastructure in a safe and efficient manner. It is capable of managing not only existing service providers but also custom, in-house solutions.

To accomplish its tasks, Terraform leverages configuration files, which describe the components required for running either a single application or an entire data center. By generating an execution plan, it details the steps needed to attain the desired state and, upon execution, constructs the infrastructure according to the given plan. Terraform intelligently identifies configuration changes and develops incremental execution plans for updating existing infrastructure resources.

The tool's versatility allows it to create, manage, and update various infrastructure resources, including physical machines, virtual machines, network switches, and containers. Terraform can comfortably manage low-level infrastructure components (compute instances, storage, networking) as well as high-level components, such as DNS entries and SaaS features.

With Terraform, almost any type of infrastructure can be depicted as a resource, demonstrating its extensive capabilities in managing modern infrastructure needs.

What is a Terraform provider?

A Terraform provider operates as an intermediary, interpreting API interactions and exposing resources. These providers can be Infrastructure as a Service (IaaS), such as Alibaba Cloud, AWS, GCP, Microsoft Azure, OpenStack; a Platform as a Service (PaaS) like Heroku; or Software as a Service (SaaS), including Terraform Cloud, DNSimple, Cloudflare.

During this phase, we will utilize Terraform code to provision resources in GCP. Our goal is to develop Terraform code that outlines and establishes a new GKE cluster which will be employed in the second part of this series.

In order to generate a new GKE cluster, we must depend on the GCP provider to manage our interactions with GCP. After defining and configuring the provider, we can then create and manage Terraform resources within the GCP environment.

What are Terraform resources?

Terraform resources are the fundamental components in the Terraform language. Each resource block is responsible for describing one or more infrastructure objects, which can range from virtual networks and compute instances to higher-level components such as DNS records.

A resource block defines a specific resource type (e.g., google_container_cluster) with a given local name, such as "web". This name serves as a reference to the resource within the same Terraform module, but it holds no significance beyond the scope of that particular module.

Understanding Terraform code

Having gained a deeper understanding of Terraform providers and resources, it's time to delve into the code. Terraform code is organized within directories, and as we're using the CLI tool, commands must be executed from within the root directories where the code is housed. In this tutorial, the Terraform code that we're working with is located in the part01/iac_gke_cluster folder.

This folder contains several key files:

  • providers.tf
  • variables.tf
  • main.tf
  • output.tf

These files represent the GCP resources infrastructure that we're aiming to create and are processed by Terraform. It's possible to consolidate all the Terraform code into a single file, but as the volume of syntax increases, this can become more challenging to manage. Consequently, most Terraform developers opt to create a separate file for each element. A quick breakdown of each file and critical attributes is provided subsequently.

Breakdown: providers.tf

We define the cloud provider in the provider.tf file. For this purpose, we will be utilizing the google_container_cluster provider. The content within the provider.tf file is as follows:

<pre class="codeWrap"><code>provider "google" {
 # version     = "2.7.0"
 credentials = file(var.credentials)
 project     = var.project
 region      = var.region
}
</code></pre>

In this block of code, parameters are included within { } brackets. The credentials parameter points to the file path of the GCP credential’s JSON file that was prepared previously. Take note that the values for parameters are prefixed with var.

This prefix var symbolizes the use of Terraform Input Variables. These are utilized as parameters for a Terraform module and enable the customization of the module without needing to modify the actual source code of the module. It also facilitates the sharing of modules across various configurations.

When you declare variables in the root module of your configuration, they can have their values set using CLI options and environment variables. On the other hand, when you declare them in child modules, these values are relayed by the calling module within the module block.

Breakdown: variables.tf

The variables.tf file outlines all the input variables implemented within the scope of this Terraform project.

<pre class="codeWrap"><code>variable "project" {
 default = "cicd-workshops"
}

variable "region" {
 default = "us-east1"
}

variable "zone" {
 default = "us-east1-d"
}

variable "cluster" {
 default = "cicd-workshops"
}

variable "credentials" {
 default = "~/.ssh/cicd_demo_gcp_creds.json"
}

variable "kubernetes_min_ver" {
 default = "latest"
}

variable "kubernetes_max_ver" {
 default = "latest"
}
</code></pre>

The variables delineated in this file are utilized broadly within this project. While all these variables carry default values, it's possible to alter these values via the CLI when executing Terraform code. The introduction of these variables adds significant flexibility to the code, enabling code reuse.

Breakdown: main.tf

The main.tf file primarily outlines the parameters of our GKE cluster.

<pre class="codeWrap"><code>terraform {
 required_version = "~>0.12"
 backend "remote" {
   organization = "datapunks"
   workspaces {
     name = "iac_gke_cluster"
   }
 }
}

resource "google_container_cluster" "primary" {
 name               = var.cluster
 location           = var.zone
 initial_node_count = 3

 master_auth {
   username = ""
   password = ""

client_certificate_config {
 issue_client_certificate = false
}
 }

 node_config {
   machine_type = var.machine_type
   oauth_scopes = [
     "https://www.googleapis.com/auth/logging.write",
     "https://www.googleapis.com/auth/monitoring",    
]

metadata = {
 disable-legacy-endpoints = "true"
}

labels = {
 app = var.app_name
}

tags = ["app", var.app_name]
 }
  timeouts {
   create = "30m"
   update = "40m"
 }
}
</code></pre>

Here is a description of every component in the main.tf file, starting with the terraform block. This block indicates the kind of Terraform backend being utilized. In Terraform, a "backend" governs how the state is loaded and how operations like 'apply' are implemented.

This kind of structure supports functionalities like state storage not tied to the local file system and remote execution. In this section of code, we are deploying the remote backend which employs the Terraform Cloud and is tied to the iac_gke_cluster workspace that was established in the section detailing the prerequisites.

<pre class="codeWrap">

<code>terraform {
 required_version = "~>0.12"
 backend "remote" {
   organization = "datapunks"
   workspaces {
     name = "iac_gke_cluster"
   }
 }
}
</code></pre>

The subsequent block of code outlines the GKE cluster that we aim to establish. Additionally, we employ a few of the variables described in variables.tf. The resource block comprises numerous parameters for provisioning and configuring the GKE Cluster on GCP.

Notable parameters include name, location, and Initial_node_count, which dictate the initial sum of virtual machines or compute resources that will constitute this new cluster. We will initiate this cluster with three compute nodes.

<pre class="codeWrap"><code> resource "google_container_cluster" "primary" {  name               = var.cluster  location           = var.zone  initial_node_count = 3

 master_auth {    username = ""    password = ""

client_certificate_config {
 issue_client_certificate = false
}

 }

 node_config {    machine_type = var.machine_type    oauth_scopes = [      "https://www.googleapis.com/auth/logging.write",      "https://www.googleapis.com/auth/monitoring",    ]

metadata = {
 disable-legacy-endpoints = "true"
}

labels = {
 app = var.app_name
}

tags = ["app", var.app_name]

 }

 timeouts {    create = "30m"    update = "40m"  }}
</code></pre>

Breakdown: output.tf

Terraform employs a feature known as output values. These represent the return values of a Terraform module, furnishing a child module with outputs. These outputs from the child module disclose a portion of the resource attributes to a parent module or exhibit specific values in the CLI output upon executing terraform apply.

In the subsequent code example, the output.tf blocks present output values to display information such as cluster name, cluster endpoint, and sensitive data, which is identified via the sensitive parameter.

<pre class="codeWrap"><code>output "cluster" {
 value = google_container_cluster.primary.name
}

output "host" {
 value     = google_container_cluster.primary.endpoint
 sensitive = true
}


output "cluster_ca_certificate" {
 value     = base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)
 sensitive = true
}

output "username" {
 value     = google_container_cluster.primary.master_auth.0.username
 sensitive = true
}

output "password" {
 value     = google_container_cluster.primary.master_auth.0.password
 sensitive = true
}
</code></pre>

Initializing Terraform

Having discussed our Terraform project and syntax, you can now proceed to provision the GKE cluster using Terraform. To do this, change the directory to the part01/iac_gke_cluster folder:

<pre class="codeWrap"><code>cd part01/iac_gke_cluster</code></pre>

While in part01/iac_gke_cluster, execute the following command:

<pre class="codeWrap"><code> terraform init</code></pre>

You should observe an output similar to the following:

<pre class="codeWrap"><code>root@d9ce721293e2:~/project/terraform/gcp/compute# terraform

initInitializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.10.0...

* provider.google: version = "~> 3.10"

Terraform has been successfully initialized!
</code></pre>

Previewing with Terraform

Terraform provides a command that enables you to carry out a dry run and validate your Terraform code without actually implementing anything. This command is termed terraform plan. This command also plots all the actions and modifications that Terraform will perform on your current infrastructure.

To execute this, run the following in the terminal:

<pre class="codeWrap"><code>terraform plan</code></pre>

The output:

<pre class="codeWrap"><code>An execution plan has been generated and is shown below.Resource actions are indicated with the following symbols:
   + create

Terraform will perform the following actions:

# google_container_cluster.primary will be created
+ resource "google_container_cluster" "primary" {
   + additional_zones            = (known after apply)
   + cluster_ipv4_cidr           = (known after apply)
   + default_max_pods_per_node   = (known after apply)
   + enable_binary_authorization = false
   + enable_intranode_visibility = (known after apply)
   + enable_kubernetes_alpha     = false
   + enable_legacy_abac          = false
   + enable_shielded_nodes       = false
   + enable_tpu                  = (known after apply)
   + endpoint                    = (known after apply)
   + id                          = (known after apply)
   + initial_node_count          = 3
   + instance_group_urls         = (known after apply)
   + label_fingerprint           = (known after apply)
   + location                    = "us-east1-d"
 }....
Plan: 1 to add, 0 to change, 0 to destroy.
</code></pre>

Terraform will establish new GCP resources for you, taking into account the code present in the main.tf file.

Terraform apply

At this point, you can proceed to construct the new infrastructure and launch the application.

Execute the following command in the terminal:

<pre class="codeWrap"><code>terraform apply</code></pre>

Terraform will request confirmation for your command execution. Input yes and press Enter.

<pre class="codeWrap"><code>Do you want to perform these actions?
 Terraform will perform the actions described above.
 Only 'yes' will be accepted to approve.

 Enter a value: yes
</code></pre>

Terraform will proceed to construct your new GKE cluster on GCP.

Please note: The completion of the cluster will take approximately 3-5 minutes. This is not an instantaneous process due to the back-end systems undertaking the provisioning and activation of the required components.

Upon completion of my cluster, I observed the following output:

<pre class="codeWrap"><code>Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:

cluster = cicd-workshops
cluster_ca_certificate = <sensitive>
host = <sensitive>
password = <sensitive>
username = <sensitive>
</code></pre>

The new GKE cluster has been successfully established and the resulting Outputs are shown. Observe that the output values marked as sensitive are concealed in the results using <sensitive> tags. This secures sensitive data, rendering it inaccessible but available for use when required.

Using Terraform destroy

Now that you have confirmed the successful creation of your GKE cluster, execute the terraform destroy command to eliminate the assets you created during this tutorial. You may opt to keep it operational, however, it's worth noting that all running assets on GCP will incur costs for which you will be responsible.

Google offers a substantial $300 credit for signing up for its free trial, but these funds can be quickly exhausted if you maintain running assets. The choice is yours, but executing terraform destroy will cease all active assets.

Execute the following command to demolish the GKE cluster:

<pre class="codeWrap"><code>terraform destroy</code></pre>

Conclusion

Congrats! You've just enhanced your skills by setting up and launching a Kubernetes cluster on GCP using Infrastructure as Code (IaC) and Terraform.

Facing Challenges in Cloud, DevOps, or Security?
Let’s tackle them together!

get free consultation sessions

In case you prefer e-mail first:

Thank you! Your message has been received!
We will contact you shortly.
Oops! Something went wrong while submitting the form.
By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information. If you wish to disable storing cookies, click here.