# Service介绍

Service资源对象可以为一组具有相同功能的Pod提供一个**单一不变**的接入点。

引入Service的原因：

* Pod的IP地址是随机变化的，并且无法直接由外部访问或访问外部；
* Service的IP地址和端口是固定的；

下图是典型的前后端之间互相访问的架构图，前端Pod不会与后端Pod直接通信，而是通过Service访问后端Pod，前端暴露给外部客户端的同样也是一个Service：

![内部和外部客户端通常通过Service连接到Pod](https://2906552408-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6Ub8CloS5kJszh6xSR%2Fsync%2Fbd2d95184a4c9906865c096ee74145583d48d878.png?generation=1588594616308665\&alt=media)

Service对象是通过**标签选择器**来匹配Pods，在匹配到一个Pod后，会创建一个**Endpoint资源**，并将该Pod的IP地址和端口记录下来，因此Service与Pod之间其实还有一层Endpoint。

## 创建Service

Service同样是使用标签选择器`spec.selector`来匹配Pod作为其转发流量的后端Pod。

创建Service同样有两种方法：

1. 使用`kubectl expose`命令来创建Service，详情可见[常用命令汇总一节](https://yangsijie151104.gitbook.io/k8s-note/ming-ling-hui-zong/chang-yong-ming-ling-hui-zong)；
2. 使用配置文件创建Service，如下：

```yaml
apiVersion: v1
kind: Service
metadata:
  name: httpd-svc
  labels:    # Service的标签
    run: httpd
spec:
  type: NodePort    # Service的类型，NodePort在ClusterIP的基础上增加了非k8s集群可访问的特性（推荐使用LoadBalancer，它会指定一个唯一可公开访问的ip地址，然后将所有连接重定向至Service，这样可以防止某一个Node挂了）
  externalTrafficPolicy: Local    # （当spec.type为NodePort或LoadBalance时）这个可以不做；写成这样是为了让外部通信连接的Node，将该连接转发至本Node的Pod上，防止转发给其他Node上的Pod时造成额外的网络跳数（但是有这个的话，本Node不会对外部流量做SNAT）
  selector:        # 通过selector中指定的值，与Pod的labels对应，用来选择Pod，当type为ExternalName时，这里不用写（如果不写的话，就不会自动创建Endpoint资源了）
    run: httpd
  ports:        # 该Service暴露的端口（可以同时暴露多个端口）
  - protocol: TCP
    nodePort: 30000        # 指定集群节点上监听的端口（若不指定，会自动生成）
    port: 8080            # ClusterIP监听的端口
    targetPort: 80        # Pod监听的端口（这里可以写成对应的Pod的spec.containers.ports.name来根据端口名匹配）
    name: xxx        # 端口的名字为xxx
  sessionAffinity: ClientIP    # 会话亲和性，默认是None，会使Service将来自同一个客户端IP地址的所有请求都转发至同一个Pod上
```

{% hint style="info" %}
**Tips:**&#x20;

1. `spec.type`共有五种：
   * None：用于创建headless Service；
   * ExternalName：用于与集群外部服务映射，并且不用指定`spec.selector`，因此不会自动创建Endpoint；
   * ClusterIP：默认类型，用于集群内部资源访问；
   * NodePort、LoadBalancer：用于暴露集群内部资源给外部访问；
2. Service共有两种IP地址：
   * CLUSTER-IP：该IP地址和其端口都是虚拟的，无法Ping通，均由iptables实现。即集群内部的资源可以访问的IP地址，除了Service的`spec.type`为**ExternalName**时没有外，其余类型都有；
   * EXTERNAL-IP：该IP地址是集群外部的资源可以访问的IP地址，其端口是真实存在的，会被一个进程监听，仅当Service的`spec.type`为**NodePort**、**LoadBalancer**或**ExternalName**时才有；
     {% endhint %}
