Kubernetes

How to Create a Kubernetes Service

Damian Igbe
Feb. 26, 2022, 4:32 p.m.

Subscribe to Newsletter

Be first to know about new blogs, training offers, and company news.

A  Kubernetes service is a very important abstraction to realize cloud-native microservice applications. It is used as a frontend to a set of pods – pods are ephemeral and can die at any time, and that is ok. Recall from here that  we used deployment to monitor our pods to create new ones whenever pods die or disappear. To cope with the ephemeral nature of pods, we need something that is not ephemeral. Something that is always there (permanent) and can be used until deleted. This is similar to the concept of a load balancer that acts as the frontend to a set of backend services by exposing a virtual IP (see type of Kubernetes services below). Kubernetes' services are used by humans and other Kubernetes services to connect to pods behind the services.

Creating a Kubernetes Service

A Kubernetes service uses Kubernetes Labels to identify the pods to connect to. It’s always a good practice to create a service before creating the pods. The order matters, especially when environmental variables are used for name resolution of the services. The other method of name resolution is the DNS service, but with DNS, the order doesn’t really matter. Let’s use the manifest files below to create a service and a deployment. For the nginx pods, we’ll use the deployment manifest that was created here, posted below:

apiVersion: apps/v1beta1 
metadata:
  name: deployment-example
spec:
  replicas: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.2
        ports:
        - containerPort: 80
        resources:           
          requests:                
            cpu: 100m             
            memory: 100Mi

In this deployment manifest file, 5 nginx containers will be created and identified with the label app:nginx. Note that you have the option to combine the 2 manifest files into one, delimited by  – – but here we are creating them separately.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - name: web-frontend
      port: 80
  selector:
    app: nginx

Now that we have the 2 manifest files, let’s create a service, followed by creating a deployment.

$kubectl create -f service.yaml

$kubectl create -f deployment.yaml

$kubectl get svc
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.0.0.1     <none>        443/TCP   5d
nginx        10.0.0.89    <none>        80/TCP    16m

$kubectl describe svc nginx
Name:            nginx
Namespace:        default
Labels:            app=nginx
Annotations:        <none>
Selector:        app=nginx
Type:            ClusterIP
IP:            10.0.0.89
Port:            web    80/TCP
Endpoints:        172.17.0.10:80,172.17.0.3:80,172.17.0.5:80 + 2 more...
Session Affinity:    None
Events:            <none>

Here we see that the service has been created and mapped to the 5 pods that were created using deployment. To check that the IP addresses under Endpoints match the IP addresses of the pods connected to the service, let's run this command.

kubectl get pods -l app=nginx -o yaml | grep podIP
    podIP: 172.17.0.5
    podIP: 172.17.0.7
    podIP: 172.17.0.10
    podIP: 172.17.0.3
    podIP: 172.17.0.9

In this case, the service  selects all the matching Pods based on app=nginx labels along with their IP addresses, and we see that they match with the IP addresses in the Endpoint's section of the service.

Discovering services

Environment variables and DNS are the  2 primary modes of finding a Service.

Environment variables

When a Pod is run on a Node, the Kubelet adds a set of environment variables to the container for each active Service. For example, the Service “nginx” which exposes TCP port 80 and has been allocated a clusterIP address of 10.0.0.89 produces the following environment variables:

$kubectl exec deployment-example-1421084195-0smn4 env
NGINX_PORT=tcp://10.0.0.89:80
NGINX_PORT_80_TCP_PROTO=tcp
NGINX_SERVICE_HOST=10.0.0.89
NGINX_PORT_80_TCP=tcp://10.0.0.89:80
NGINX_PORT_80_TCP_PORT=80
NGINX_PORT_80_TCP_ADDR=10.0.0.89
NGINX_SERVICE_PORT=80
NGINX_SERVICE_PORT_WEB=80

As stated above, any Service that a Pod wants to access must be created before the Pod itself, or else the environment variables will not be populated. DNS does not have this restriction.

DNS

DNS is an optional cluster add-on, though strongly recommended. When installed, the DNS server 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.

For example, for the nginx’s service created above in the Kubernetes ‘default’ namespace, a DNS record for “nginx.default” is created. Pods which exist in the “default” Namespace should be able to find it by simply doing a name lookup for “nginx”. Pods which exist in other Namespaces must qualify the name as “default.nginx”. The result of these name lookups is the cluster IP.

 Types of Kubernetes Services

There are currently 4 types of Kubernetes services, as depicted in Kubernetes  documentation.

  • ClusterIP: Exposes the service on a cluster-internal IP. ClusterIP  makes the service only reachable from within the cluster. This is the default ServiceType.
  • NodePort: Exposes the service on each Node’s IP at a static port (the NodePort). The Kubernetes master will allocate a port from a flag-configured range (default: 30000-32767).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 requesting <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. No proxying of any kind is set up. The Kubernetes DNS server is the only way to access services of type ExternalName.

Kubernetes ServiceTypes allow you to specify what kind of service you want. The default is ClusterIP.

Type LoadBalancer

If the cloud providers support external load balancers, a load balancer can be provisioned by setting the type field to “LoadBalancer”. Here is an example:

kind: Service
apiVersion: v1
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8000
    nodePort: 2500
  clusterIP: 10.0.0.200
  loadBalancerIP: 78.11.13.17
  type: LoadBalancer

Note the following:

  • Traffic from the external load balancer will be directed to the backend Pods, though exactly how that works depends on the cloud provider.
  • Some cloud providers allow the loadBalancerIP to be specified. If that is the case, the load-balancer is  created with the user-specified loadBalancerIP. When  loadBalancerIP field is not specified, an ephemeral IP is assigned to the loadBalancer. If the loadBalancerIP is specified, but the cloud provider does not support the feature, the field is ignored. For more details, refer to Kubernetes  documentation.

Conclusion

In this tutorial, you learned how to create a Kubernetes Service object. A service presents a permanent face to ephemeral pods. There are different types of services that can be created, with ClusterIP being the default.

Zero-to-Hero Program: We Train and Mentor you to land your first Tech role