Objectives

In this lab, I will:

  • Use the kubectl CLI
  • Create a Kubernetes Pod
  • Create a Kubernetes Deployment
  • Create a ReplicaSet that maintains a set number of replicas
  • Witness Kubernetes load balancing in action

Cloud IDE

Verify the environment and command line tools

$ kubectl version
WARNING: This version information is deprecated and will be replaced
with the output from kubectl version --short.  Use --output=yaml|json
to get the full version.

Client Version: version.Info
{
  Major:"1",
  Minor:"27",
  GitVersion:"v1.27.6",
  GitCommit:"741c8db18a52787d734cbe4795f0b4ad860906d6",
  GitTreeState:"clean", BuildDate:"2023-09-13T09:21:34Z",
  GoVersion:"go1.20.8",
  Compiler:"gc", Platform:"linux/amd64"
}

Kustomize Version: v5.0.1
Server Version: version.Info
{
  Major:"1",
  Minor:"26",
  GitVersion:"v1.26.13+IKS",
  GitCommit:"fbfb72fd9f7d07c29a23849656a07ce69833b650",
  GitTreeState:"clean",
  BuildDate:"2024-01-18T18:12:58Z",
  GoVersion:"go1.20.13",
  Compiler:"gc",
  Platform:"linux/amd64"
}
$ [ ! -d 'CC201' ] && git clone \
  https://github.com/ibm-developer-skills-network/CC201.git

$ ls
Dockerfile  app.js  hello-world-apply.yaml
hello-world-create.yaml  package.json

Using the kubectl CLI

Kubernetes namespaces enable to virtualize a cluster. We already have access to one namespace in a Kubernetes cluster, kubectl is already set to target that cluster and namespace.

# kubectl requires configuration so that it targets the appropriate cluster.
$ kubectl config get-clusters
NAME
labs-prod-kubernetes-sandbox/c8ana0

# kubectl  context is a group of access parameters, including a cluster,
# a user, and a namespace.
$ kubectl config get-contexts
CURRENT NAME       CLUSTER                              AUTHINFO   NAMESPACE
*       x-context  labs-prod-kubernetes-sandbox/c8ana0  x          sn-labs-x

# list all the pods in your namespace.
$ kubectl get pods
No resources found in sn-labs-x namespace.

Create a Pod with an imperative command

We create a pod that run the hello-world image you built and push it to IBM Cloud Container Registry in the previous lab.

# exporting namespace as an environment variable so that
# it can be used in subsequent commands.
$ export MY_NAMESPACE=sn-labs-$USERNAME

# build and push the image
$ docker build -t us.icr.io/$MY_NAMESPACE/hello-world:1 . && docker push us.icr.io/$MY_NAMESPACE/hello-world:1

# run the hello-world image as a container in Kubernetes
$ kubectl run hello-world --image us.icr.io/$MY_NAMESPACE/hello-world:1 --overrides='{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"icr"}]}}}}'
  pod/hello-world created

# list the pods in your namespace
$ kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
hello-world   1/1     Running   0          11s

# the wide option for the output to get more details about the resource
$ kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE     IP               NODE          NOMINATED NODE   READINESS GATES
hello-world   1/1     Running   0          2m31s   172.17.153.207   10.241.64.6   <none>           <none>

# describe the pod to get more details about it
$ kubectl describe pod hello-world

# delete the pod
$ kubectl delete pod hello-world

# list the pods to verify that none exist
$ kubectl get pods

Create a Pod with imperative object configuration

# hello-world-create.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  containers:
    - name: hello-world
      image: us.icr.io/sn-labs-x/hello-world:1
      ports:
        - containerPort: 8080
  imagePullSecrets:
    - name: icr
# imperative create a Pod using the provided configuration file
$ kubectl create -f hello-world-create.yaml
  pod/hello-world created

# list the pods in your namespace
$ kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
hello-world   1/1     Running   0          15s

# delete the pod
$ kubectl delete pod hello-world
pod "hello-world" deleted

# list the pods to verify that non exist
$ kubectl get pods
No resources found in sn-labs-x namespace.

Create a Pod with declarative command

# hello-world-apply.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  generation: 1
  labels:
    run: hello-world
  name: hello-world
spec:
  replicas: 3
  selector:
    matchLabels:
      run: hello-world
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        run: hello-world
    spec:
      containers:
        - image: us.icr.io/<my_namespace>/hello-world:1
          imagePullPolicy: Always
          name: hello-world
          ports:
            - containerPort: 8080
              protocol: TCP
          resources:
            limits:
              cpu: 2m
              memory: 30Mi
            requests:
              cpu: 1m
              memory: 10Mi
      imagePullSecrets:
        - name: icr
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      securityContext: {}
      terminationGracePeriodSeconds: 30
# to set this configuration as the desired state in Kubernetes
$ kubectl apply -f hello-world-apply.yaml
deployment.apps/hello-world created

# get the deployments to ensure that the deployment was created
$ kubectl get deployments
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
hello-world   3/3     3            3           56s

# list the pods to ensure that three replicas exist
$ kubectl get pods
NAME                           READY   STATUS    RESTARTS   AGE
hello-world-5d7cc94cb5-npdtg   1/1     Running   0          92s
hello-world-5d7cc94cb5-pskk6   1/1     Running   0          92s
hello-world-5d7cc94cb5-rks8g   1/1     Running   0          92s

# delete the pod and created a new one immediately
$ kubectl delete pod hello-world-5d7cc94cb5-npdtg && kubectl get pods
pod "hello-world-5d7cc94cb5-npdtg" deleted
NAME                           READY   STATUS    RESTARTS   AGE
hello-world-5d7cc94cb5-pskk6   1/1     Running   0          4m29s
hello-world-5d7cc94cb5-rks8g   1/1     Running   0          4m29s
hello-world-5d7cc94cb5-xn2tp   1/1     Running   0          34s

Load balancing the application

// app.js
var express = require('express');
var os = require('os');
var hostname = os.hostname();
var app = express();

app.get('/', function (req, res) {
  res.send('Hello world, ' + hostname + '! Your app is up and running!\n');
});
app.listen(8080, function () {
  console.log('Sample app is listening on port 8080.');
});
# Dockerfile
FROM node:9.4.0-alpine
COPY app.js .
COPY package.json .
RUN npm install &&\
    apk update &&\
    apk upgrade
EXPOSE  8080
CMD node app.js
# access the application, expose it to the internet using Kubernetes service
$ kubectl expose deployment/hello-world

# list service in order to see that this service was created.
$ kubectl get services
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
hello-world   ClusterIP   172.21.142.190   <none>        8080/TCP   2m41s

# create a proxy to make it accessible outside of the cluster
$ kubectl proxy
Starting to serve on 127.0.0.1:8001

# ping the application to get a response
$ curl -L localhost:8001/api/v1/namespaces/sn-labs-$USERNAME/services/hello-world/proxy
Hello world, hello-world-5d7cc94cb5-xn2tp! Your app is up and running!

# run the command in loop, observe the Kubernetes load balancing the request
# across the three replicas, each request could hit a different instance
# of our application.
$ for i in `seq 10`; do curl -L localhost:8001/api/v1/namespaces/sn-labs-$USERNAME/services/hello-world/proxy; done
Hello world, hello-world-5d7cc94cb5-pskk6! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-pskk6! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-xn2tp! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-pskk6! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-xn2tp! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-pskk6! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-rks8g! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-rks8g! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-pskk6! Your app is up and running!
Hello world, hello-world-5d7cc94cb5-xn2tp! Your app is up and running!

# delete the deployment and service
$ kubectl delete deployment/hello-world service/hello-world
  deployment.apps "hello-world" deleted
  service "hello-world" deleted

Reference