What is a Service in Kubernetes?

Service is one of the core resource objects in Kubernetes. It defines an entry point for accessing a service. The frontend application (Pod) accesses a cluster of backend Pod replicas through this entry point. The seamless connection between Service and its backend Pod replica cluster is achieved through Label Selector. The role of ReplicationController (RC) is to ensure that the service capability and quality of Service are maintained at the expected standard.

Microservices Network Architecture provided by Kubernetes

By analyzing, identifying, and modeling all services in the system as microservices – Kubernetes Service, our system is ultimately composed of multiple independent microservice units that provide different business capabilities. These services communicate via TCP/IP, thus possessing strong distributed capabilities, elastic scalability, and fault tolerance.

Since each Pod is assigned a separate IP address and each Pod provides an independent Endpoint (Pod IP + ContainerPort) for client access, multiple Pod replicas now form a cluster to provide access.

Kubernetes requires the installation of kube-proxy on each Node. The kube-proxy process is essentially an intelligent software load balancer responsible for forwarding requests to a backend Pod instance for the Service and implementing service load balancing and session persistence mechanisms internally.

Kubernetes has ingeniously designed Services to not share a single load balancer’s IP address. Instead, each Service is allocated a globally unique virtual IP address, known as Cluster IP. This way, each service becomes a “communication node” with a unique IP address, simplifying service invocation to basic TCP network communication problems.

While Pod Endpoint addresses change with Pod destruction and recreation, as the new Pod address differs from the old Pod, once a Service is created, Kubernetes automatically assigns it an available Cluster IP. Furthermore, throughout the Service’s lifecycle, its Cluster IP remains unchanged. Therefore, mapping the Service’s name to the Service’s Cluster IP address resolves the issue.

Creating a Service Example: Manually creating a Service

kind: Service
apiVersion: v1
metadata:
name: mallh5-service
namespace: abcdocker
spec:
selector:
  app: mallh5web
type: NodePort
ports:
  - protocol: TCP      # Only TCP and UDP are supported; defaults to TCP if not specified
    port: 3017
    targetPort: 5003
    nodePort: 31122

Parameters Explanation:

  • Port: Port represents the port exposed on the cluster IP (Service IP). “:port” is provided for internal cluster client access to the service.
  • NodePort: NodePort is a way for external clients to access the service entry point provided by Kubernetes to cluster clients (another way is LoadBalancer). “:nodePort” is provided for external clients to access the service.
  • targetPort: TargetPort is easy to understand; it is the port on the Pod. Data arriving from port and nodePort ultimately flows through kube-proxy to the targetPort on the backend Pod and enters the container.

Creation:

[root@master test]# kubectl create -f abcdocker-server.yaml
service/mallh5-service created

Viewing detailed server information:

[root@master test]# kubectl get service --namespace=abcdocker
NAME             TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
mallh5-service   NodePort   10.254.22.153   <none>        3017:31122/TCP   13m
[root@master test]# kubectl get service
NAME           TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
kubernetes     ClusterIP   10.254.0.1       <none>        443/TCP       35d
nginx-service   NodePort    10.254.200.178   <none>        80:31000/TCP   34d

You can check the network via kubectl get endpoints.

Service Details:

[root@abcdockeryaml]# kubectl describe svc myserver
Name:             myserver     # Name
Namespace:         default      # Namespace
Labels:           <none>
Annotations:       <none>
Selector:          app=nginx    # Matches Pod labels
Type:             ClusterIP    # svc ip type
IP:                10.254.219.137   # svc IP address
Port:             mynginx-http  80/TCP   # svc port (can be adjusted according to business)
TargetPort:        80/TCP       # pod port
Endpoints:         172.30.144.2:80  # Matching pod IP
Session Affinity: None
Events:           <none>

Tips: targetPort supports strings. Scenario: If there are 2 pods with labels both set as nginx, but our business logic matches one of them, we can add a name variable under containerPort in one of the pods, and then directly write the targetPort into the variable when referenced by svc.

Deployment file:

---
apiVersion: v1
kind

: ReplicationController
metadata:
name: rc-demo
labels:
    app: rc-demo
spec:
replicas: 1
template:
  metadata:
    labels:
      app: nginx
  spec:
    containers:
      - name: nginx-demo
        image: nginx
        resources:
            requests:
              cpu: 100m
        ports:
        - containerPort: 80
          name: nginxweb
# Add a name to the ports container port; it can be directly used in svc

svc yaml:

apiVersion: v1
kind: Service
metadata:
name: myserver
namespace: default
spec:
selector:
  app: nginx
ports:
  - name: mynginx-http
    protocol: TCP
    port: 80
    targetPort: nginxweb
# targetPort is referenced directly

Describing the result:

[root@abcdocker yaml]# kubectl describe svc myserver
Name:             myserver
Namespace:         default
Labels:           <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                    {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myserver","namespace":"default"},"spec":{"ports":[{"name":"myngin...
Selector:          app=nginx
Type:             ClusterIP
IP:                10.254.219.137
Port:             mynginx-http  80/TCP
TargetPort:       nginxweb/TCP
Endpoints:         <none>
Session Affinity: None
Events:           <none>

Issues with External System Accessing Service:

We need to understand three types of IPs in Kubernetes:

  1. Node IP: IP address of each node in the Kubernetes cluster
  2. Pod IP: IP address of each Pod
  3. Cluster IP: IP address of the Service

Parameter Explanation:

  1. Node IP: The Node IP is the IP address of each node’s physical network card in the Kubernetes cluster. It is a real physical network, and all servers belonging to this network can communicate directly through this network, regardless of whether there are nodes outside the Kubernetes cluster. When nodes outside Kubernetes need to access nodes inside Kubernetes or TCP/IP services, they must use the Node IP.

Access diagram for server:

  1. Pod IP: The Pod IP is the IP address of each Pod. It is allocated by the Docker Engine based on the IP address range of the docker0 bridge. It is usually a virtual layer 2 network. Kubernetes requires Pods on different Nodes to communicate directly with each other, so when a Pod’s container accesses another Pod’s container within Kubernetes, communication occurs through the virtual layer 2 network where the Pod IP is located, while the actual TCP/IP traffic flows through the physical network card where the Node IP is located.
  2. Cluster IP: It is a virtual IP but more like a spoofed IP network.
    • Cluster IP only acts on Kubernetes Service objects and is managed and allocated by Kubernetes (from the Cluster IP address pool).
    • Cluster IP cannot be pinged because there is no “physical network object” to respond.
    • In communication between Node IP, Pod IP, and Cluster IP within the Kubernetes cluster, Kubernetes uses its own special routing rules.

Brief Description of NodePort Implementation:

The implementation of NodePort is to open a corresponding TCP request for the Service that needs external access on each Node in the Kubernetes cluster. External systems only need to use any Node’s IP address + specific NodePort port to access the service. By running the netstat service on any node, we can see that the NodePort port is being listened on.

However, NodePort has not completely solved all the problems of external access to the Service, such as the load balancing problem. For example, if there are 10 nodes in the cluster, it is best to have a load balancer.

External requests only need to access the IP address of this load balancer, which is responsible for forwarding traffic to the backend NodePort on one of the nodes.

Example:

---
apiVersion: v1
kind: Service
metadata:
name: myserver
namespace: default
spec:
selector:
  app: nginx
type: NodePort
ports:
  - name: mynginx-http
    protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30001       # If we don't specify a nodePort port, a random port between 30000 and 32767 will be generated

After creation, view svc:

[root@abcdocker yaml]# kubectl get svc
NAME         TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
kubernetes   ClusterIP   10.254.0.1       <none>        443/TCP       83d
myserver     NodePort    10.254.201.121   <none>        80:30001/TCP   5s

Describe the svc:

[root@abcdocker yaml]# kubectl describe svc myserver
Name:                     myserver
Namespace:               default
Labels:                   <none>
Annotations:             kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myserver","namespace":"default"},"spec":{"ports":[{"name":"myngin...
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.254.201.121
Port:                     mynginx-http  80/TCP
TargetPort:               80/TCP
NodePort:                 mynginx-http  30001/TCP
Endpoints:                172.30.144.2:80
Session Affinity:         None
External Traffic Policy: Cluster
Events:                   <none>

Now you can access it using a browser.

We can see that svc access is actually generated through kube-proxy. Now let’s take a look at how kube-proxy works.

[root@abcdocker yaml]# netstat -lntup|grep 30001
tcp6       0      0 :::30001               :::*                   LISTEN      933/kube-proxy

How Kube-proxy Works:

  1. In many cases, Service is just a concept. The actual Service role is played by the kube-proxy service process.
  2. A kube-proxy service process runs on each Node node.
  3. For each TCP-type Kubernetes Service, kube-proxy will establish a SocketServer on the local Node node to handle incoming requests and evenly send them to the port of a backend Pod. This process defaults to using the Round Robin (rr) load balancing algorithm.
  4. kube-proxy creates a “service proxy object” for each Service by querying and listening to changes in Service and Endpoints in the API Server, and synchronizes them automatically. The service proxy object is a kind of architecture within the kube-proxy program. It includes a SocketServer for listening to requests for this service. The port of the SocketServer is randomly selected

from the local available ports. In addition, kube-proxy internally creates a load balancer LoadBalancer.

  1. For the list of Services that have changed, kube-proxy will process them one by one.

If no cluster IP is set, no action is taken. Otherwise, take all port definition lists for the Service:

  • Assign a service proxy object for the Service port and create related iptables rules for the Service.
  • Update the forwarding address list corresponding to the Service in the load balancer component.

kube-proxy adds four rules chains to the NAT table in the local iptables when started and when changes in Service or Endpoint are detected:

  1. KUBE-PORTABLS-CONTAINER: Access service from a container through Cluster IP and port number.
  2. KUBE-PORTALS-HOST: Access service from the host through Cluster IP and port number.
  3. KUBE-NODEPORT-CONTAINER: Access service from a container through NODE IP and port number.
  4. KUBE-NODEPORT-HOST: Access service from the host through Node IP and port number.

Note: We can also set a default policy for kube-proxy to randomly select a pod. We can also implement session affinity based on the client IP by setting the value of service.spec.sessionAffinity to “ClientIP” (the default value is “None”).

By ops

Leave a Reply

Your email address will not be published. Required fields are marked *