service和ingress
service
参考:https://www.cnblogs.com/fuyuteng/p/11598768.html
kubernetes service 原理解析 - 知乎 (zhihu.com)
为什么需要 service
在 kubernetes 中,当创建带有多个副本的 deployment 时,kubernetes 会创建出多个 pod,此时即一个服务后端有多个容器,那么在 kubernetes 中负载均衡怎么做,容器漂移后 ip 也会发生变化,如何做服务发现以及会话保持?这就是 service 的作用,service 是一组具有相同 label pod 集合的抽象,集群内外的各个服务可以通过 service 进行互相通信,当创建一个 service 对象时也会对应创建一个 endpoint 对象,endpoint 是用来做容器发现的,service 只是将多个 pod 进行关联,实际的路由转发都是由 kubernetes 中的 kube-proxy 组件来实现,因此,service 必须结合 kube-proxy 使用,kube-proxy 组件可以运行在 kubernetes 集群中的每一个节点上也可以只运行在单独的几个节点上,其会根据 service 和 endpoints 的变动来改变节点上 iptables 或者 ipvs 中保存的路由规则
service 的工作原理
service资源基于标签选择器将一组pod定义成一个逻辑组合,并通过自己的ip地址和端口调度代理请求至组内pod的对象之上,它向客户隐藏了真实的、处理用户请求的pod资源,使得客户端的请求看上去像是由service直接处理并进行响应一样
Service 对象的 IP 地址也称为 Cluster IP,是一种 VIP(虚拟IP),k8s集群内部的VIP
service网段不能跟机房网络、docker网段、容器网段冲突,否则可能会导致网络不通
Service以负载均衡的方式进行流量调度,Service和Pod之间松耦合,创建Service和Pod的任务可由不同的用户分别完成
Service 通过API Server实时监控(watch)标签选择器匹配到的后端Pod,不过Service并不直接链接至Pod,它们中间还有一个中间层 --Endpoints资源对象,默认情况下,创建Service对象时,其关联的Endpoints对象会自动创建
endpoints controller 是负责生成和维护所有 endpoints 对象的控制器,监听 service 和对应 pod 的变化,更新对应 service 的 endpoints 对象。当用户创建 service 后 endpoints controller 会监听 pod 的状态,当 pod 处于 running 且准备就绪时,endpoints controller 会将 pod ip 记录到 endpoints 对象中,因此,service 的容器发现是通过 endpoints 来实现的。而 kube-proxy 会监听 service 和 endpoints 的更新并调用其代理模块在主机上刷新路由转发规则
Endpoints 是一个由IP地址和端口组成的列表,这些IP和端口来自于其 Service 关联的 Pod
每个节点上的 kube-proxy 组件 watch 各 Service 及其关联的 Endpoints,如果有变动,就会实时更新当前节点上相应的 iptables 或 ipvs 规则,确保 Cluster IP 的流量能调度到 Endpoints
简单来讲,一个 Service 对象就是一个 Node 上的这些 iptables 和 ipvs 规则
service 的负载均衡
pod之间通信,一般不会pod直接访问pod,而是pod先访问service,然后service再到pod
关于将 Cluster IP 的流量能调度到 Endpoints,有三种方式:userspace代理、iptables代理、ipvs代理
userspace代理
1.1版本之前的默认代理模型,效率低
iptables代理
通过iptables进行目标地址转换和流量调度,缺点是不会在后端Pod资源无响应时自动进行重定向
ipvs代理
和iptables代理模型的区别仅在于,流量的调度功能由ipvs实现,其他功能仍由iptables完成
service 的类型
service 支持的类型也就是 kubernetes 中服务暴露的方式,默认有四种:ClusterIP、NodePort、LoadBalancer、ExternelName
ClusterIP
kubernetes 集群默认的服务暴露方式,它只能用于集群内部通信,可以被各 pod 访问,其访问方式为:
pod ---> ClusterIP:ServicePort --> (iptables)DNAT --> PodIP:containePort
# 集群内pod直接访问service的ip即可

NodePort
如果想要在集群外访问集群内部的服务,可以使用 NodePort 类型的 service,在集群内部署了 kube-proxy 的节点打开一个指定的端口,将访问node此端口的流量直接发送到这个端口,然后会被转发到 service 后端真实的服务进行访问。Nodeport 构建在 ClusterIP 上,其访问链路如下所示:
client ---> NodeIP:NodePort ---> ClusterIP:ServicePort ---> (iptables)DNAT ---> PodIP:containePort
# 外部流量请求先到node,再到service
# 只要安装了kube-proxy的node,都可以处理外部流量,有实力的话可以单独拿出几个node,打上污点,然后负载均衡只会将外部流量转发到这几个node

LoadBalancer
主要在公有云如阿里云、AWS 上使用,LoadBalancer 构建在 nodePort 基础之上,通过公有云服务商提供的负载均衡器将 k8s 集群中的服务暴露到外网,云厂商的 LoadBalancer 会给用户分配一个 IP,之后通过该 IP 的流量会转发到你的 service 上
LoadBalancer service 类型的结构如下图所示:

ExternelName
通过 CNAME 将 service 与 externalName 的值(比如:http://foo.bar.example.com)映射起来,这种方式用的比较少。
service 的服务发现
Pod 与 Service 的 DNS:https://kubernetes.io/zh/docs/concepts/services-networking/dns-pod-service/
虽然 service 的 endpoints 解决了容器发现问题,但不提前知道 service 的 Cluster IP,怎么发现 service 服务呢?service 当前支持两种类型的服务发现机制,一种是通过环境变量,另一种是通过 DNS。在这两种方案中,建议使用后者:
在集群中部署 CoreDNS 服务, 来达到集群内部的 pod 通过DNS 的方式进行集群内部各个服务之间的通讯
当前 kubernetes 集群默认使用 CoreDNS 作为默认的 DNS 服务,主要原因是 CoreDNS 是基于 Plugin 的方式进行扩展的,简单,灵活,并且不完全被Kubernetes所捆绑
service 的使用
ClusterIP 方式
apiVersion: v1
kind: Service
metadata:
name: my-nginx #service的名称,此名称会被DNS解析
spec:
clusterIP: 10.105.146.177
ports:
- port: 80 # Service的端口号
protocol: TCP
targetPort: 8080 # 后端目标进程的端口号或名称,名称需由Pod规范定义
selector:
app: my-nginx
sessionAffinity: None
type: ClusterIP
NodePort 方式
apiVersion: v1
kind: Service
metadata:
name: my-nginx
spec:
ports:
- nodePort: 30090 # 节点端口,kube-proxy监听本机此端口,将访问此端口的流量转发到service
port: 80 # service端口
protocol: TCP
targetPort: 8080 # 目标pod端口
selector:
app: my-nginx
sessionAffinity: None
type: NodePort
Headless service(就是没有 Cluster IP 的 service )
当不需要负载均衡以及单独的 ClusterIP 时,可以通过指定 spec.clusterIP 的值为 None 来创建 Headless service,它会给一个集群内部的每个成员提供一个唯一的 DNS 域名来作为每个成员的网络标识,集群内部成员之间使用域名通信。
deployment中对应的服务是 service,而statefulset中与之对应的服务就是 headless service
apiVersion: v1
kind: Service
metadata:
name: my-nginx
spec:
clusterIP: None
ports:
- nodePort: 30090
port: 80
protocol: TCP
targetPort: 8080
selector:
app: my-nginx
Ingress
https://kubernetes.io/zh/docs/concepts/services-networking/ingress/
https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/
上面介绍的 ClusterIP 、NodePort 、LoadBalancer 都是基于ip和port做负载均衡,属于四层负载均衡
Ingress 可以作用于多个 service,被称为 service 的 service,作为集群内部服务的入口,Ingress其实就是七层的反向代理,可以根据不同的 url,将请求转发到不同的 service 上
Ingress 的结构如下图所示:

kube-proxy监听node的指定端口,将此端口的流量转发到ingress,然后ingress再转发到不同的service
反正都是实现七层代理,也可以使用deployment创建一组pod,提供nginx服务,实现代替ingress的效果:
kube-proxy监听node的指定端口,将此端口的流量转发到nginx service,然后nginx在转发到不同的service,可以配置service ip,也可以使用服务发现
建议:域名多、流量大,使用nginx;反之使用ingress
其他服务类型
完全不使用k8s内置的service,而是通过注册中心自动发现pod地址,如果开发有实力,推荐使用这种方式