什么是Service?
Service可以简单的理解为逻辑上的一组Pod,一种可以访问Pod的策略,而且其他Pod可以通过这个Service访问到这个Service代理的Pod。相对于Pod而言,它会有一个固定的名称,一旦创建就固定不变。Kubernetes 为 Pods 提供自己的IP地址,并为一组 Pod 提供相同的DNS名, 并且可以在它们之间进行负载均衡。
service在kubectl中可以简写为svc。
无头服务(Headless Service)
在文章k8s学习-StatefulSet(模板、更新、扩缩容、删除等)中提到了Headles Servce,在这里补充上。
有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 “None” 来创建 Headless Service。
可以使用 Headless Service与其他服务发现机制进行接口,而不必与 Kubernetes 的实现捆绑在一起。
对 Headless Service并不会分配 Cluster IP,kube-proxy 不会处理它们, 而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了选择算符。
Service的类型
- ClusterIP:在集群内部使用,也是默认值。
- ExternalName:通过返回定义的CNAME别名。
- NodePort:在所有安装了kube-proxy的节点上打开一个端口,此端口可以代理至后端Pod,然后集群外部可以使用节点的IP地址和NodePort的端口号访问到集群Pod的服务。NodePort端口范围默认是30000-32767。
- LoadBalancer:使用云提供商的负载均衡器公开服务。
注意:需要使用 kube-dns 1.7 及以上版本或者 CoreDNS 0.0.8 及以上版本才能使用 ExternalName 类
型。
创建的dashboard就是NodePort类型的service
可以通过 节点ip:端口号 进行访问。
Service的代理模式
userspace 代理模式
kube-proxy 会监视 Kubernetes 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。 对每个 Service,它会在本地 Node 上随机打开一个端口。 任何连接到“代理端口”的请求,都会被代理到 Service 的后端 Pods 中的某个上面(按照Endpoints )。 使用哪个后端 Pod,是 kube-proxy 基于 SessionAffinity 来确定的。
最后,它配置 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP) 和 Port 的请求,并重定向到代理端口,代理端口再代理请求到后端Pod。
iptables 代理模式
kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。
默认的策略是,kube-proxy 在 iptables 模式下随机选择一个后端。
使用 iptables 处理流量具有较低的系统开销,因为流量由 Linux netfilter 处理, 而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。
如果 kube-proxy 在 iptables 模式下运行,并且所选的第一个 Pod 没有响应, 则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败, 并会自动使用其他后端 Pod 重试。
IPVS 代理模式
在 ipvs 模式下,kube-proxy 监视 Kubernetes 服务和端点,调用 netlink 接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。 该控制循环可确保IPVS 状态与所需状态匹配。访问服务时,IPVS 将流量定向到后端Pod之一。
IPVS代理模式基于类似于 iptables 模式的 netfilter 挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS 提供了更多选项来平衡后端 Pod 的流量:
- rr:轮替(Round-Robin)
- lc:最少链接(Least Connection),即打开链接数量最少者优先
- dh:目标地址哈希(Destination Hashing)
- sh:源地址哈希(Source Hashing)
- sed:最短预期延迟(Shortest Expected Delay)
- nq:从不排队(Never Queue)
模板
apiVersion: v1 kind: Service metadata: labels: app: nginx-svc name: nginx-svc spec: ports: - name: http # Service端口的名称 port: 80 # Service的端口, service a访问service b,使用http://serviceb protocol: TCP # TCP或UDP SCTP 默认: TCP targetPort: 80 # 后端应用的端口 - name: https port: 443 protocol: TCP targetPort: 443 selector: # 筛选器 app: nginx sessionAffinity: None type: ClusterIP # Service的类型
上述模板创建一个名为nginx-svc的服务,自己的端口为80、443,将请求代理到端口为80、443,且标签含app=nginx的pod上。
含有selector,会自动创建EndPoint对象(代理外部服务一节会提及)。
实战
创建
创建服务之前,先创建含有标签app=nginx的pod
kubectl get po -n killer --show-labels
命令
kubectl create -f service-nginx.yaml -n killer
结果
访问
命令
curl http://10.110.159.194:80
也可以将ip改为nginx-svc,如果是访问某空间下的service,可以在service名称后加.namespace,例如service-svc.killer
截图
修改
修改为NodePort类型
命令
kubectl edit svc nginx-svc -n killer
结果
修改内容
查看结果
这样外部就可以访问了
代理外部服务
迁移上云/容器化的过程中,可能存在部分上云/容器化,部分仍然在传统的服务器上,这样可以提前创建service去代理外部的服务,例如,web后端的api、mysql、redis等。
我们把前面的yaml文件修改一下,去除一下selector
apiVersion: v1 kind: Service metadata: labels: app: nginx-svc-external name: nginx-svc-external spec: ports: - name: http # Service端口的名称 port: 80 # Service的端口, service a访问service b,使用http://serviceb protocol: TCP # UDP TCP SCTP default: TCP targetPort: 80 # 后端应用的端口 sessionAffinity: None type: ClusterIP
将endpoint导出来
修改为以下yaml
apiVersion: v1 kind: Endpoints metadata: labels: app: nginx-svc-external name: nginx-svc-external subsets: - addresses: - ip: 110.242.68.4 ports: - name: http port: 80 protocol: TCP
上面的ip是百度的:
分别创建service和对应的endpoint
访问结果如下:
可以看到Service代理了百度的内容,也可以向上面一样,改为NodePort类型,使用浏览器访问进行验证。
代理外部域名
上面的更加通用一点,如果仅仅是域名的话,可以使用ExternalName类型的Service。
我们把前面的yaml修改一下
apiVersion: v1 kind: Service metadata: labels: app: nginx-external-domainname name: nginx-external-domainname spec: type: ExternalName externalName: www.baidu.com
创建后查看一下:
进入同ns下的pod访问一下:
删除
命令
kubectl delete svc nginx-external-domainname -n killer
结果
更多k8s相关内容,请看文章:k8s学习-思维导图与学习笔记