欢迎加入本站的kubernetes技术交流群,微信添加:加Blue_L。
kubernetes中服务主要用来进行服务发现和负载均衡。
在集群中我们的工作负载是不稳定的,ip地址可能随着pod重启,升级和扩容等不断发生变化,为了能够让其他工作负载可以稳定访问到这个工作负载,我们需要为其创建一个服务对象。
服务对象也是一个api资源对象,和其他api对象定义方法相同,下面是一个服务对象的定义:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
# 根据标签选择pod
app: nginx
# type: ClusterIP
# clusterIP: 192.168.3.251
ports:
# TCP, UDP, SCTP
- protocol: TCP
appProtocol: http
name: nginx-http
port: 80
targetPort: 80
# 支持多个端口配置
- name: https
protocol: TCP
port: 443
targetPort: 9377
externalTrafficPolicy: Cluster
这个服务对象会创建一个service资源,服务控制器会根据我们的selector选择对应的pod,创建endpoint后endpointslice资源关联到这个服务。服务控制器还会为我们的服务自动创建一个虚拟ip地址,其他工作负载使用这个虚拟ip地址访问这个服务。endpoint资源代表后端真正的服务实例,它关联到pod,访问到虚拟ip的流量会转到对应的后端pod对应的端口上。
通过selector可以让kunbernetes根据标签自动选择pod并创建endpoint关联到service。有些场景我们也可以不使用selector,例如:
引用已有服务的ip列表
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 443
targetPort: 6443
---
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 6443
引用外部已有服务
apiVersion: v1
kind: Service
metadata:
name: my-baidu
namespace: prod
spec:
# dns cname
type: ExternalName
externalName: www.baidu.com
service对象完整参考:https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/。
endpoint对象完整参考:https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoints-v1/
考虑到超多service实例的集群,endpoints更新会造成网络性能问题,出现了新endpointslice资源,默认每个slice包含100个endpoint,如果有更多的endpoint会创建新的slice。
在kubernetes中,kube-proxy负责非ExternalName类型的service虚拟ip的转发。
User-space模式
kube-proxy本地启动随机监听端口
clusterIP:port(iptables) -> 监听端口 -> endpoints,默认采用轮询算法。
iptables模式
clusterIP:port -> endpoints,默认随机算法。
问题:如果后端pod异常,iptables方式不会自动使用其他pod重试。最好配置pod的就绪探针保证后端pod都是正常运行的。
ipvs模式
通过netlink接口创建ipvs规则。ipvs模式使用哈希表存储规则,具有低延迟,更好的同步性能,更高的吞吐性能。同时支持负载均衡算法配置(使用kube-proxy的–ipvs-scheduler参数):
rr
: round-robinlc
: least connection (smallest number of open connections)dh
: destination hashingsh
: source hashingsed
: shortest expected delaynq
: never queue
https://kubernetes.io/blog/2018/07/09/ipvs-based-in-cluster-load-balancing-deep-dive/
上述三种模式都支持基于客户端ip的会话保持(SessionAffinity)。iptables下使用recent模块
流量策略
External traffic policy
Cluster:转到所有pod,并且通过nat隐藏客户端源IP。可能数据包会再次转到其他节点。
Local:只转发给在本节点的pod,如果某些节点没有对应pod则不做任何转发。该模式下会保留客户端源IP(LoadBalancer和NodePort类型)。
Internal traffic policy
Cluster:流量转发到所有endpoint。
Local:流量只转发到本地endpoint,如果本地没有对应的pod,则丢弃。
创建对应服务进行测试。
https://www.asykim.com/blog/deep-dive-into-kubernetes-external-traffic-policies
https://blog.cptsai.com/2020/11/15/k8s-external-traffic-policy
服务发现
kubernetes支持通过环境变量和dns方式进行服务发现。
{SVCNAME}_SERVICE_HOST
{SVCNAME}_SERVICE_PORT
例如 redis-master服务
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
但是pod创建之前service要首先存在
更多的情况下是使用dns进行服务发现。例如在default命名空间下有个service叫my-service,则可以通过my-service或my-service.default来访问这个服务。
如果service对象的spec.clusterIP=None则称之为无头服务,无头服务不会分配service的ip地址,kube-proxy不对这个服务进行流量负载。
如果无头服务定义了selector字段,则kubernetes进行匹配,自动创建endpoint对象。该服务的dns解析返回一个pod ip地址列表的A记录。
如果无头服务未定义selector,则如果定义了ExternalName,则dns解析返回cname只向这个外部域名。其他情况是匹配同名的endpoint对象。
服务的类型
ClusterIP
NodePort
–service-node-port-range
LoadBalancer
spec.allocateLoadBalancerNodePort
spec.loadBalancerClass
ExternalName
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
外部ip,针对任意类型的service。如果外部主机有针对某个网段到集群内的一个或多个节点的路由,那么就可以使用这个externalIP访问服务。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10