Skip to content

Hello Kubernetes⚓︎

This tutorial builds on Hello Docker to run a container image in kubernetes.

Create Cluster with Kind⚓︎

Since you probably don't have a spare kubernetes cluster laying around, we'll use kind to create a kubernetes cluster which runs in docker.

Install Kind⚓︎

From Installing From Release Binaries in the kind documentation.

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.30.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

Create Cluster⚓︎

Create a kind cluster named tutorial with kubernetes version 1.35.1.

kind create cluster --name tutorial --image kindest/node:v1.35.1

Connect to Cluster⚓︎

A kubernetes cluster isn't very useful if we don't have a way to interact with it. The primary tool we'll use is a command line interface called kubectl.

Install Kubectl⚓︎

From Kubernetes documentation

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install kubectl

Get Cluster Version⚓︎

Tip

If you have multiple clusters, you may want to run this command to make the kind-tutorial context active.

kubectl config use-context kind-tutorial
kubectl version
Output
Client Version: v1.34.2
Kustomize Version: v5.7.1
Server Version: v1.34.0

Create a Pod⚓︎

Next, we'll create a pod which runs a container, much like we did for Run the Container in docker.

Create Pod Definition⚓︎

If you're using Visual Studio Code, the Kubernetes Extension will provide completion and tooltips when creating resources.

Create a new file hello-node-pod.yaml and paste the following.

hello-node-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-node-pod #(1)!
spec:
  containers: #(2)!
    - name: app
      # Image we built in the previous tutorial (3)
      image: hello-node:1.0.1
      imagePullPolicy: Never #(4)!
      ports:
        # This port number should match index.js (5)
        - containerPort: 3000

  1. name can be anything that contains only letters, numbers, and dashes. In this tutorial, it's only used in kubectl commands.
  2. A pod can have more than one container, but we only need one in this tutorial.
  3. See Hello Docker
  4. Setting imagePullPolicy: Never is an unusual thing to do. We're only using it because we built our app's image locally. We know the image doesn't exist in a public registry, so we don't want to try to pull it from Docker Hub.
  5. See the line const port = 3000; from Hello World Wide Web - Add Source

Apply Pod Definition⚓︎

Apply the pod definition to create the pod in the cluster.

kubectl apply -f hello-node-pod.yaml
Output
pod/hello-node-pod created

Get Pods⚓︎

Show all pods to see the one we just created.

kubectl get pods
Output
NAME             READY   STATUS              RESTARTS   AGE
hello-node-pod   0/1     ErrImageNeverPull   0          12s

Note the 0/1 READY status which indicates the pod's container is not starting.

View Pod Events⚓︎

Let's take a look at the pod events to see why it's not READY.

kubectl events --for pod/hello-node-pod
Output
LAST SEEN              TYPE      REASON              OBJECT               MESSAGE
71s                    Normal    Scheduled           Pod/hello-node-pod   Successfully assigned default/hello-node-pod to tutorial-control-plane
10s (x7 over 70s)      Warning   ErrImageNeverPull   Pod/hello-node-pod   Container image "hello-node:1.0.1" is not present with pull policy of Never
10s (x7 over 70s)      Warning   Failed              Pod/hello-node-pod   Error: ErrImageNeverPull

The image hello-node:1.0.1 is not available in the cluster.

Load Image⚓︎

Run a kind command to load the docker image into the kubernetes cluster registry.

kind load docker-image --name tutorial hello-node:1.0.1
kubectl get pods
Output
NAME             READY   STATUS    RESTARTS   AGE
hello-node-pod   1/1     Running   0          3m15s

Show Logs⚓︎

Similar to docker logs, we can use kubectl to show the pod logs.

kubectl logs hello-node-pod
Output
Server running at http://0.0.0.0:3000/

Port Forwarding⚓︎

We'd like to access the app from a browser. We'll learn other ways to do this later, but for now we can forward a port on our host to the container. To make sure we're communicating with our app running in kubernetes instead of the previous tutorial version, we'll forward a different host port (3002) to container port 3000.

kubectl port-forward pod/hello-node-pod 3002:3000
Output
Forwarding from 127.0.0.1:3002 -> 3000
Forwarding from [::1]:3002 -> 3000

kubectl port-forward will keep running to handle connections. Open http://127.0.0.1:3002/ in the browser, and we can again see our Hello World greeting!

Hello World

kubectl port-forward will also generate some additional output.

Output
Handling connection for 3002

Now that we're done testing, press Ctrl+C in your terminal to stop the kubectl port-forward.

Cleanup⚓︎

Let's clean up the cluster that we created so the container doesn't consume resources.

Delete Pod⚓︎

If we're deleting the whole cluster anyways, it's not really necessary to delete the pod first. However, it's good to know how to delete it just in case.

kubectl delete -f hello-node-pod.yaml

Verify that the pod is gone.

kubectl get pod

This command is going to hang for awhile. Remember our old nemesis from Stopping the Container? We've got the same problem here, but kubernetes has different default behavior than docker. It will send SIGTERM to ask the container's main process, wait 30 seconds for it to exit, and then send SIGKILL which can't be ignored.

Output
No resources found in default namespace.

Delete Cluster⚓︎

While an idle kubernetes node doesn't consume many resources, it still requires some that we can free up by deleting the cluster. It's easy to recreate if you want to try more experiments.

kind delete cluster --name tutorial
Output
Deleting cluster "tutorial" ...
Deleted nodes: ["tutorial-control-plane"]

Review⚓︎

We were able to start our app in kubernetes, just like we did last time using docker. However, docker port-forward isn't a very friendly way to access the app. Next time, we'll see more advanced ways to deploy apps and route network traffic in kubernetes.