rx-m-kubernetes-bootcamp-lab-05 - 图1

Kubernetes

Lab 5 – Services

Kubernetes pods are mortal. They are born and they die, and they are not resurrected. ReplicaSets create and destroy
Pods dynamically (e.g. when scaling up or down or when doing rolling updates). While each pod gets its own IP address,
even those IP addresses cannot be relied upon to be stable over time. This leads to a problem; if some set of backend
Pods provides functionality to other frontend Pods inside the Kubernetes cluster, how do the frontends find out and keep
track of the backends?

Services

A Kubernetes Service is an abstraction which defines a logical set of pods and a policy by which to access them. The set
of pods targeted by a Service is determined by a label selector. As an example, consider an image-processing backend
which is running with 3 replicas. Those replicas are fungible, frontends do not care which backend they use. While the
actual pods that compose the backend set may change, the frontend clients should not need to be aware of that or keep
track of the list of backends themselves. The Service abstraction enables this decoupling.

For applications integrated with the Kubernetes control plane, Kubernetes offers a simple Endpoints API that is updated
whenever the set of pods in a Service changes. For Kubernetes hosted applications, Kubernetes offers a virtual-IP-based
façade which redirects connections to the backend pods. For applications outside of the Kubernetes cluster, Kubernetes
offers a cluster wide port forwarding feature that provides a way for external traffic to enter the pod network and
reach a service’s pods.

In Kubernetes, a Service is just a JSON object in etcd, similar to a Pod. Like all of the “REST” objects, a Service
definition can be POSTed to the kube-apiserver to create a new service instance. The service controller then acts on
service specifications reserving Cluster IPs and Node Ports as needed. This in turn causes the KubeProxy agents and/or
SDN implementations to take local action on each node (modifying iptables/ipvs/etc.).

1. A Simple Service

Let’s begin by creating a simple service using a service config file. Before you begin delete any services (except
the kubernetes service), resource controllers, pods or other resources you may have running.

Now create a simple service called “testweb” with its own ClusterIP passing traffic on port 80 and configure the
service to use the selector “run=testweb”.

Something like this:

  1. user@ubuntu:~/$ cd ~
  2. user@ubuntu:~$ mkdir ~/svc && cd ~/svc
  3. user@ubuntu:~/svc$ nano svc.yaml
  4. user@ubuntu:~/svc$ cat svc.yaml
  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: testweb
  5. labels:
  6. name: testweb
  7. spec:
  8. type: ClusterIP
  9. ports:
  10. - port: 80
  11. selector:
  12. run: testweb
  1. user@ubuntu:~/svc$

Now create the service:

  1. user@ubuntu:~/svc$ kubectl apply -f svc.yaml
  2. service/testweb created
  3. user@ubuntu:~/svc$

N.B. Your host, service and pod ip addresses will be different from those shown in the lab examples. Be sure to substitute the correct addresses from you lab system as you work through the lab.

List the services in your namespace:

  1. user@ubuntu:~/svc$ kubectl get services
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 156m
  4. testweb ClusterIP 10.98.126.85 <none> 80/TCP 14s
  5. user@ubuntu:~/svc$ SVC=$(kubectl get service testweb -o template --template={{.spec.clusterIP}}) && echo $SVC
  6. 10.98.126.85
  7. user@ubuntu:~/svc$

Great we have a service running. Now what?

Let’s try to use it. To make use of services we need to be in the pod network. Some SDN solutions provide an onramp
(route) on the nodes to the pod network but the ClusterIP is virtual so it is often not available outside of running
pods. Services are always available inside pods though so we’ll run a pod to use as a test client.

Run a pod for testing the service:

  1. user@ubuntu:~/svc$ kubectl run -it --generator=run-pod/v1 testclient --image=busybox:latest
  2. If you don't see a command prompt, try pressing enter.
  3. / #

Try to wget your service’s Cluster IP from inside the pod:

  1. / # wget -O - 10.98.126.85
  2. Connecting to 10.98.126.85 (10.98.126.85:80)
  3. wget: can't connect to remote host (10.98.126.85): Connection refused
  4. / #

We have created a service and it has an IP but the IP is virtual and there’s no pod(s) for the proxy to send the traffic
to. Services truly can outlive their implementations.

To fix this lack of implementation we can create a pod with a label that matches the service selector.

Leave your test pod running, open a new terminal on the host and create a simple nginx pod to support your service and
run it.

  1. user@ubuntu:~$ cd ~/svc
  2. user@ubuntu:~/svc$ nano web.yaml
  3. user@ubuntu:~/svc$ cat web.yaml
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: bigwebstuff
  5. labels:
  6. run: testweb
  7. spec:
  8. containers:
  9. - name: web-container
  10. image: nginx
  11. ports:
  12. - containerPort: 80
  1. user@ubuntu:~/svc$

Now run the pod:

  1. user@ubuntu:~/svc$ kubectl apply -f web.yaml
  2. pod/bigwebstuff created
  3. user@ubuntu:~/svc$

With the pod up, retry the service IP from the test pod:

  1. / # wget -O - 10.98.126.85
  2. Connecting to 10.98.126.85 (10.98.126.85:80)
  3. writing to stdout
  4. <!DOCTYPE html>
  5. <html>
  6. <head>
  7. <title>Welcome to nginx!</title>
  8. <style>
  9. body {
  10. width: 35em;
  11. margin: 0 auto;
  12. font-family: Tahoma, Verdana, Arial, sans-serif;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <h1>Welcome to nginx!</h1>
  18. <p>If you see this page, the nginx web server is successfully installed and
  19. working. Further configuration is required.</p>
  20. <p>For online documentation and support please refer to
  21. <a href="http://nginx.org/">nginx.org</a>.<br/>
  22. Commercial support is available at
  23. <a href="http://nginx.com/">nginx.com</a>.</p>
  24. <p><em>Thank you for using nginx.</em></p>
  25. </body>
  26. </html>
  27. - 100% |*********************************************************************************************************************************************************************************************************************************************************************************************************| 612 0:00:00 ETA
  28. written to stdout
  29. / #

On the host, describe your service to verify the wiring between the ClusterIP and the container in the pod:

  1. user@ubuntu:~/svc$ kubectl describe service testweb
  1. Name: testweb
  2. Namespace: default
  3. Labels: name=testweb
  4. Annotations: kubectl.kubernetes.io/last-applied-configuration:
  5. {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"name":"testweb"},"name":"testweb","namespace":"default"},"spec...
  6. Selector: run=testweb
  7. Type: ClusterIP
  8. IP: 10.98.126.85
  9. Port: <unset> 80/TCP
  10. TargetPort: 80/TCP
  11. Endpoints: 10.32.0.5:80
  12. Session Affinity: None
  13. Events: <none>
  1. user@ubuntu:~/svc$

So as you can see our nginx container must be listening on 10.32.0.5.

Use kubectl to dispaly the pod and host IPs:

  1. user@ubuntu:~/svc$ kubectl get pod bigwebstuff -o json | jq -r '.status | .podIP, .hostIP'
  2. 10.32.0.5
  3. 192.168.228.157
  4. user@ubuntu:~/svc$

Try hitting the pod by its pod IP from the test container:

  1. / # wget -O - 10.32.0.5
  2. Connecting to 10.32.0.5 (10.32.0.5:80)
  3. writing to stdout
  4. <!DOCTYPE html>
  5. <html>
  6. <head>
  7. <title>Welcome to nginx!</title>
  8. <style>
  9. body {
  10. width: 35em;
  11. margin: 0 auto;
  12. font-family: Tahoma, Verdana, Arial, sans-serif;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <h1>Welcome to nginx!</h1>
  18. <p>If you see this page, the nginx web server is successfully installed and
  19. working. Further configuration is required.</p>
  20. <p>For online documentation and support please refer to
  21. <a href="http://nginx.org/">nginx.org</a>.<br/>
  22. Commercial support is available at
  23. <a href="http://nginx.com/">nginx.com</a>.</p>
  24. <p><em>Thank you for using nginx.</em></p>
  25. </body>
  26. </html>
  27. - 100% |*********************************************************************************************************************************************************************************************************************************************************************************************************| 612 0:00:00 ETA
  28. written to stdout
  29. / #

All we needed to do to enable our service was to create a pod with the right label. Note that if our pod container dies,
no one will restart it as things now stand, but our service will carry on.

Exit the testclient container before proceeding:

  1. / # exit
  2. Session ended, resume using 'kubectl attach testclient -c testclient -i -t' command when the pod is running
  3. user@ubuntu:~/svc$

2. Add a Resource Controller to Your Service

To improve the robustness of our service implementation we can switch from a pod to a resource controller. Change your
config to instantiate a deployment which creates 3 replicas and with a template just like the pod we launched
in the last step.

  1. user@ubuntu:~/svc$ nano webd.yaml
  2. user@ubuntu:~/svc$ cat webd.yaml
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: bigwebstuff
  5. labels:
  6. name: bigwebstuff
  7. spec:
  8. replicas: 3
  9. selector:
  10. matchLabels:
  11. run: testweb
  12. template:
  13. metadata:
  14. labels:
  15. run: testweb
  16. spec:
  17. containers:
  18. - name: podweb
  19. image: nginx:latest
  20. ports:
  21. - containerPort: 80
  1. user@ubuntu:~/svc$

Now create the deployment:

  1. user@ubuntu:~/svc$ kubectl apply -f webd.yaml
  2. deployment.apps/bigwebstuff created
  3. user@ubuntu:~/svc$

Now let’s see what happened to our pods and our service:

  1. user@ubuntu:~/svc$ kubectl describe service testweb
  1. Name: testweb
  2. Namespace: default
  3. Labels: name=testweb
  4. Annotations: kubectl.kubernetes.io/last-applied-configuration:
  5. {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"name":"testweb"},"name":"testweb","namespace":"default"},"spec...
  6. Selector: run=testweb
  7. Type: ClusterIP
  8. IP: 10.98.126.85
  9. Port: <unset> 80/TCP
  10. TargetPort: 80/TCP
  11. Endpoints: 10.32.0.5:80,10.32.0.6:80,10.32.0.7:80
  12. Session Affinity: None
  13. Events: <none>
  1. user@ubuntu:~/svc$

List the running Pods:

  1. user@ubuntu:~/svc$ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. bigwebstuff 1/1 Running 0 4m8s
  4. bigwebstuff-6c4c6bf494-8bhgx 1/1 Running 0 60s
  5. bigwebstuff-6c4c6bf494-bkrqv 1/1 Running 0 60s
  6. bigwebstuff-6c4c6bf494-tx27b 1/1 Running 0 60s
  7. testclient 1/1 Running 1 5m30s
  8. user@ubuntu:~/svc$
  • What happened to our old pod?
  • How many pods did the RS create?
  • What does the age output in the get pods command tell you?
  • What are the pods names with the suffixes from?

Service selector and Replica Set selector behavior differ in subtle ways. You will notice the Service has 4 pods, while
the Replica Set has 3.

Challenge

Without changing any of the running resources, create a new deployment that runs the httpd image with 2 replicas
such that the service will send traffic to it as well. Test your service to verify proper operation using curl in the
test container.

N.B. Apache httpd returns a different startup message than that returned by nginx

Cleanup

Delete the deployment, pods and service created in this lab using kubectl delete:

  1. user@ubuntu:~/svc$ kubectl delete deploy bigwebstuff
  2. deployment.apps "bigwebstuff" deleted
  3. user@ubuntu:~/svc$ kubectl delete pods bigwebstuff testclient
  4. pod "bigwebstuff" deleted
  5. pod "testclient" deleted
  6. user@ubuntu:~/svc$ kubectl delete svc testweb
  7. service "testweb" deleted
  8. user@ubuntu:~/svc$ cd ~
  9. user@ubuntu:~$

Use kubectl get all to list all the remaining resources in the default namespace:

  1. user@ubuntu:~$ kubectl get all
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 165m
  4. user@ubuntu:~$

All that should remain is the kubernetes service.

Congratulations you have completed the lab.

Copyright (c) 2013-2020 RX-M LLC, Cloud Native Consulting, all rights reserved