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
Post a Comment