In this blog post, we will talk about services, what it is used for and how to create and work with it. I assume you have a basic understanding of minikube, kubectl and deployment kind. A Kubernetes “Service” is an abstraction which defines a logical set of Pods and a policy by which to access them. In general,
- Pods targeted by a Service are found by a Label Selector.
- For Kubernetes-native applications, the Endpoints API will be updated whenever the set of Pods in a Service changes. For non-native applications, there is a virtual-IP-based bridge to Services which redirects to the backend Pods.
- Service will be assigned an IP address (“cluster IP”), which is used by the service proxies.
- Service can map an incoming port to any targetPort. (By default the targetPort will be set to the same value as the port field and it can defined as a string.)
- The actual port number assigned to that name can be different in each backend Pod. For example, you can change the port number that pods expose in the next version of your backend software, without breaking clients.
- Service support TCP, UDP and SCTP for protocols. The default is TCP.
- Service can be defined with or without a selector.
- Service supports multiple port definitions.
Types of service?
There are 4 types of service
- ClusterIP – Exposes the service on a cluster-internal IP. Service is only reachable from within the cluster. This is the default Type.
- NodePort – Exposes the service on each Node’s IP at a static port. A ClusterIP service, to which the NodePort service will route, is automatically created. You’ll be able to contact the NodePort service, from outside the cluster, by using “<NodeIP>:<NodePort>”.
- LoadBalancer – Exposes the service externally using a cloud provider’s load balancer. NodePort and ClusterIP services, to which the external load balancer will route, are automatically created.
- ExternalName – Maps the service to the contents of the externalName field (e.g. foo.bar.example.com), by returning a CNAME record with its value.
Before moving forward, it is worth going over the role of kube-proxy in kubernetes, it s responsible for implementing a form of virtual IP for Services of type other than ExternalName. To achieve this, there are 3 possible modes you can set.
- Proxy-mode: userspace – In this mode, kube-proxy watches the Kubernetes master for the addition and removal of Service and Endpoints objects. For each Service it opens a randomly chosen port on the local node. Any connections to this “proxy port” will be proxied to one of the Service’s backend Pods (as reported in Endpoints).
- Proxy-mode: iptables – In this mode, kube-proxy watches the Kubernetes master for the addition and removal of Service and Endpoints objects. For each Service, it installs iptables rules which capture traffic to the Service’s clusterIP (which is virtual) and Port and redirects that traffic to one of the Service’s backend sets. For each Endpoints object, it installs iptables rules which select a backend Pod. By default, the choice of backend is random.
- Proxy-mode: ipvs – In this mode, kube-proxy watches Kubernetes Services and Endpoints, calls netlink interface to create ipvs rules accordingly and syncs ipvs rules with Kubernetes Services and Endpoints periodically, to make sure ipvs status is consistent with the expectation. When Service is accessed, traffic will be redirected to one of the backend Pods.
There are two ways to discover service in kubernetes. By ENV variable or DNS.
- ENV Var – When a Pod is run on a Node, the kubelet adds a set of environment variables for each active Service.
- DNS – The DNS server is a cluster add-on that watches the Kubernetes API for new Services and creates a set of DNS records for each. If DNS has been enabled throughout the cluster then all Pods should be able to do name resolution of Services automatically. This is the recommended option.
Sometimes you don’t need or want load-balancing and a single service IP. In this case, you can create “headless” services by specifying “None” for the cluster IP (.spec.clusterIP).
With selectors – For headless services that define selectors, the endpoints controller creates Endpoints records in the API, and modifies the DNS configuration to return A records (addresses) that point directly to the Pods backing the Service.
Without selectors – For headless services that do not define selectors, the endpoints controller does not create Endpoints records. However, the DNS system looks for and configures either:
- CNAME records for ExternalName-type services.
- A records for any Endpoints that share a name with the service, for all other types.
Creating a service?
To give us an idea of how to use a service, let us take a look at a simple example where we have two instances of a Hello World application running. We use a deployment kind to create our hello world application. After deployment is up and running, we will create a service for our application with type ClusterIP.
First, let us create a deployment by running “kubectl run hello-world –replicas=2 –labels=”run=load-balancer-example” –image=gcr.io/google-samples/node-hello:1.0 –port=8080”. Without going into too much detail, this command creates a deployment with 2 replicas of our hello world application. If we run “kubectl get deployment hello-world”, we will see that our deployment is up and we can check our replicaset and pods created with the deployment.
$ kubectl get deployments hello-world NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE hello-world 2 2 2 2 56s
Now that we have our applications running, how do we access it? We create a service with type “ClusterIP”. We can create a yaml manifest for the service, then apply it or we can use “kubectl expose” command on our deployment (expose command creates a service for our deployment without creating a yaml file). The easiest is the kubectl expose command.
$ kubectl expose deployment hello-world --type=ClusterIP --name=example-service service "example-service" exposed
Here we are creating a service named example-service for our hello-world application with type ClusterIP.
To access our application, we run “kubectl get service example-service ” to get our port number, then we run as special command called port-forward. The reason is because our service type is ClusterIP and since this type of service can only be accessed from the within the cluster, the only way to access our app from our workstation is to forward the port to a local port. We can use other types like “LoadBalanacer” for example which will create a LB in AWS or GCP, then we can access the app using the dns address given to the LB with our port number.
$ kubectl get service example-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE example-service ClusterIP 220.127.116.11 <none> 8080/TCP 1h
$ kubectl port-forward service/example-service 8080:8080 Forwarding from 127.0.0.1:8080 -> 8080
Now we can browse http://localhost:8080 from our workstation and we should see
These postings are my own and do not necessarily represent BMC's position, strategies, or opinion.
See an error or have a suggestion? Please let us know by emailing email@example.com.