
# 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负载间访问   
![](https://support.huaweicloud.com/devg-cci/zh-cn_image_0198270234.png)
#### Service是如何工作的
Kubernetes中的Service对象就是用来解决上述Pod访问问题的。Service有一个固定IP地址，Service将访问他的流量转发给Pod，具体转发给哪些Pod通过Label来选择，而且Service可以给这些Pod做负载均衡。
那么对于上面的例子，通过为前后台添加两个Service，通过Service来访问Pod，这样前台Pod就无需感知后台Pod的变化，如[图2]所示。
图2通过Service访问Pod   
![](https://support.huaweicloud.com/devg-cci/zh-cn_image_0197854496.png)
#### 创建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的类型
```
![](https://support.huaweicloud.com/devg-cci/public_sys-resources/note_3.0-zh-cn.png)
在原生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。
![](https://support.huaweicloud.com/devg-cci/public_sys-resources/note_3.0-zh-cn.png)
kube-dns是预留给域名解析使用的Service，云容器实例会自动创建，域名解析的详细内容请参见[使用ServiceName访问Service]。
 #### 使用ServiceName访问Service
云容器实例中您可以使用[CoreDNS](https://coredns.io/)插件给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地址。
![](https://support.huaweicloud.com/devg-cci/public_sys-resources/notice_3.0-zh-cn.png)
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，您可以根据需要选择。您可以调用[创建负载均衡器（增强型）](https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561535.html)创建ELB实例，更方便的方法是通过[ELB控制台](https://console.huaweicloud.com/vpc/?#/elb/manager/elbflag/elbList/enhanceElb)创建增强型ELB实例。
![](https://support.huaweicloud.com/devg-cci/public_sys-resources/note_3.0-zh-cn.png)
- ELB实例必须与Service在同一个VPC内，否则无法绑定。
- 跨namespace不支持service或ELB域名访问，只能通过ELB内网IP:端口访问。
 
图3LoadBalancer Service   
![](https://support.huaweicloud.com/devg-cci/zh-cn_image_0197967721.png)
下面是一个创建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的类型
```
