Service
直接访问Pod的问题
负载创建完成后,如何访问负载呢?访问负载实际上就是访问Pod,但是直接访问Pod会有如下几个问题:
- Pod会随时被Deployment这样的控制器删除重建,那访问Pod的结果就会变得不可预知。
- Pod的IP地址是在Pod启动后才被分配,在启动前并不知道Pod的IP地址。
- 应用往往都是由多个运行相同镜像的一组Pod组成,一个个Pod的访问也变得不现实。
举个例子,假设有这样一个应用程序,使用Deployment创建了前台和后台,前台会调用后台做一些计算处理,如图1所示。后台运行了3个Pod,这些Pod是相互独立且可被替换的,当Pod出现状况被重建时,新建的Pod的IP地址是新IP,前台的Pod无法直接感知。
Service是如何工作的
Kubernetes中的Service对象就是用来解决上述Pod访问问题的。Service有一个固定IP地址,Service将访问他的流量转发给Pod,具体转发给哪些Pod通过Label来选择,而且Service可以给这些Pod做负载均衡。
那么对于上面的例子,通过为前后台添加两个Service,通过Service来访问Pod,这样前台Pod就无需感知后台Pod的变化,如图2所示。
创建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:端口访问。
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的类型