Detailed explanation of k8s service

Posted by xadmin on Sun, 26 Dec 2021 18:53:15 +0100

Detailed explanation of k8s service


Kubernetes Service defines such an abstraction: a logical set of pods, a policy that can access them - commonly known as microservices. This group of pods can be accessed by the Service, usually through the selector.

For example, consider an image processing backend that runs three copies. These copies are interchangeable -- the front end doesn't care which back-end copy they call. However, the Pod that makes up this group of back-end programs may actually change. The front-end client should not and does not need to know, and does not need to track the status of this group of back-end programs. The abstraction of Service definition can decouple this association.

Service can provide load balancing, but there are the following restrictions on its use:

  • It can only provide 4-tier load balancing capabilities, but not 7-tier functions. Sometimes we may need more matching rules to forward requests, which is not supported by layer 4 load balancing

As shown in the example of web access service:

VIP and Service agents

In the Kubernetes cluster, each Node runs a Kube proxy process. Kube proxy implements a form of VIP (virtual IP) for the Service instead of the form of ExternalName.

From kubernetes v1 Starting from 0, the userspace proxy mode can be used. Kubernetes v1.1 added iptables proxy mode in kubernetes v1 2. The iptables mode of Kube proxy becomes the default setting. Kubernetes v1.8 added ipvs proxy mode.

Why not use DNS polling?

The reasons are as follows:

  • DNS implementation has a long history. It does not comply with the record TTL and caches the results after the name is found.
  • Some applications perform DNS lookups only once and cache results indefinitely.
  • Even if the application and database are properly re resolved, low or zero TTL value on DNS records may bring high load to DNS, making management difficult.

In short, it is inappropriate because there is a cache.

iptables proxy mode

In this mode, Kube proxy monitors the addition and removal of Service objects and Endpoints objects by the Kubernetes control node. For each Service, it will configure iptables rules to capture the requests that arrive at the clusterIP and port of the Service, and then redirect the requests to one of the set of backend of the Service. For each endpoint object, it will also configure iptables rules, which will select a backend combination.

The default policy is that Kube proxy randomly selects a backend in iptables mode.

Handling traffic using iptables has lower system overhead because traffic is handled by Linux netfilter without switching between user space and kernel space. This method may also be more reliable.

If Kube proxy runs in iptables mode and the first Pod selected does not respond, the connection fails. This is different from the userspace mode: in this case, Kube proxy will detect that the connection to the first Pod has failed and will automatically retry with other backend pods.

We can use the Pod readiness probe to verify whether the backend Pod can work normally, so that Kube proxy in iptables mode can only see the backend that is tested normally. This means that you can avoid sending traffic through Kube proxy to pods that are known to have failed.

IPVS proxy mode

In IPVS mode, Kube proxy monitors Kubernetes services and endpoints, calls netlink interface to create IPVS rules accordingly, and periodically synchronizes IPVS rules with Kubernetes services and endpoints. This control cycle ensures that the IPVS state matches the desired state. When accessing a service, IPVS directs traffic to one of the back-end pods.

IPVS proxy mode is based on netfilter hook function similar to iptables mode, but uses hash table as the basic data structure and works in kernel space. This means that compared with Kube proxy in iptables mode, Kube proxy in IPVS mode has shorter redirection communication delay and better performance when synchronizing proxy rules. Compared with other proxy modes, IPVS mode also supports higher network traffic throughput.

IPVS provides more options to balance the traffic of back-end pods. These are:

  • rr: round-robin
  • lc: least connection (smallest number of open connections)
  • dh: destination hashing
  • sh: source hashing
  • sed: shortest expected delay
  • nq: never queue

Note: to run Kube proxy in IPVS mode, you must make IPVS Linux available on the node before starting Kube proxy. When Kube proxy starts in IPVS proxy mode, it verifies that IPVS kernel modules are available. If the IPVS kernel module is not detected, Kube proxy will return to running in iptables proxy mode.

Service type

The Kubernetes Service has the following 4 types:

  • ClusterIP: the default type. A virtual IP that can only be accessed inside the Cluster is automatically assigned
  • NodePort: expose services through the IP and static port (NodePort) on each Node. Based on ClusterIP, NodePort services will be routed to ClusterIP services. By requesting < nodeip >: < NodePort >, NodePort services inside a cluster can be accessed from outside the cluster.
  • LoadBalancer: using the load balancer of the cloud provider, you can expose services to the outside. External load balancers can route to NodePort services and ClusterIP services.
  • externalName: by returning CNAME and its value, you can map the service to the contents of the externalName field (for example, foo.bar.example.com). No type of proxy was created.

It should be noted that the Service can map a receiving port to any targetPort. By default, targetPort will be set to the same value as the port field.

Service domain name format: $(service name)$ (namespace). svc. cluster.local, where cluster Local is the domain name of the specified cluster

Resource list for Deoployment

[root@master mainfest]# vim deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        
[root@master mainfest]# kubectl apply -f deploy.yaml 
deployment.apps/deploy created
[root@master mainfest]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
deploy-585449566-kdwq5   1/1     Running   0          55s   10.244.1.58   node1   <none>           <none>
deploy-585449566-p5fdj   1/1     Running   0          55s   10.244.1.57   node1   <none>           <none>
deploy-585449566-wcj5r   1/1     Running   0          55s   10.244.1.56   node1   <none>           <none>

curl access

[root@master mainfest]# curl 10.244.1.58
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
[root@master mainfest]# curl 10.244.1.57
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
[root@master mainfest]# curl 10.244.1.56
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

ClusterIP type example

Create a service of cluster IP type

[root@master mainfest]# vim svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  type: ClusterIP		//It can be left blank. The default value is ClusterIP
  selector:
    app: nginx
  ports:
  - name: nginx
    port: 80
    targetPort: 80
    
[root@master mainfest]# kubectl apply -f svc-clusterip.yaml 
service/svc-clusterip created
[root@master mainfest]# kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP   6d23h
svc-clusterip   ClusterIP   10.105.207.162   <none>        80/TCP    2m39s

curl access

[root@master mainfest]# curl 10.105.207.162
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

NodePort type example

If the type field is set to NodePort, the Kubernetes control layer will allocate ports within the range specified by the -- service node port range flag (default: 30000-32767).

[root@master mainfest]# vim svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-nodeport
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - name: nginx1
    port: 80			//Service the port on which the service is provided
    targetPort: 80		//The port on which the request is forwarded to the backend pod
    nodePort: 30001		//Optional field. By default, for convenience, k8s control level will randomly assign a port number from a certain range (default: 30000-32767)
    
[root@master mainfest]# kubectl apply -f svc-nodeport.yaml 
service/svc-nodeport created
[root@master mainfest]# kubectl get svc
svc-nodeport    NodePort    10.105.10.125    <none>        80:30001/TCP   7s
[root@master mainfest]# curl 10.105.10.125
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
[root@master mainfest]# curl 192.168.100.110:30001 		// Access 30001 port of this machine
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

LoadBalancer type example

When using a Service from a cloud provider that supports an external load balancer, set the value of type to "LoadBalancer" to provide a load balancer for the Service. The load balancer is created asynchronously. The information about the provided load balancer will be through the Service status The LoadBalancer field is published.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 192.0.2.127

Traffic from the external load balancer will be redirected directly to the back-end Pod, but how they actually work depends on the cloud provider.

Some cloud providers allow loadBalancerIP to be set. In these cases, the load balancer will be created based on the loadBalancerIP set by the user. If the loadBalancerIP field is not set, a temporary IP is assigned to the load balancer. If loadBalancerIP is set, but the cloud provider does not support this feature, the set loadBalancerIP value will be ignored.

Topics: Operation & Maintenance Docker Kubernetes