How to Run Kubernetes on Localhost (Terraform, Mac)

In this article, I will show you how to run Kubernetes locally (localhost). It's useful for people who need to teach themselves new skills or build a prototype without having to pay for hosting.

How to Run Kubernetes on Localhost (Terraform, Mac)

How do you run Kubernetes on a Mac?

You can run Kubernetes on a Mac (as localhost) using Docker for Desktop and Terraform.

I will show you how to set that up in the steps below.

Step 1. Install Terraform

You can find Terraform install instructions for Mac, Windows, and Linux here:

I followed the instructions for using brew to install it on my Mac.

ūüí°
If you are on a Mac and don't have brew installed, see the instructions here: https://brew.sh

On a Mac, you can install Terraform like this:

brew tap hashicorp/tap
brew install hashicorp/tap/terraform

Check the version

To check the version, run this command:

 terraform version 

If you get a warning that your version is out of date, ignore the instructions to download and do this instead:

brew upgrade terraform

Step 2. Run Docker

To run Docker on a Mac, run this command from a terminal window:

open -a Docker
ūüí°
If you don't have Docker installed on your Mac, see here: https://docs.docker.com/desktop/install/mac-install/

Step 3. Enable Kubernetes

On a Mac, open the Docker for Desktop Settings dialog. This can usually be found using the Docker icon in the top right main menu bar.

  • Find the Kubernetes tab or window
  • Check Enable Kubernetes
  • If applicable, click Apply & Restart

I've noticed that this can take a while.  But I've also had many issues with Docker for Desktop getting corrupt.  If that happens then use the button to reset your environment. But only as a last resort if it seems to be stuck after 20 minutes.

There are alternative ways to run Kubernetes on a Mac.  But that's beyond the scope of this article.

Step 4. Install kubectl

The command line tool that I use all the time to interact with Kubernetes is kubectl.

To install kubectl on a Mac, run the following:

brew install kubectl

If you already had it installed, that command will upgrade it.

Check the version

To check the version of kubectl, run this command:

kubectl version

Check that Kubernetes is configured properly

To check that Kubernetes is running properly, run this command:

kubectl cluster-info

On a typical Mac setup, you should see a line like this:

Kubernetes control plane is running at https://kubernetes.docker.internal:6443

We will need to verify that the setting matches in a later step.

Step 5. Create a project folder

To create a project folder, run these commands:

mkdir ~/projects/terraform/terraform-kubernetes-101
cd ~/projects/terraform/terraform-kubernetes-101

Step 6.  Create a main.tf file

Terraform works by reading *.tf files. Those files contain declarative statements defining the cloud infrastructure you want to build.  By default, it looks for a file called main.tf .

  • Create a file called main.tf in the root of your project:
  • Open main.tf in an IDE like Visual Studio Code (VS Code)
  • If VS Code prompts you about extensions to support the *.tf file, go ahead and install them
touch main.tf

Step 7. Declare a provider

For this example, we're going to be working with Kubernetes.  So we can specify the use of a Kubernetes provider.

In Terraform you can think of a provider as a plugin, that provides extra third-party resource types and data sources. You can use them to define your infrastructure.

To use the recommended Kubernetes provider, add this to the top of main.tf and save it:

# Configure Kubernetes provider and connect to the Kubernetes API server
provider "kubernetes" {
  host = "https://localhost:6443"
  config_context_cluster = "docker-desktop"
  config_path            = "~/.kube/config"
}

This setup assumes the standard defaults when running Kubernetes on a Mac using Docker for Desktop.

Verify settings

Verify the settings.  If they don't match your environment, update the declaration above to match.

Verify host setting

To verify that the host setting matches the Kubernetes control plane (default: https://localhost:6443), run this command:

kubectl cluster-info

Verify config_context_cluster

To verify that the config_context_cluster setting matches the current Kubernetes context (default: docker-desktop), run this command:

kubectl config current-context

Verify config_path

By default on a Mac, your Kubernetes config (config_path above) should be here ~/.kube/config.  You can verify that with this command:

ls -la ~/.kube/config

You can also check some of the settings with this command:

kubectl config view

Step 8. Declare a Kubernetes pod

Kubernetes is a "container orchestrator". You can run a container (like a Docker container) in what's known in Kubernetes as a pod. Going into detail is beyond the scope, but I will link to some references at the end of this article.  For now, we just want to get a container up and running in a pod.

To declare a pod,  add this block at the end of main.tf and save it:

# Create a pod
resource "kubernetes_pod" "random_server" {
  metadata {
    name = "random-example"
    labels = {
      App = "random_server"
    }
  }

  spec {
    container {
      image = "mitchallen/random-server:latest"
      name  = "random"
      port {
        container_port = 3100
      }
    }
  }
}

This declaration will create a pod using one of my random docker containers.  By default, that container maps to port 3100.

You can add multiple pods to a Kubernetes cluster. But to keep things simple, here we are only adding one.

Step 9. Declare a Kubernetes service

Besides a pod, you usually need to also declare a Kubernetes service. Think of a service as a way to open up your container so it can be accessed by the outside world, usually using a port.

To declare a service,  add this block at the end of main.tf and save it:

# Create a service
resource "kubernetes_service" "random" {
  metadata {
    name = "random-example"
  }
  spec {
    selector = {
      App = "${kubernetes_pod.random_server.metadata.0.labels.App}"
    }
    port {
      port = 80
      target_port = 3100
    }

    type = "NodePort"
  }
}

The selector refers back to the metadata of the pod that was declared earlier.  It also maps its port.

Step 10. Terraform init

The first thing you should always do with a new Terraform project is run this command (once):

terraform init

The command does several things to initialize the current directory to work with Terraform.  For more information on the command, see the references section.

Step 11. Format the *.tf file

To format the main.tf file, do the following:

  • Save all files in your project
  • Run this command:
terraform fmt

It's an optional step that can keep your code clean and consistent.

Step 12. Run the plan

Once you've saved all the files, it's time to make a Terraform plan.  Think of it as a dry run.  Terraform parses your *.tf file(s) to determine how to build the infrastructure that you've defined.  Or it may fail with an error - like if your docker host is incorrect.

Run this command to see if a plan can be generated successfully.

terraform plan

If you get an error regarding docker: either docker isn't running, or you need to try a different docker endpoint passed to the provider.

You may see a warning like this:

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

You can get by for this demo without creating an out file.  But eventually, you should learn how to use them.

Step 13. Apply the plan

To actually make the changes, run this command:

terraform apply

When prompted, type Yes to agree to apply the plan.

Hopefully you will get a response like this while your project is being created:

kubernetes_pod.random_server: Creating...
kubernetes_pod.random_server: Creation complete after 6s [id=default/random-example]
kubernetes_service.random: Creating...
kubernetes_service.random: Creation complete after 1s [id=default/random-example]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Step 14. Check the results

Once your project is up and running, check the pods with this command:

kubectl get pods

You should get back a response like this:

NAME             READY   STATUS    RESTARTS   AGE
random-example   1/1     Running   0          39s

The pod name (random-example) should match what was defined main.tf.

Step 15. Get the port

Now we have to figure out what port Kubernetes used for NodePort to make the service accessible to the outside world.  To do that, run this command:

kubectl get svc random-example -o jsonpath='{.spec.ports[0].nodePort}'

On my Mac, I got back a port value of 30427.  So to verify that my service is available I ran this command (adjust for whatever port was returned to you on your machine):

curl localhost:30427

You should see a response from my service like this:

{"status":"OK","app":"random-server","version":"1.0.2","uptime":"00:05:16","route":"/"}

Step 17. Check the services

Another way to get the NodePort is to look at the services. You can do that with this command:

kubectl get services

You should get back a response like this:

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.......       <none>        443/TCP        79m
random-example   NodePort    10.........     <none>        80:30427/TCP   6s

See how the NodePort (30427 in my case) for the service (random-example) matches above?

Step 18. Generate a graph

Terraform has a graph command that can be used to create a visual representation of the plan. It does this by generating DOT code.

Run this command to see an example of the generated DOT code:

terraform graph -type=plan

Install dot

To turn the output of the graphic command into a picture, you need to install the special dot cli tool. For a Mac, you would do it like this:

brew install graphviz

For other operating systems, use this link to find install instructions:

Generate a plan picture

Once the dot utility is installed, you can pipe the instructions through it to turn the plan into a picture.

Run this command to:

  • Turn the plan into dot code
  • Pipe it through the dot utility and ¬†
  • Open and view the result
terraform graph -type=plan | dot -Tpng > plan.png

open plan.png

Step 19. Cleanup

When you are done experimenting, Terraform makes it very easy to clean up.

You can remove the Kubernetes pods and services by running this command:

terraform destroy
  • Type yes when asked to confirm

Verify cleanup

Run this command to verify that your pods were removed:

kubectl get pods

Step 20. Add .gitignore

The makers of Terraform have posted an example .gitignore file, which you can copy from here:

To use it:

  • Create a new file called .gitignore in the root of your project
  • Copy the contents from the file in the link to your .gitignore file and save it

Example project

You can find the example project here:

Conclusion

In this article, you learned how to:

  • Install Terraform on a Mac
  • Use Terraform to define a Kubernetes Infrastructure as Code (IaC)
  • Install kubectl to interact with Kubernetes from the command-line
  • Initiate, plan, and apply a Terraform project
  • Define Kubernetes pods, services and ports
  • Create a visual representation of a Terraform plan
  • Clean up using a simple command

References

  • developer.hashicorp.com/terraform - [1]
  • developer.hashicorp.com/terraform/downloads - [2]
  • github.com/hashicorp/terraform-provider-kubernetes - [3]
  • kubernetes.io/docs/tasks/tools/install-kubectl-macos/ - [4]
  • kubernetes.io/docs/reference/kubectl/cheatsheet/ - [5]
  • www.redhat.com/sysadmin/kubeconfig - [6]
  • www.redhat.com/en/topics/containers/what-is-kubernetes-pod - [7]
  • hub.docker.com/r/mitchallen/random-server - [8]
  • kubernetes.io/docs/concepts/services-networking/service/ - [9]
  • kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/ - [10]