更新时间:2023-12-21 GMT+08:00
分享

Service

直接访问Pod的问题

负载创建完成后,如何访问负载呢?访问负载实际上就是访问Pod,但是直接访问Pod会有如下几个问题:

  • Pod会随时被Deployment这样的控制器删除重建,那访问Pod的结果就会变得不可预知。
  • Pod的IP地址是在Pod启动后才被分配,在启动前并不知道Pod的IP地址。
  • 应用往往都是由多个运行相同镜像的一组Pod组成,一个个Pod的访问也变得不现实。

举个例子,假设有这样一个应用程序,使用Deployment创建了前台和后台,前台会调用后台做一些计算处理,如图1所示。后台运行了3个Pod,这些Pod是相互独立且可被替换的,当Pod出现状况被重建时,新建的Pod的IP地址是新IP,前台的Pod无法直接感知。

图1 负载间访问

Service是如何工作的

Kubernetes中的Service对象就是用来解决上述Pod访问问题的。Service有一个固定IP地址,Service将访问他的流量转发给Pod,具体转发给哪些Pod通过Label来选择,而且Service可以给这些Pod做负载均衡。

那么对于上面的例子,通过为前后台添加两个Service,通过Service来访问Pod,这样前台Pod就无需感知后台Pod的变化,如图2所示。

图2 通过Service访问Pod

创建Service

下面示例创建一个名为“nginx”的Service,通过selector选择到标签“app:nginx”的Pod,目标Pod的端口为80,Service对外暴露的端口为8080。

访问服务只需要通过“服务名称:对外暴露的端口”接口,对应本例即“nginx:8080”。这样,在其他负载中,只需要通过“nginx:8080”就可以访问到“nginx”关联的Pod。

apiVersion: v1
kind: Service
metadata:
  name: nginx        # Service的名称
spec:
  selector:          # Label Selector,选择包含app=nginx标签的Pod
    app: nginx
  ports:
  - name: service0
    targetPort: 80   # Pod的端口
    port: 8080       # Service对外暴露的端口
    protocol: TCP    # 转发协议类型,支持TCP和UDP
  type: ClusterIP    # Service的类型

在原生kubernetes中,Service还有NodePort类型,当前云容器实例不支持使用NodePort类型Service。

将上面Service的定义保存到nginx-svc.yaml文件中,使用kubectl创建这个Service。

# kubectl create -f nginx-svc.yaml -n $namespace_name
service/nginx created

# kubectl get svc -n $namespace_name
NAME       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.247.9.190     <none>        53/UDP,53/TCP   7m
nginx      ClusterIP   10.247.148.137   <none>        8080/TCP        1h

您可以看到Service有个Cluster IP,这个IP是固定不变的,除非Service被删除,所以您也可以使用ClusterIP在内部访问Service。

kube-dns是预留给域名解析使用的Service,云容器实例会自动创建,域名解析的详细内容请参见使用ServiceName访问Service

使用ServiceName访问Service

云容器实例中您可以使用CoreDNS插件给Service做域名解析,然后使用“ServiceName:Port”访问Service,这也是Kubernetes中最常用的一种使用方式,CoreDNS的安装请参见插件管理

CoreDNS安装成功后会成为DNS服务器,当创建Service后,CoreDNS会将Service的名称与IP记录起来,这样Pod就可以通过向CoreDNS查询Service的名称获得Service的IP地址。

访问时通过nginx.<namespace>.svc.cluster.local访问,其中nginx为Service的名称,<namespace>为命名空间名称,svc.cluster.local为域名后缀,在实际使用中,可以省略<namespace>.svc.cluster.local,直接使用Service的名称即可。

例如上面创建的名为nginx的Service,直接通过“nginx:8080”就可以访问到Service,进而访问后台Pod。

使用ServiceName的方式有个主要的优点就是可以在开发应用程序时可以将ServiceName写在程序中,这样无需感知具体Service的IP地址。

CoreDNS插件占用计算资源,共2个Pod,每个Pod占用0.5核1G的资源,您需要为此付费。

LoadBalancer类型的Service

现在您知道可以创建ClusterIP类型的Service,通过Service的IP可以访问到Service后端的Pod。

云容器实例同时还支持创建LoadBalancer类型的Service,将增强型ELB实例与Service绑定,这样访问ELB实例的流量就会访问到Service。

ELB实例根据IP地址不同可以分为私网ELB实例和公网ELB实例,区别在于公网ELB实例绑定了一个公网IP,您可以根据需要选择。您可以调用创建负载均衡器(增强型)创建ELB实例,更方便的方法是通过ELB控制台创建增强型ELB实例。

  • ELB实例必须与Service在同一个VPC内,否则无法绑定。
  • 跨namespace不支持service或ELB域名访问,只能通过ELB内网IP:端口访问。
图3 LoadBalancer Service
下面是一个创建LoadBalancer类型的Service。创建完成后,可以通过ELB的IP:Port访问到后端Pod。
apiVersion: v1
kind: Service
metadata:
  name: nginx
  annotations:
    kubernetes.io/elb.id: 77e6246c-a091-xxxx-xxxx-789baa571280  # ELB的ID
spec:
  selector:
    app: nginx
  ports:
  - name: service0
    targetPort: 80
    port: 8080         # ELB访问端口
    protocol: TCP
  type: LoadBalancer   # Service的类型

相关文档