Introduction to Kubernetes (11): Understanding Service and Endpoint

2021年4月23日 508点热度 0人点赞 0条评论
内容目录

Table of Contents

  • Srevice
  • Creation and Phenomenon of Service
  • Service Definition
  • Endpoint Slices
  • Creating Endpoint and Service
    • Service
    • Creating Application
    • Creating Endpoint

The deployment of a Deployment and Service has been introduced in A Brief Introduction to Kubernetes (8): External Access to the Cluster, and this article serves as a supplement, extensively discussing Service.

Article link: https://www.cnblogs.com/whuanle/p/14685430.html

Srevice

Service is an abstract method to expose applications running on a set of Pods as a network service. If we deploy Pods using Deployment, we can create a Service based on that Deployment.

In Kubernetes (K8S), each Pod has its own unique IP address, whereas Service can provide the same DNS name for multiple Pods (a group) and can load balance among them directly.

For example, in a group of nginx Pods, if nginx scales dynamically or for some reason the IP/port changes, then both the IP and port are not fixed, and it becomes challenging to determine the address of the newly scaled Pods. Moreover, if a Pod is deleted, the IP becomes unavailable.

Another scenario: if a group of Pods is referred to as the frontend, such as a web service, and another group of Pods is referred to as the backend, for example, MySQL, how can the frontend find and track the IP address it needs to connect to in order to use the backend portion of its workload?

This is precisely the problem that Service aims to solve. A Kubernetes Service defines an abstraction: a logical group of Pods, and a strategy for accessing them — typically referred to as microservices. When using Service to create a service for a group of Pods (created with Deployment), it doesn't matter how many Pod replicas we create or how they change; the frontend doesn't need to care which backend replica it is calling, nor does it need to know the status of backend Pods or track them. Service abstracts this association between the frontend and backend, decoupling them.

Creation and Phenomenon of Service

Let's quickly create Pods using the command below; the Pods will be deployed and run on various nodes.

kubectl create deployment nginx --image=nginx:latest --replicas=3
kubectl expose deployment nginx --type=LoadBalancer --port=80

Then execute the command to view Services:

kubectl get services

This indicates that the external access port is 30424.

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP        3d6h
nginx        LoadBalancer   10.101.132.236   <pending>     80:30424/TCP   39s

At this point, we can access this Service via the public network and port.

We can view the YAML file of this Service:

kubectl get service nginx -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2021-04-23T07:40:35Z"
  labels:
    app: nginx
  name: nginx
  namespace: default
  resourceVersion: "369738"
  uid: 8dc49805-2fc8-4407-adc0-8e31dc24fa79
spec:
  clusterIP: 10.101.132.236
  clusterIPs:
  - 10.101.132.236
  externalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - nodePort: 30424
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer: {}

With a standard YAML file template, we can easily modify and customize a Service.

Let's view the Pods created through Deployment:

kubectl get pods -o wide
NAME                        IP              NODE       NOMINATED NODE   READINESS GATES
nginx-55649fd747-9fzlr    192.168.56.56    instance-2   <none>           <none>
nginx-55649fd747-ckhrw    192.168.56.57    instance-2   <none>           <none>
nginx-55649fd747-ldzkf    192.168.23.138   instance-1   <none>           <none>

Note: The Pods run on different nodes.

When we access through the external network, the Service will automatically provide us with one of the Pods, but this process is quite complex; here we will just present the surface phenomena.

               ------------
               |          |
--- Public IP --> |   pod1   |
               |   pod2  |
               |   pod3  |
               ------------

Then, we will check the iptables configuration with the command:

iptables-save

Then search for the keyword random:

file

You will see three default/nginx, and the first Pod has a chance of access of 0.33333..., and then with a probability of 2/3, out of 2/3, there's a 0.5 chance of selecting the second Pod. The remaining 1/3 chance selects the third Pod.

If you want to access a Pod, you can do so by accessing the IP of any node that has nginx Pods. Since the master cannot run Pods, accessing through the master's IP is not possible.

Of course, it isn't strictly 0.33333..; the rules of iptables are a bit complicated, and it's difficult to explain fully here. We just need to know that the external network can access the Service, and the Service forwards traffic to us via iptables. Even if the Pods deployed by Deployment are not on the same node, services like K8S's DNS will handle it correctly, and we won't need to manually configure these networks.

[Image source: k8s official website]

Service Definition

In the previous section, we introduced the method to create a Service (kubectl expose ...) and also discussed its dependency on iptables. Here we will continue to learn the method to define a Service.

Previously, we operated directly through Deployment, uniformly mapping Pods (replicas) for a Deployment. Of course, we can also perform network mapping for different Pods.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 6666
      targetPort: 80

In this YAML, we used the selector, which generally contains the app label, indicating the name of the Pod (usually the image name). The port and targetPort correspond to the port of the Pod and the port exposed for external access, respectively.

When we don't use Deployment or job objects for the Pods, we can use the selector to choose appropriate Pods.

Service can map a receiving container or Pod port, targetPort, to any external port port that can be accessed from outside. If you use kubectl expose to map ports, it will by default randomly provide a 30xxx port. In contrast, when using YAML, the targetPort will, by default, be set to the same value as the port field.

Endpoint Slices

"Endpoint Slices provide a simple way to track network endpoints in a Kubernetes cluster. They offer a scalable and extensible alternative to Endpoints."

In Kubernetes, an EndpointSlice contains a reference to a group of network endpoints. After specifying a selector, the control plane will automatically create Endpoints for Kubernetes Services that utilize the specified selection operator.

This means that creating a Service (with selection operator) will automatically create an Endpoint.

Let's check the endpoints in the default namespace:

kubectl get endpoints
NAME         ENDPOINTS                                            AGE
kubernetes   10.170.0.2:6443                                      3d7h
nginx        192.168.56.24:80,192.168.56.25:80,192.168.56.26:80   59m

These are the IPs and ports of the Pods, meaning that through Endpoints, tracking network endpoints within the Kubernetes cluster becomes more arbitrary. However, such an explanation can be hard to understand. The author had to refer to many resources and troubleshoot many times to grasp these concepts. Next, we will proceed step by step in practical operations and gradually understand the meanings behind these operations.

Creating Endpoint and Service

Next, we will manually create the Service and Endpoint. We will create the Service first and then the Endpoint (the order of creating these can be arbitrary).

Service

First, let's delete the previously created Service.

kubectl delete service nginx

Write the contents into a service.yaml file as follows:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
    - protocol: TCP
      port: 6666
      targetPort: 80

Apply this Service:

kubectl apply -f service.yaml

Check the service:

kubectl get services
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   3d12h
nginx        ClusterIP   10.98.113.242   <none>        6666/TCP    6s

Since this Service does not map any Pods, it is currently of no use. However, it already provides developers with a clarification, or rather confirming the Service port and address for nginx. As for the actual nginx service, we will determine that later. The order of creating Service and Endpoint is arbitrary; here we proposed the abstraction of first agreeing on the port and then providing the service, hence creating Service first.

Creating Application

We can randomly choose a worker or master node to create an nginx container:

docker run -itd -p 80:80 nginx:latest

Why create a container directly instead of a Pod? Because we are in the development stage. If we change nginx to MySQL, how do we debug it? Test our database? Simulate data? We will create applications through Deployment in production, but for now, we can utilize our own database or local applications.

The official documentation states:

  • We want to use an external database cluster in production, but our own database in the testing environment.
  • We want services to point to services in another namespace or clusters.
  • You are migrating workloads to Kubernetes, and while assessing this approach, you are only running part of the backend on Kubernetes.

In summary, we have created a Service, which provides the abstraction. As for how to deliver this service, we can use a Pod, directly execute binaries on machines via command, or use Docker. Moreover, MySQL may be provided as an external service, or MySQL could be directly deployed on the host without using containers or Pods. We can track the MySQL service's port via the Endpoint.

Next, we query the IP of this container:

docker inspect {container_id} | grep IPAddress

The author obtained: "IPAddress": "172.17.0.2", so let's try curl 172.17.0.2 to see if we can access nginx, and if it works, we can proceed to the next step.

Creating Endpoint

Create an endpoint.yaml file with the following content (note to replace the IP with your container's access IP):

apiVersion: v1
kind: Endpoints
metadata:
  name: nginx
subsets:
  - addresses:
      - ip: 172.17.0.2
    ports:
      - port: 80

Then apply the YAML:

kubectl apply -f endpoint.yaml

Check the endpoint:

kubectl get endpoints
# cannot be filled as endpoint

Then access the Service's IP:

curl 10.99.142.24:6666

You can also access this IP via the public network.

If the Endpoint needs to track multiple IPs (multiple Pods or containers or applications), you can use:

  - addresses:
      - ip: 172.17.0.2
      - ip: 172.17.0.3
      - ip: 172.17.0.4
      ... ...

痴者工良

高级程序员劝退师

文章评论