Skip to main content

K8s Activities 1

Introduction to Kubernetes Activities:

Objectives:

In this lab, you 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


1. Verify that kubectl CLI is installed:

theia@theiadocker-mohiindia:/home/project$ kubectl version

Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.2", GitCommit:"f5743093fd1c663cb0cbc89748f730662345d44d", GitTreeState:"clean", BuildDate:"2020-09-16T13:41:02Z", GoVersion:"go1.15", Compiler:"gc", Platform:"linux/amd64"}

Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.14+IKS", GitCommit:"35f1768b1292e15e3e5ca2e31658ecf675c4d5a6", GitTreeState:"clean", BuildDate:"2020-11-12T18:37:40Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}


2. Clone the git repository that contains the artifacts needed for this lab, if it doesn't already exist:

theia@theiadocker-mohiindia:/home/project$ [ ! -d 'cc201' ] && git clone https://gitlab.com/ibm/skills-network/courses/cc201.git

Cloning into 'cc201'...

remote: Enumerating objects: 755, done.

remote: Counting objects: 100% (755/755), done.

remote: Compressing objects: 100% (391/391), done.

remote: Total 755 (delta 424), reused 654 (delta 352), pack-reused 0

Receiving objects: 100% (755/755), 6.43 MiB | 19.66 MiB/s, done.

Resolving deltas: 100% (424/424), done.


3. Change to the directory for this lab:

theia@theiadocker-mohiindia:/home/project$ cd cc201/labs/2_IntroKubernetes/

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ pwd

/home/project/cc201/labs/2_IntroKubernetes


4. List the contents of this directory to see the artifacts for this lab:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ ls

app.js  Dockerfile  hello-world-apply.yaml  hello-world-create.yaml  images  instructions.md  package.json


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ cat hello-world-create.yaml 

apiVersion: v1

kind: Pod

metadata:

  name: hello-world

spec:

  containers:

  - name: hello-world

    image: us.icr.io/<my_namespace>/hello-world:1

    ports:

    - containerPort: 80

  imagePullSecrets:

  - name: icr


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ cat 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: 80

          protocol: TCP

      imagePullSecrets:

      - name: icr

      dnsPolicy: ClusterFirst

      restartPolicy: Always

      securityContext: {}

      terminationGracePeriodSeconds: 30


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ cat 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


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ cat 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 from ' + hostname + '! Your app is up and running!\n')

})

app.listen(8080, function() {

  console.log('Sample app is listening on port 8080.')

})


5. Use the kubectl CLI

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

Let's look at some basic kubectl commands.

kubectl requires configuration so that it targets the appropriate cluster. Get cluster information with the following command:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl config get-clusters

NAME

labs-user-sandbox-prod-tor01/bp1hokjw0e5bk715r7f0


6. A kubectl context is a group of access parameters, including a cluster, a user, and a namespace. View your current context with the following command:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl config get-contexts

CURRENT   NAME                CLUSTER                                             AUTHINFO    NAMESPACE

*         mohiindia-context   labs-user-sandbox-prod-tor01/bp1hokjw0e5bk715r7f0   mohiindia   sn-labs-mohiindia



7. List all the Pods in your namespace. If this is a new session for you, you will not see any Pods:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods

No resources found in sn-labs-mohiindia namespace.



8. Create a Pod with an imperative command

Now it's time to create your first Pod. This Pod will run the hello-world image you built and pushed to IBM Cloud Container Registry in the last lab. As explained in the videos for this module, you can create a Pod imperatively or declaratively. Let's do it imperatively first.


Export your namespace as an environment variable so that it can be used in subsequent commands. Make sure to substitute your namespace after the equals sign. If you don't remember your lab account namespace, run "ibmcloud cr namespaces".

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ ibmcloud cr namespaces

Listing namespaces for account 'QuickLabs - IBM Skills Network' in registry 'us.icr.io'...


Namespace   

sn-labs-mohiindia   


OK

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ export MY_NAMESPACE=ibmcloud cr namespaces

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ 


9. Build and push the image again, as it may have been deleted automatically since you completed the first lab:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ docker build -t us.icr.io/$MY_NAMESPACE/hello-world:1 . && docker push us.icr.io/$MY_NAMESPACE/hello-world:1

Sending build context to Docker daemon  268.8kB

Step 1/6 : FROM node:9.4.0-alpine

9.4.0-alpine: Pulling from library/node

605ce1bd3f31: Pull complete 

fe58b30348fe: Pull complete 

46ef8987ccbd: Pull complete 

Digest: sha256:9cd67a00ed111285460a83847720132204185e9321ec35dacec0d8b9bf674adf

Status: Downloaded newer image for node:9.4.0-alpine

 ---> b5f94997f35f

Step 2/6 : COPY app.js .

 ---> 6eaef72196e4

Step 3/6 : COPY package.json .

 ---> 6000506895ca

Step 4/6 : RUN npm install &&    apk update &&    apk upgrade

 ---> Running in a63d4f51b8f4

npm notice created a lockfile as package-lock.json. You should commit this file.

npm WARN hello-world-demo@0.0.1 No repository field.

npm WARN hello-world-demo@0.0.1 No license field.


added 50 packages in 2.529s

fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz

fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/community/x86_64/APKINDEX.tar.gz

v3.6.5-44-gda55e27396 [http://dl-cdn.alpinelinux.org/alpine/v3.6/main]

v3.6.5-34-gf0ba0b43d5 [http://dl-cdn.alpinelinux.org/alpine/v3.6/community]

OK: 8448 distinct packages available

Upgrading critical system libraries and apk-tools:

(1/1) Upgrading apk-tools (2.7.5-r0 -> 2.7.6-r0)

Executing busybox-1.26.2-r9.trigger

Continuing the upgrade transaction with new apk-tools:

(1/7) Upgrading musl (1.1.16-r14 -> 1.1.16-r15)

(2/7) Upgrading busybox (1.26.2-r9 -> 1.26.2-r11)

Executing busybox-1.26.2-r11.post-upgrade

(3/7) Upgrading libressl2.5-libcrypto (2.5.5-r0 -> 2.5.5-r2)

(4/7) Upgrading libressl2.5-libssl (2.5.5-r0 -> 2.5.5-r2)

(5/7) Installing libressl2.5-libtls (2.5.5-r2)

(6/7) Installing ssl_client (1.26.2-r11)

(7/7) Upgrading musl-utils (1.1.16-r14 -> 1.1.16-r15)

Executing busybox-1.26.2-r11.trigger

OK: 5 MiB in 15 packages

Removing intermediate container a63d4f51b8f4

 ---> cae9d31427b3

Step 5/6 : EXPOSE  8080

 ---> Running in ecf3c9e6e6b5

Removing intermediate container ecf3c9e6e6b5

 ---> 5215f586a1a4

Step 6/6 : CMD node app.js

 ---> Running in c0c87db0e728

Removing intermediate container c0c87db0e728

 ---> 2cca284e45d3

Successfully built 2cca284e45d3

Successfully tagged us.icr.io/ibmcloud/hello-world:1

The push refers to repository [us.icr.io/ibmcloud/hello-world]

44817cbc18d6: Preparing 

778ff3b847d4: Preparing 

0e5257df4676: Preparing 

0804854a4553: Preparing 

6bd4a62f5178: Preparing 

9dfa40a0da3b: Waiting 

denied: requested access to the resource is denied

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ 


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ docker images

REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE

us.icr.io/ibmcloud/hello-world   1                   2cca284e45d3        2 minutes ago       76.2MB

node                             9.4.0-alpine        b5f94997f35f        2 years ago         68MB


10. Run the hello-world image as a container in Kubernetes:


The --overrides option here enables us to specify the needed credentials to pull this image from IBM Cloud Container Registry. Note that this is an imperative command, as we told Kubernetes explicitly what to do: run hello-world.


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl run hello-world --image us.icr.io/$MY_NAMESPACE/hello-world:1 --overrides='{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"icr"}]}}}}'

pod/hello-world created


11. List the Pods in your namespace:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods

NAME          READY   STATUS             RESTARTS   AGE

hello-world   0/1     ImagePullBackOff   0          71s


12. Great, the previous command indeed created a Pod for us. You can see an auto-generated name was given to this Pod.


You can also specify the wide option for the output to get more details about the resource.

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods -o wide

NAME          READY   STATUS             RESTARTS   AGE    IP               NODE            NOMINATED NODE   READINESS GATES

hello-world   0/1     ImagePullBackOff   0          2m3s   172.30.161.209   10.114.85.169   <none>           <none>


13. Describe the Pod to get more details about it:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl describe pod hello-world

Name:         hello-world

Namespace:    sn-labs-mohiindia

Priority:     0

Node:         10.114.85.169/10.114.85.169

Start Time:   Thu, 10 Dec 2020 13:54:09 +0000

Labels:       run=hello-world

Annotations:  kubernetes.io/limit-ranger:

                LimitRanger plugin set: cpu, ephemeral-storage, memory request for container hello-world; cpu, ephemeral-storage, memory limit for contain...

              kubernetes.io/psp: ibm-privileged-psp

Status:       Pending

IP:           172.30.161.209

IPs:

  IP:  172.30.161.209

Containers:

  hello-world:

    Container ID:   

    Image:          us.icr.io/ibmcloud/hello-world:1

    Image ID:       

    Port:           <none>

    Host Port:      <none>

    State:          Waiting

      Reason:       ImagePullBackOff

    Ready:          False

    Restart Count:  0

    Limits:

      cpu:                500m

      ephemeral-storage:  3Gi

      memory:             512Mi

    Requests:

      cpu:                200m

      ephemeral-storage:  512Mi

      memory:             128Mi

    Environment:          <none>

    Mounts:

      /var/run/secrets/kubernetes.io/serviceaccount from default-token-vtd7x (ro)

Conditions:

  Type              Status

  Initialized       True 

  Ready             False 

  ContainersReady   False 

  PodScheduled      True 

Volumes:

  default-token-vtd7x:

    Type:        Secret (a volume populated by a Secret)

    SecretName:  default-token-vtd7x

    Optional:    false

QoS Class:       Burstable

Node-Selectors:  <none>

Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 600s

                 node.kubernetes.io/unreachable:NoExecute op=Exists for 600s

Events:

  Type     Reason     Age                  From               Message

  ----     ------     ----                 ----               -------

  Normal   Scheduled  2m41s                default-scheduler  Successfully assigned sn-labs-mohiindia/hello-world to 10.114.85.169

  Normal   Pulling    80s (x4 over 2m40s)  kubelet            Pulling image "us.icr.io/ibmcloud/hello-world:1"

  Warning  Failed     80s (x4 over 2m39s)  kubelet            Failed to pull image "us.icr.io/ibmcloud/hello-world:1": rpc error: code = Unknown desc = failed to pull and unpack image "us.icr.io/ibmcloud/hello-world:1": failed to resolve reference "us.icr.io/ibmcloud/hello-world:1": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed

  Warning  Failed     80s (x4 over 2m39s)  kubelet            Error: ErrImagePull

  Warning  Failed     51s (x6 over 2m39s)  kubelet            Error: ImagePullBackOff

  Normal   BackOff    36s (x7 over 2m39s)  kubelet            Back-off pulling image "us.icr.io/ibmcloud/hello-world:1"


14. Delete the Pod:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl delete pod hello-world

pod "hello-world" deleted


15. List the Pods to verify that none exist:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods

No resources found in sn-labs-mohiindia namespace.


16. Create a Pod with imperative object configuration:

Imperative object configuration lets you create objects by specifying the action to take (e.g., create, update, delete) while using a configuration file. A configuration file, hello-world-create.yaml, is provided to you in this directory.

Use the Explorer to view and edit the configuration file. Click the Explorer icon (it looks like a sheet of paper) on the left side of the window, and then navigate to the directory for this lab: cc201 > labs > 2_IntroKubernetes. Click hello-world-create.yaml to view the configuration file.


apiVersion: v1

kind: Pod

metadata:

  name: hello-world

spec:

  containers:

  - name: hello-world

    image: us.icr.io/sn-labs-mohiindia/hello-world:1

    ports:

    - containerPort: 80

  imagePullSecrets:

  - name: icr


17. Imperatively create a Pod using the provided configuration file:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl create -f hello-world-create.yaml 

pod/hello-world created

Note that this is indeed imperative, as you explicitly told Kubernetes to create the resources defined in the file.



18. List the Pods in your namespace:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods

NAME          READY   STATUS             RESTARTS   AGE

hello-world   0/1     ImagePullBackOff   0          58s


19. Delete the Pod:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl delete pod hello-world

pod "hello-world" deleted


20. Create a Pod with a declarative command:

The previous two ways to create a Pod were imperative -- we explicitly told kubectl what to do. While the imperative commands are easy to understand and run, they are not ideal for a production environment. Let's look at declarative commands.


A sample hello-world-apply.yaml file is provided in this directory. Use the Explorer again to open this file. Notice the following:

We are creating a Deployment (kind: Deployment).

There will be three replica Pods for this Deployment (replicas: 3).

The Pods should run the hello-world image (- image: us.icr.io/<my_namespace>/hello-world:1). You can ignore the rest for now. We will get to a lot of those concepts in the next lab.

Use the Explorer to edit hello-world-apply.yaml. You need to insert your namespace where it says <my_namespace>. Make sure to save the file when you're done.


Use the kubectl apply command to set this configuration as the desired state in Kubernetes.


theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl apply -f hello-world-apply.yaml 

deployment.apps/hello-world created


21. Get the Deployments to ensure that a Deployment was created:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get deployments

NAME          READY   UP-TO-DATE   AVAILABLE   AGE

hello-world   0/3     3            0           43s


22. List the Pods to ensure that three replicas exist:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods

NAME                         READY   STATUS             RESTARTS   AGE

hello-world-8bdd4cdc-98bdv   0/1     ImagePullBackOff   0          76s

hello-world-8bdd4cdc-gs7nr   0/1     ImagePullBackOff   0          76s

hello-world-8bdd4cdc-pldf8   0/1     ImagePullBackOff   0          76s


With declarative management, we did not tell Kubernetes which actions to perform. Instead, kubectl inferred that this Deployment needed to be created. If you delete a Pod now, a new one will be created in its place to maintain three replicas.


23. Note one of the Pod names from the previous step, and delete that Pod:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl delete pod hello-world-8bdd4cdc-gs7nr

pod "hello-world-8bdd4cdc-gs7nr" deleted


24. List the Pods to see a new one being created:

If you do this quickly enough, you can see one Pod being terminated and another Pod being created.

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods

NAME                         READY   STATUS             RESTARTS   AGE

hello-world-8bdd4cdc-98bdv   0/1     ImagePullBackOff   0          4m6s

hello-world-8bdd4cdc-fx2zz   0/1     ImagePullBackOff   0          23s   --> Automatically created

hello-world-8bdd4cdc-pldf8   0/1     ImagePullBackOff   0          4m6s


25. Load balancing the application:

Since there are three replicas of this application deployed in the cluster, Kubernetes will load balance requests across these three instances. Let's expose our application to the internet and see how Kubernetes load balances requests.


In order to access the application, we have to expose it to the internet using a Kubernetes Service:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl expose deployment/hello-world --type=NodePort --port=8080 --name=hello-world --target-port=8080

service/hello-world exposed


This command creates what is called a NodePort Service. This will open up a port on the worker node to allow the application to be accessed using that port and the IP address of the node.


26. List Services in order to see that this service was created:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get services

NAME          TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE

hello-world   NodePort   172.21.244.165   <none>        8080:30721/TCP   37s


27. Two things are needed to access this application: a worker node IP address and the correct port. To get a worker node IP, rerun the get pods command with the wide option and note any one of the node IP addresses (from the column entitled NODE):

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get pods -o wide

NAME                         READY   STATUS             RESTARTS   AGE     IP              NODE            NOMINATED NODE   READINESS GATES

hello-world-8bdd4cdc-98bdv   0/1     ImagePullBackOff   0          8m50s   172.30.245.69   10.114.85.140   <none>           <none>

hello-world-8bdd4cdc-fx2zz   0/1     ImagePullBackOff   0          5m7s    172.30.16.239   10.114.85.137   <none>           <none>

hello-world-8bdd4cdc-pldf8   0/1     ImagePullBackOff   0          8m50s   172.30.47.51    10.114.85.155   <none>           <none>


Using this sample output, you could choose 10.114.85.140, 10.114.85.137, or 10.114.85.155 for the node IP address.


28. Export the node IP address as an environment variable. Make sure to substitute the copied IP address into this command:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ export NODE_IP=10.114.85.140  or  export NODE_IP=10.114.85.137  or export NODE_IP=10.114.85.155


29. To get the port number, run the following command and note the port:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl get services

NAME          TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE

hello-world   NodePort   172.21.244.165   <none>        8080:30721/TCP   4m10s


30. Export the port as an environment variable. Make sure to substitute the copied port into this command:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ export NODE_PORT=30721


31. Ping the application to get a response:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ curl 10.114.85.137:30721


32. Notice that this output includes the Pod name. Run the command ten times and note the different Pod names in each line of output:


for i in `seq 10`; do curl 10.114.85.137:30721; done


You should see more than one Pod name, and quite possibly all three Pod names, in the output. This is because Kubernetes load balances the requests across the three replicas, so each request could hit a different instance of our application.


33. Delete the Deployment and Service. This can be done in a single command by using slashes:

theia@theiadocker-mohiindia:/home/project/cc201/labs/2_IntroKubernetes$ kubectl delete deployment/hello-world service/hello-world

deployment.apps "hello-world" deleted

service "hello-world" deleted


Congratulations!

Comments

Popular posts from this blog

K8s Architecture

Container runtime: Remember, Kubernetes is a container orchestration tool. Hence it requires some container runtime software for running the containers. Kubernetes supports several container runtimes: Docker, containerd, CRI-O, and any implementation of the Kubernetes CRI (Container Runtime Interface). Kubernetes Architecture vs Kubernetes Cluster: When you deploy Kubernetes, you get a cluster. Yes We can also say it as Kubernetes Architecture since it contains entire block of Kubernetes Architecture. A Kubernetes cluster consists of a set of worker machines, also called nodes, that run containerized applications. Every cluster has at least one worker node. The worker node(s) host the Pods that are the components of the application workload. The control plane manages the worker nodes and the Pods in the cluster.  In production environments, the control plane usually runs across multiple computers and a cluster usually runs multiple nodes, providing fault-tolerance and high availability