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:
user@ubuntu:~/$ cd ~user@ubuntu:~$ mkdir ~/svc && cd ~/svcuser@ubuntu:~/svc$ nano svc.yamluser@ubuntu:~/svc$ cat svc.yaml
apiVersion: v1kind: Servicemetadata:name: testweblabels:name: testwebspec:type: ClusterIPports:- port: 80selector:run: testweb
user@ubuntu:~/svc$
Now create the service:
user@ubuntu:~/svc$ kubectl apply -f svc.yamlservice/testweb createduser@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:
user@ubuntu:~/svc$ kubectl get servicesNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 10.96.0.1 <none> 443/TCP 156mtestweb ClusterIP 10.98.126.85 <none> 80/TCP 14suser@ubuntu:~/svc$ SVC=$(kubectl get service testweb -o template --template={{.spec.clusterIP}}) && echo $SVC10.98.126.85user@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:
user@ubuntu:~/svc$ kubectl run -it --generator=run-pod/v1 testclient --image=busybox:latestIf you don't see a command prompt, try pressing enter./ #
Try to wget your service’s Cluster IP from inside the pod:
/ # wget -O - 10.98.126.85Connecting to 10.98.126.85 (10.98.126.85:80)wget: can't connect to remote host (10.98.126.85): Connection refused/ #
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.
user@ubuntu:~$ cd ~/svcuser@ubuntu:~/svc$ nano web.yamluser@ubuntu:~/svc$ cat web.yaml
apiVersion: v1kind: Podmetadata:name: bigwebstufflabels:run: testwebspec:containers:- name: web-containerimage: nginxports:- containerPort: 80
user@ubuntu:~/svc$
Now run the pod:
user@ubuntu:~/svc$ kubectl apply -f web.yamlpod/bigwebstuff createduser@ubuntu:~/svc$
With the pod up, retry the service IP from the test pod:
/ # wget -O - 10.98.126.85Connecting to 10.98.126.85 (10.98.126.85:80)writing to stdout<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.</p><p>For online documentation and support please refer to<a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p></body></html>- 100% |*********************************************************************************************************************************************************************************************************************************************************************************************************| 612 0:00:00 ETAwritten to stdout/ #
On the host, describe your service to verify the wiring between the ClusterIP and the container in the pod:
user@ubuntu:~/svc$ kubectl describe service testweb
Name: testwebNamespace: defaultLabels: name=testwebAnnotations: kubectl.kubernetes.io/last-applied-configuration:{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"name":"testweb"},"name":"testweb","namespace":"default"},"spec...Selector: run=testwebType: ClusterIPIP: 10.98.126.85Port: <unset> 80/TCPTargetPort: 80/TCPEndpoints: 10.32.0.5:80Session Affinity: NoneEvents: <none>
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:
user@ubuntu:~/svc$ kubectl get pod bigwebstuff -o json | jq -r '.status | .podIP, .hostIP'10.32.0.5192.168.228.157user@ubuntu:~/svc$
Try hitting the pod by its pod IP from the test container:
/ # wget -O - 10.32.0.5Connecting to 10.32.0.5 (10.32.0.5:80)writing to stdout<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.</p><p>For online documentation and support please refer to<a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p></body></html>- 100% |*********************************************************************************************************************************************************************************************************************************************************************************************************| 612 0:00:00 ETAwritten to stdout/ #
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:
/ # exitSession ended, resume using 'kubectl attach testclient -c testclient -i -t' command when the pod is runninguser@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.
user@ubuntu:~/svc$ nano webd.yamluser@ubuntu:~/svc$ cat webd.yaml
apiVersion: apps/v1kind: Deploymentmetadata:name: bigwebstufflabels:name: bigwebstuffspec:replicas: 3selector:matchLabels:run: testwebtemplate:metadata:labels:run: testwebspec:containers:- name: podwebimage: nginx:latestports:- containerPort: 80
user@ubuntu:~/svc$
Now create the deployment:
user@ubuntu:~/svc$ kubectl apply -f webd.yamldeployment.apps/bigwebstuff createduser@ubuntu:~/svc$
Now let’s see what happened to our pods and our service:
user@ubuntu:~/svc$ kubectl describe service testweb
Name: testwebNamespace: defaultLabels: name=testwebAnnotations: kubectl.kubernetes.io/last-applied-configuration:{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"name":"testweb"},"name":"testweb","namespace":"default"},"spec...Selector: run=testwebType: ClusterIPIP: 10.98.126.85Port: <unset> 80/TCPTargetPort: 80/TCPEndpoints: 10.32.0.5:80,10.32.0.6:80,10.32.0.7:80Session Affinity: NoneEvents: <none>
user@ubuntu:~/svc$
List the running Pods:
user@ubuntu:~/svc$ kubectl get podsNAME READY STATUS RESTARTS AGEbigwebstuff 1/1 Running 0 4m8sbigwebstuff-6c4c6bf494-8bhgx 1/1 Running 0 60sbigwebstuff-6c4c6bf494-bkrqv 1/1 Running 0 60sbigwebstuff-6c4c6bf494-tx27b 1/1 Running 0 60stestclient 1/1 Running 1 5m30suser@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:
user@ubuntu:~/svc$ kubectl delete deploy bigwebstuffdeployment.apps "bigwebstuff" deleteduser@ubuntu:~/svc$ kubectl delete pods bigwebstuff testclientpod "bigwebstuff" deletedpod "testclient" deleteduser@ubuntu:~/svc$ kubectl delete svc testwebservice "testweb" deleteduser@ubuntu:~/svc$ cd ~user@ubuntu:~$
Use kubectl get all to list all the remaining resources in the default namespace:
user@ubuntu:~$ kubectl get allNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 165muser@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
