通过以前的学习,我们已经能够通过ReplicaSet来创建一组Pod来提供具有高可用性的服务。虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:

  • Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。
  • Pod IP会随着Pod的销毁而消失,当ReplicaSet对Pod进行动态伸缩时,Pod IP可能随时随地都会变化,这样对于我们访问这个服务带来了难度.

因此,Kubernetes中的Service对象就是解决以上问题的实现服务发现核心关键。

Service可以看作是一组提供相同服务的Pod对外的访问接口。

借助Service,应用可以方便地实现服务发现和4层负载均衡。和其他Controller对象一样,通过Label Selector来确定一个Service将要使用哪些Pod。一个简单的Service定义如下:

例如,定义一个提供Web服务的RC,由两个Tomcat容器副组成,每个容器都通过containerPort设置提供服务的端口号为8080:

webapp-rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: webapp
spec:
  replicas: 2
  template:
    metadata:
      name: webapp
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: tomcat
        ports:
        - containerPort: 8080

[root@bogon ~]# kubectl get rc
NAME     DESIRED   CURRENT   READY   AGE
webapp   2         2         2       17m

[root@bogon ~]# kubectl get pod
NAME            READY   STATUS      RESTARTS   AGE
webapp-jsmkp    1/1     Running     0          118s
webapp-s7b6t    1/1     Running     0          118s

创建该RC之后获取该Pod的IP地址:

[root@bogon ~]# kubectl get pod -l app=webapp -o yaml|egrep  -w "podIP|nodeName"
    nodeName: server01
    podIP: 172.17.0.2
    nodeName: server01
    podIP: 172.17.0.3

去server01 节点上直接通过这两个Pod的IP地址和端口号访问Tomcat服务:

[root@server01 ~]# curl  172.17.0.2:8080
<!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/9.0.37</h3></body></html>

直接通过Pod的IP地址和端口号可以访问到容器应用内的服务,但是Pod的IP地址是不可靠的。

例如当Pod所在的Node发生故障时,Pod将被Kubernetes重新调度到另一个Node,Pod的IP地址将发生变化。

更重要的是,如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。

Kubernetes中的Service就是用于解决这些问题的核心组件。

kubectl expose快速创建service

以前面创建的webapp应用为例,为了让客户端应用访问到两个Tomcat Pod实例,需要创建一个Service来提供服务。
Kubernetes提供了一种快速的方法,即通过kubectl expose命令来创建Service:

[root@bogon ~]# kubectl expose rc webapp
service/webapp exposed

查看新创建的Service,可以看到系统为它分配了一个虚拟的IP地址(ClusterIP),Service所需的端口号则从Pod中的containerPort复制而来:

[root@bogon ~]# kubectl get svc -l app=webapp
NAME     TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)    AGE
webapp   ClusterIP   169.169.122.239   <none>        8080/TCP   3m

接下来就可以通过Service的IP地址和Service的端口号访问该Service了:

[root@server01 ~]# curl 169.169.122.239:8080

这里,对Service地址169.169.122.239:8080的访问被自动负载分发到了后端两个Pod之一。

配置文件定义Service

除了使用kubectl expose命令创建Service,我们也可以通过配置文件定义Service,再通过kubectl create命令进行创建。

例如对于前面的webapp应用,我们可以设置一个Service,代码如下:

webapp-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: webapp1
spec:
  ports:
  - port: 8081
    targetPort: 8080
  selector:
    app: webapp
[root@bogon ~]# kubectl create -f webapp-svc.yaml 
service/webapp1 created
[root@bogon ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)    AGE
webapp1      ClusterIP   169.169.33.76     <none>        8081/TCP   5s

[root@server01 ~]# curl  169.169.33.76:8081

Service定义中的关键字段是ports和selector。

本例中ports定义部分指定了Service所需的虚拟端口号为8081,由于与Pod容器端口号8080不一样,所以需要再通过targetPort来指定后端Pod的端口号。

selector定义部分 设置的是后端Pod所拥有的label:app=webapp。

负载分发策略

目前Kubernetes提供了两种负载分发策略:RoundRobin和SessionAffinity,具体说明如下:

  • RoundRobin:轮询模式,即轮询将请求转发到后端的各个Pod上。
  • SessionAffinity:基于客户端IP地址进行会话保持的模式。
    即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。

在默认情况下,Kubernetes采用RoundRobin模式对客户端请求进行负载分发,但我们也可以通过设置service.spec.sessionAffinity=ClientIP来启用SessionAffinity策略。这样,同一个客户端IP发来的请求就会被转发到后端固定的某个Pod上了。

通过Service的定义,Kubernetes实现了一种分布式应用统一入口的定义和负载均衡机制。

多端口service

有时一个容器应用也可能提供多个端口的服务,那么在Service的定义中也可以相应地设置为将多个端口对应到多个应用服务。

在下面的例子中,Service设置了两个端口号,并且为每个端口号都进行了命名:

[root@bogon ~]# vim manyport.yaml 

apiVersion: v1
kind: Service
metadata:
  name: webapp2
spec:
  ports:
  - port: 8080
    targetPort: 8080
    name: web
  - port: 8005
    targetPort: 8005
    name: management
  selector:
    app: webapp

[root@bogon ~]# kubectl create -f manyport.yaml 
service/webapp2 created
[root@bogon ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)             AGE
webapp2      ClusterIP   169.169.19.150    <none>        8080/TCP,8005/TCP   11s

多端口,不同协议的service

另一个例子是两个端口号使用了不同的4层协议—TCP和UDP:

[root@bogon ~]# vim utp-tcp-webapp.yaml 

apiVersion: v1
kind: Service
metadata:
  name: webapp3
spec:
  ports:
  - port: 8080
    protocol: UDP
    name: udp
  - port: 8080
    protocol: TCP
    name: tcp
  selector:
    app: webapp

[root@bogon ~]# kubectl create -f utp-tcp-webapp.yaml 
service/webapp3 created
[root@bogon ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)             AGE
webapp3      ClusterIP   169.169.215.151   <none>        8080/UDP,8080/TCP   6s

外部服务service

在某些环境中,应用系统需要将一个外部数据库作为后端服务进行连接,或将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service来实现:

[root@bogon ~]# vim no-lable-selector.yaml 

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

[root@bogon ~]# kubectl create -f no-lable-selector.yaml 
service/my-service created
[root@bogon ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)             AGE

my-service   ClusterIP   169.169.239.213   <none>        80/TCP              5s

通过该定义创建的是一个不带标签选择器的Service,即无法选择后端的Pod,系统不会自动创建Endpoint。
因此需要手动创建一个和该Service同名的Endpoint,用于指向实际的后端访问地址。

创建Endpoint的配置文件内容如下:

[root@bogon ~]# vim endpoint.yaml 

kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
- addresses:
  - ip: 1.2.3.4
  ports:
  - port: 8080

[root@bogon ~]# kubectl create -f endpoint.yaml 
endpoints/my-service created

[root@bogon ~]# kubectl get ep
NAME         ENDPOINTS                                                     AGE
my-service   1.2.3.4:8080                                                  12s
webapp       172.17.0.2:8080,172.17.0.3:8080                               40m

访问没有标签选择器的Service和带有标签选择器的Service一样,请求将会被路由到由用户手动定义的后端Endpoint上。
在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐