1. Service详解
1.1 Service介绍
在Kubernetes中,Service资源解决了Pod IP地址不固定的问题,提供了一种更稳定和可靠的服务访问方式。以下是Service的一些关键特性和工作原理:
-
Service的稳定性:由于Pod可能会因为故障、重启或扩容而获得新的IP地址,直接使用Pod的IP来访问服务是不可靠的。Service通过提供一个固定的虚拟IP(ClusterIP)作为访问入口,使得服务访问变得更加稳定。
-
Service的类型:Kubernetes支持不同类型的Service,包括ClusterIP、NodePort、LoadBalancer和ExternalName,每种类型适用于不同的访问场景:
-
ClusterIP:为Service在集群内部提供一个固定的虚拟IP,只有集群内部的客户端可以访问此服务。
-
NodePort:在所有节点的特定端口上公开Service,使得外部可以通过
<NodeIP>:<NodePort>
访问服务。 -
LoadBalancer:在NodePort的基础上,通过云服务商的负载均衡器对外提供服务,适用于公有云环境。
-
ExternalName:将服务映射到外部服务的DNS名称,不通过kube-proxy进行代理。
-
-
Service的负载均衡:Service可以对关联的Pod进行轮询或随机的负载均衡,使得请求可以均匀地分发到各个Pod上。
-
Service的发现机制:Kubernetes中的Pod可以通过DNS或环境变量来发现Service。通过DNS,Pod可以通过Service的名称和命名空间来解析Service的ClusterIP。
-
Service和Pod的关系:Service通过标签选择器(label selector)与一组Pod关联。当Service创建后,kube-proxy或相关的网络插件会监控Pod的变化,并更新Service的后端列表(Endpoints。
-
Headless Service:一种特殊的Service,不分配ClusterIP,而是通过DNS返回Pod的IP列表,适用于需要直接访问每个Pod的场景,如StatefulSets。
-
Service的端口:Service可以定义一个或多个端口,将外部请求映射到Pod的特定端口上。端口分为Service端口(port)、Pod端口(targetPort)和NodePort(仅NodePort类型Service)
-
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。
1.2 kube-proxy三种工作模式
8.2.1 userspace模式
-
userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。
-
该模式下,kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。
-
kube-proxy
在userspace
模式下的工作原理如下:-
Service的监听:
kube-proxy
为每个Service创建一个监听端口,这个端口对应Service的ClusterIP和端口号。当客户端向Service的ClusterIP:Port发送请求时,请求会被iptables
规则捕获并重定向到kube-proxy
的监听端口上。 -
负载均衡:
kube-proxy
根据定义的负载均衡算法(如轮询、随机等)从提供该服务的所有Pod中选择一个Pod,然后与该Pod建立连接,并将请求转发到选定的Pod上。kube-proxy
在此过程中充当了四层(TCP/UDP)负载均衡器的角色。 -
用户空间的开销:由于
kube-proxy
在userspace
模式下运行,数据包需要在内核空间和用户空间之间来回拷贝,这会导致额外的性能开销。虽然这种方法比较稳定,但是在高负载情况下,由于数据拷贝的开销,效率相对较低。 -
其他模式:除了
userspace
模式,kube-proxy
还支持iptables
模式和ipvs
模式,这两种模式效率更高,因为它们直接在内核空间工作,减少了数据拷贝的开销。iptables
模式通过修改网络规则来实现负载均衡,而ipvs
模式则是内核级别的负载均衡,通常提供更好的性能和更复杂的负载均衡特性。
-
8.2.2 iptables模式
-
iptables模式下,kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向ClusterIP的请求重定向到一个Pod IP。
-
该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。
-
iptables
模式下的kube-proxy
工作原理如下:-
创建规则:在
iptables
模式下,kube-proxy
为每个Service后端的Pod创建相应的iptables
规则。当请求到达Service的ClusterIP时,这些规则将请求直接重定向到后端Pod的IP地址上。 -
直接重定向:与
userspace
模式不同,在iptables
模式下,kube-proxy
不进行实际的流量转发,而是通过修改网络规则来实现负载均衡。这意味着流量直接从客户端通过网络路由到目的Pod,而不需要经过kube-proxy
进程。 -
效率提升:由于避免了用户空间和内核空间之间的上下文切换以及数据拷贝,
iptables
模式通常比userspace
模式有更高的网络性能。 -
简化的LB策略:在
iptables
模式下,kube-proxy
不实现复杂的负载均衡策略,如会话保持或智能负载均衡。它仅根据轮询策略简单地分发请求到各个Pod。 -
重试限制:由于
iptables
规则直接将流量路由到Pod,如果选定的Pod不可用,请求将失败,而kube-proxy
不会进行重试。这与userspace
模式不同,在userspace
模式下,kube-proxy
可以检测到连接失败并重新选择另一个Pod进行重试。 -
安全性:
iptables
模式下,流量直接从客户端路由到Pod,这意味着Pod的网络安全策略需要正确配置,以确保不会直接暴露给不受信任的网络。
-
-
iptables
模式提供了一种高效的方式来实现Service的负载均衡,但牺牲了一定程度的灵活性和容错能力。选择合适的模式需要根据具体的应用需求和性能要求来决定。对于需要高性能和简单负载均衡策略的场景,iptables
模式是一个不错的选择。而对于需要复杂负载均衡策略和容错能力的场景,userspace
模式或ipvs
模式可能更合适。
8.2.3 ipvs模式
-
ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法。
-
ipvs
(IP Virtual Server)模式是kube-proxy
的另一种工作模式,它在很多方面都优于iptables
模式:-
内核级负载均衡:与
iptables
不同,ipvs
是在内核空间实现的,这意味着它可以直接进行网络包的转发,而不需要在用户空间和内核空间之间来回拷贝数据。因此,ipvs
可以提供更低的延迟和更高的数据包转发速率。 -
高效的性能:由于
ipvs
在内核中实现,它通常能够提供比iptables
更高的性能,尤其是在高负载和大规模集群中。 -
丰富的负载均衡算法:
ipvs
支持多种负载均衡算法,包括轮询(round-robin)、最小连接(least-connection)、源IP哈希(source IP hashing)等,这为实现复杂的负载均衡策略提供了可能。 -
Session persistence:
ipvs
支持会话保持(Session persistence),这意味着来自同一客户端的请求可以始终被路由到同一个后端Pod,这对于需要维持会话状态的应用非常重要。 -
健康检查:
ipvs
可以根据Pod的健康状态来路由流量,只将请求转发到健康的Pod上,从而提高服务的可用性。 -
SNAT优化:
ipvs
可以更有效地处理源地址转换(SNAT),这对于使用NodePort
或LoadBalancer
服务类型时,从外部网络访问集群内部服务非常重要。 -
直接路由:
ipvs
使用直接路由(Direct Routing)模式,这意味着流量不需要经过额外的网络地址转换,从而减少了资源消耗。 -
状态同步:
kube-proxy
在ipvs
模式下,会监控Pod和Service的变化,并将这些变化实时同步到ipvs
规则中。
-
-
此模式必须安装ipvs内核模块,否则会降级为iptables
[root@K8s-master ~]# kubectl edit configmap kube-proxy -n kube-system mode: "ipvs" #修改此处,默认为空 [root@K8s-master ~]# kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE coredns-74586cf9b6-96v5x 1/1 Running 0 3d2h coredns-74586cf9b6-f5q7h 1/1 Running 0 3d2h etcd-k8s-master 1/1 Running 0 3d2h kube-apiserver-k8s-master 1/1 Running 0 3d2h kube-controller-manager-k8s-master 1/1 Running 0 3d2h kube-flannel-ds-6gqmv 1/1 Running 0 3d kube-flannel-ds-g7zcj 1/1 Running 0 3d kube-flannel-ds-hh52b 1/1 Running 0 3d kube-proxy-glhml 1/1 Running 0 3d1h kube-proxy-klcs2 1/1 Running 0 3d1h kube-proxy-x9v8k 1/1 Running 0 3d2h kube-scheduler-k8s-master 1/1 Running 0 3d2h [root@K8s-master ~]# kubectl delete pod kube-proxy-glhml kube-proxy-klcs2 kube-proxy-x9v8k -n kube-system pod "kube-proxy-glhml" deleted pod "kube-proxy-klcs2" deleted pod "kube-proxy-x9v8k" deleted
1.3 Service类型
-
Service的资源清单文件:
--- kind: Service # 资源类型 apiVersion: v1 # 资源版本 metadata: # 元数据 name: service # 资源名称 namespace: dev # 命名空间 spec: # 描述 selector: # 标签选择器,用于确定当前service代理哪些pod app: nginx type: ClusterIP # Service类型,指定service的访问方式 clusterIP: None # 虚拟服务的ip地址,设置为None表示创建Headless Service sessionAffinity: ClientIP # session亲和性,支持ClientIP、None两个选项 ports: # 端口信息 - protocol: TCP port: 3017 # service端口 targetPort: 5003 # pod端口
-
ClusterIP:
-
默认类型。Kubernetes 为 Service 分配一个虚拟的 IP 地址(ClusterIP),这个 IP 只能在集群内部访问。
-
适用于在集群内部提供服务发现和负载均衡,而不对外公开服务。
-
-
NodePort:
-
在集群的所有节点上打开一个静态端口(NodePort),外部可以通过任何节点的 IP 地址加这个端口来访问 Service。
-
适用于需要从集群外部访问服务的场景,但不像 LoadBalancer 那样需要云服务提供商的支持。
-
-
LoadBalancer:
-
与 NodePort 类似,但会利用云服务提供商的负载均衡器来分发流量。
-
适用于需要高可用性和可扩展性的场景,并且外部访问流量较大时推荐使用。
-
注意:需要云服务商支持,并且可能涉及额外的费用。
-
-
ExternalName:
-
不分配 ClusterIP,而是将服务映射到一个外部的 DNS 名称。
-
适用于将集群外部的服务(如数据库服务)映射到 Kubernetes 集群内部,使得集群内的服务可以像调用集群内的服务一样调用这些外部服务。
-
1.4 Service使用
1.4.1 环境准备
-
创建三个Pod
[root@K8s-master ~]# vim deployment.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: pc-deployment namespace: test spec: replicas: 3 selector: matchLabels: app: nginx-pod template: metadata: labels: app: nginx-pod spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 [root@K8s-master ~]# kubectl apply -f deployment.yaml deployment.apps/pc-deployment created [root@K8s-master ~]# kubectl get pod -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pc-deployment-6769786c4c-62d94 1/1 Running 0 3m1s 10.244.2.2 k8s-node-02 <none> <none> pc-deployment-6769786c4c-fv6jj 1/1 Running 0 3m1s 10.244.2.3 k8s-node-02 <none> <none> pc-deployment-6769786c4c-qn9ls 1/1 Running 0 3m1s 10.244.1.4 k8s-node-01 <none> <none> [root@K8s-master ~]# kubectl get pod -n test --show-labels NAME READY STATUS RESTARTS AGE LABELS pc-deployment-6769786c4c-62d94 1/1 Running 0 3m20s app=nginx-pod,pod-template-hash=6769786c4c pc-deployment-6769786c4c-fv6jj 1/1 Running 0 3m20s app=nginx-pod,pod-template-hash=6769786c4c pc-deployment-6769786c4c-qn9ls 1/1 Running 0 3m20s app=nginx-pod,pod-template-hash=6769786c4c # 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的IP地址不一致) [root@K8s-master ~]# kubectl exec -it pc-deployment-6769786c4c-62d94 -n test /bin/sh # echo "`hostname -I` web-01" > /usr/share/nginx/html/index.html # exit [root@K8s-master ~]# kubectl exec -it pc-deployment-6769786c4c-fv6jj -n test /bin/sh # echo "`hostname -I` web-02" > /usr/share/nginx/html/index.html # exit [root@K8s-master ~]# kubectl exec -it pc-deployment-6769786c4c-qn9ls -n test /bin/sh # echo "`hostname -I` web-03" > /usr/share/nginx/html/index.html # exit [root@K8s-master ~]# curl 10.244.2.2 10.244.2.2 web-01 [root@K8s-master ~]# curl 10.244.2.3 10.244.2.3 web-02 [root@K8s-master ~]# curl 10.244.1.4 10.244.1.4 web-03
1.4.2 ClusterIP类型的Service
-
ClusterIP 类型的 Service 是 Kubernetes 中最基本的服务之一,它提供了一种稳定的、只限于集群内部访问的方式。以下是 ClusterIP 类型 Service 的一些关键特点:
-
虚拟 IP 地址:ClusterIP 类型的 Service 会被分配一个虚拟的 IP 地址(Cluster IP),这个 IP 地址在 Service 的整个生命周期中保持不变。
-
集群内部通信:Pods 可以通过这个固定的 Cluster IP 来访问 Service,从而实现集群内部的服务发现和负载均衡,而无需关心后端 Pods 的具体 IP 地址。
-
不对外公开:ClusterIP 类型的 Service 不会被分配外部可访问的 IP 地址,因此它只能从集群内部访问,不能从集群外部直接访问。
-
负载均衡:Kubernetes 会自动为 ClusterIP 类型的 Service 提供简单的负载均衡,将请求分发到后端的多个 Pods 上。
-
会话亲和性:可以通过设置
sessionAffinity
属性来控制会话亲和性,确保来自同一客户端的请求始终被路由到同一个后端 Pod。
-
DNS 解析:在集群内部,可以通过 Service 名称进行 DNS 解析,解析结果为分配给该 Service 的 Cluster IP。
-
适用于内部服务:ClusterIP 类型的 Service 非常适合用于需要在集群内部通信的内部服务,如数据库、缓存服务等。
-
[root@K8s-master ~]# vim service-clusterip.yaml --- apiVersion: v1 kind: Service metadata: name: svc-clusterip namespace: test spec: selector: app: nginx-pod type: ClusterIP ports: - port: 80 targetPort: 80 [root@K8s-master ~]# kubectl apply -f service-clusterip.yaml service/svc-clusterip created [root@K8s-master ~]# kubectl get svc,pod -n test NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/svc-clusterip ClusterIP 10.110.170.62 <none> 80/TCP 78s NAME READY STATUS RESTARTS AGE pod/pc-deployment-6769786c4c-62d94 1/1 Running 0 5m48s pod/pc-deployment-6769786c4c-fv6jj 1/1 Running 0 5m48s pod/pc-deployment-6769786c4c-qn9ls 1/1 Running 0 5m48s [root@K8s-master ~]# kubectl describe svc svc-clusterip -n test Name: svc-clusterip Namespace: test Labels: <none> Annotations: <none> Selector: app=nginx-pod Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.110.170.62 IPs: 10.110.170.62 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.4:80,10.244.2.2:80,10.244.2.3:80 Session Affinity: None Events: <none> #访问ClusterIP [root@K8s-master ~]# for ((i=1;i<=6;i++)) do curl 10.110.170.62; done #集群内部访问(只能再集群内部fan 10.244.2.3 web-02 10.244.2.2 web-01 10.244.1.4 web-03 10.244.2.3 web-02 10.244.2.2 web-01 10.244.1.4 web-03 [root@K8s-master ~]# ipvsadm -Ln | grep -A3 10.110.170.99:80 #默认负载均衡算法为轮询 TCP 10.110.170.62:80 rr -> 10.244.2.3:80 Masq 1 0 0 -> 10.244.2.2:80 Masq 1 0 0 -> 10.244.1.4:80 Masq 1 0 0
1.4.3 Endpoint类型的Service
-
Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,它是根据service配置文件中selector描述产生的。
-
一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换句话说,service和pod之间的联系是通过endpoints实现的。
-
Endpoint 类型 Service 的一些特点:
-
直接使用 Endpoints:Endpoint 类型的服务不提供标准的负载均衡器或代理,它允许客户端直接与 Endpoints 对象中的 Pod 进行通信。
-
无需代理:与标准的 Service 类型不同,Endpoint 类型的服务不会创建 kube-proxy 监听的端点。这意味着,使用这种服务类型的客户端需要能够直接解析和连接到 Endpoints 对象中定义的 Pod IP 地址。
-
适用于特定场景:这种服务类型适用于那些需要绕过 Kubernetes 服务发现机制的特定场景,例如,当您需要直接与 Pod 通信以进行调试或使用自定义的负载均衡器时。
-
手动管理:使用 Endpoint 类型的服务时,您需要手动管理 Endpoints 对象,包括添加和删除端点。
-
不提供服务发现:由于它不通过标准的 Kubernetes 服务发现机制,所以它不提供自动的服务发现功能。
-
安全性:直接与 Pod 通信可能会带来安全风险,因为 Pod 的 IP 地址可能会变化,而且没有内置的负载均衡或健康检查机制。
-
使用场景限制:Endpoint 类型的服务通常只在特定的用例中使用,比如当您需要直接控制网络流量或使用自定义的网络策略时。
-
[root@K8s-master ~]# kubectl get endpoints -n test -o wide NAME ENDPOINTS AGE svc-clusterip 10.244.1.4:80,10.244.2.2:80,10.244.2.3:80 93s
在 Kubernetes 中,负载分发策略用于确定如何将传入的网络请求分配到后端的 Pod 上。以下是您提到的两种策略的详细说明:
-
默认的 kube-proxy 策略:
-
随机(Random):kube-proxy 随机选择一个后端 Pod 来处理传入的请求。这种策略适用于大多数情况,因为它可以提供良好的负载均衡,并且实现起来相对简单。
-
轮询(Round Robin):kube-proxy 按顺序将请求分发给后端的每个 Pod。当所有 Pod 都具有相同的处理能力时,这种策略可以确保每个 Pod 接收到相同数量的请求。
除了随机和轮询,Kubernetes 还提供了其他几种负载分发策略,包括:
-
最小连接数(Least Connections):选择当前活跃连接数最少的 Pod。
-
源 IP(Source IP):确保来自同一个客户端 IP 的所有请求都发送到同一个 Pod,以保持会话的一致性。
-
-
会话保持:
-
Kubernetes 的服务可以配置会话保持,也称为会话亲和性或粘性会话。当启用会话保持时,来自同一个客户端的所有请求都将被路由到同一个后端 Pod,直到该会话结束。
-
这种策略对于需要保持用户会话状态的应用非常有用,例如购物车应用或需要用户登录信息的应用。
-
会话保持可以通过两种方式实现:
-
客户端 IP:基于客户端的 IP 地址进行会话保持,确保来自同一 IP 的请求总是路由到同一个 Pod。
-
Cookie 基于会话:kube-proxy 可以生成一个特殊的哈希值,存储在客户端的 Cookie 中,以确保来自同一个 Cookie 的请求被路由到同一个 Pod。
-
此模式可以使在spec中添加
sessionAffinity: ClientIP
选项,表示Service将使用客户端IP地址来进行会话亲和性(session affinity)。当一个客户端请求连接到Service时,Service将根据客户端的IP地址将请求转发到一个稳定的Pod上。这意味着来自同一个客户端的所有请求在默认情况下都会被转发到同一个Pod上,直到该Pod不再可用。
-
-
[root@K8s-master ~]# kubectl delete -f service-clusterip.yaml service "svc-clusterip" deleted [root@K8s-master ~]# vim service-clusterip.yaml --- apiVersion: v1 kind: Service metadata: name: svc-clusterip namespace: test spec: selector: app: nginx-pod clusterIP: 10.110.170.99 #可以指定IP type: ClusterIP sessionAffinity: ClientIP #表示Service将使用客户端IP地址来进行会话亲和性 ports: - port: 80 targetPort: 80 [root@K8s-master ~]# kubectl apply -f service-clusterip.yaml service/svc-clusterip created [root@K8s-master ~]# kubectl describe service/svc-clusterip -n test Name: svc-clusterip Namespace: test Labels: <none> Annotations: <none> Selector: app=nginx-pod Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.110.170.99 IPs: 10.110.170.99 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.4:80,10.244.2.2:80,10.244.2.3:80 Session Affinity: ClientIP Events: <none> [root@K8s-master ~]# ipvsadm -Ln | grep -A3 10.110.170.99:80 TCP 10.110.170.99:80 rr persistent 10800 #会话保持,在10800秒内始终由最稳定的一台提供访问 -> 10.244.1.4:80 Masq 1 0 0 -> 10.244.2.2:80 Masq 1 0 0 -> 10.244.2.3:80 Masq 1 0 0 [root@K8s-master ~]# for ((i=1;i<=6;i++)) do curl 10.110.170.99; done 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.3 web-02
1.4.4 HeadLiness类型的Service
-
在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。
-
在 Kubernetes 中,存在一种不分配 Cluster IP 的 Service 类型,称为 "Headless Service"。
-
Headless Service 的特点如下:
-
不分配 Cluster IP:与标准的 ClusterIP 类型 Service 不同,Headless Service 不会为服务分配一个虚拟 IP 地址。
-
通过域名访问:Pods 可以通过 Service 的 DNS 名称直接访问到后端的 Pods。由于没有 Cluster IP,客户端不能直接通过一个固定的 IP 地址来访问服务,而是通过 DNS 解析来获取后端 Pods 的实际 IP 地址。
-
自定义负载均衡:开发者可以自由地实现自己的负载均衡策略,而不是依赖 Kubernetes 提供的内置负载均衡。
-
DNS 解析:Kubernetes 会为每个后端 Pod 创建一个 DNS 记录。这意味着通过 DNS 查询,客户端可以获取到后端所有 Pod 的 IP 地址。
-
适用于无头服务:Headless Service 特别适合于那些需要直接与 Pod 通信的场景,例如,当使用 StatefulSets 部署具有唯一身份的 Pods 时。
-
[root@K8s-master ~]# vim service-headliness.yaml --- apiVersion: v1 kind: Service metadata: name: svc-clusterip namespace: test spec: selector: app: nginx-pod clusterIP: None #这里IP设为None type: ClusterIP sessionAffinity: ClientIP ports: - port: 80 targetPort: 80 [root@K8s-master ~]# kubectl apply -f service-headliness.yaml service/svc-clusterip created [root@K8s-master ~]# kubectl describe service/svc-clusterip -n test #这里集群IP为None Name: svc-clusterip Namespace: test Labels: <none> Annotations: <none> Selector: app=nginx-pod Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: None IPs: None Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.4:80,10.244.2.2:80,10.244.2.3:80 Session Affinity: ClientIP Events: <none> # 查看域名解析 [root@K8s-master ~]# kubectl exec -it pc-deployment-6769786c4c-62d94 -n test /bin/bash root@pc-deployment-6769786c4c-62d94:/# cat /etc/resolv.conf search test.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 [root@K8s-master ~]# nslookup svc-clusterip.test.svc.cluster.local 10.96.0.10 Server: 10.96.0.10 Address: 10.96.0.10#53 Name: svc-clusterip.test.svc.cluster.local Address: 10.244.2.2 Name: svc-clusterip.test.svc.cluster.local Address: 10.244.2.3 Name: svc-clusterip.test.svc.cluster.local Address: 10.244.1.4 [root@K8s-master ~]# sed -i 's/^nameserver/#nameserver/' /etc/resolv.conf [root@K8s-master ~]# echo 'nameserver 10.96.0.10' >> /etc/resolv.conf #指定DNS [root@K8s-master ~]# for ((i=1;i<=6;i++)) do curl svc-clusterip.test.svc.cluster.local; done 10.244.1.4 web-03 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.3 web-02 10.244.2.2 web-01
1.4.5 NodePort类型的Service
-
之前的样例中,创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过
NodeIp: NodePort
来访问Service了。
-
NodePort 类型的 Service 是 Kubernetes 中用于将服务暴露给集群外部的一种方式。下面是 NodePort 类型 Service 的一些关键点:
-
端口映射:NodePort 类型的 Service 会在集群的所有节点上打开一个静态端口(NodePort),这个端口范围通常是 30000-32767。这个端口将被映射到 Service 所代理的后端 Pods 的端口上。
-
通过节点 IP 访问:外部用户可以通过
<NodeIP>:<NodePort>
的形式访问到这个 Service。这里的NodeIP
是集群中任何节点的 IP 地址,而NodePort
是 Service 映射的端口号。
-
无需额外配置:与 LoadBalancer 类型相比,NodePort 不需要云服务商提供的额外配置或支持,因此它在不支持 LoadBalancer 的环境中非常有用。
-
安全性考虑:由于 NodePort 会在所有节点上开放端口,因此需要考虑到安全性问题。通常建议使用防火墙规则来限制访问,只允许特定的 IP 地址访问这些端口。
-
适用于简单的外部访问:NodePort 适合于简单的外部访问需求,但对于生产环境,通常推荐使用 LoadBalancer 或 Ingress,因为它们提供了更好的性能、安全性和高级路由功能。
-
[root@K8s-master ~]# vim service-nodeport.yaml --- apiVersion: v1 kind: Service metadata: name: svc-nodeport namespace: test spec: selector: app: nginx-pod type: NodePort ports: - port: 80 # Service 在所有节点上监听的端口号 nodePort: 32522 # 默认的取值范围是:30000-32767, 如果不指定,会默认分配 targetPort: 80 # 后端 Pod 监听的端口号 [root@K8s-master ~]# kubectl apply -f service-nodeport.yaml service/svc-nodeport created [root@K8s-master ~]# kubectl get svc,pod -n test -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/svc-nodeport NodePort 10.102.4.48 <none> 80:32522/TCP 5s app=nginx-pod NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/pc-deployment-6769786c4c-62d94 1/1 Running 0 74m 10.244.2.2 k8s-node-02 <none> <none> pod/pc-deployment-6769786c4c-fv6jj 1/1 Running 0 74m 10.244.2.3 k8s-node-02 <none> <none> pod/pc-deployment-6769786c4c-qn9ls 1/1 Running 0 74m 10.244.1.4 k8s-node-01 <none> <none>
-
外部访问测试
[root@K8s-master ~]# ipvsadm -Ln | grep -A3 32522 TCP 192.168.110.21:32522 rr -> 10.244.1.4:80 Masq 1 0 0 -> 10.244.2.2:80 Masq 1 0 1 -> 10.244.2.3:80 Masq 1 0 1 -- TCP 10.244.0.0:32522 rr -> 10.244.1.4:80 Masq 1 0 0 -> 10.244.2.2:80 Masq 1 0 0 -> 10.244.2.3:80 Masq 1 0 0
1.4.6 LoadBalancer类型的Service
-
LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。
-
LoadBalancer 类型的 Service 是 Kubernetes 中用于将服务暴露给外部世界的一种方式,它通常需要云服务商的支持。以下是 LoadBalancer 类型 Service 的一些关键特点:
-
外部负载均衡器:LoadBalancer 类型的 Service 会利用云服务商提供的外部负载均衡器(如 AWS ELB、Azure Load Balancer 或 GCP Load Balancer)来暴露服务。
-
自动配置:Kubernetes 会自动配置负载均衡器,将外部请求路由到后端的 Pods。
-
外部 IP 地址:LoadBalancer 会提供一个或多个外部可访问的 IP 地址,这些 IP 地址可以是公网 IP 或云服务商的内部 IP。
-
自动扩展:许多云服务商的负载均衡器支持自动扩展,这意味着它们可以根据流量自动调整后端 Pods 的数量。
-
安全性和性能:LoadBalancer 提供了额外的安全和性能优势,因为它们通常包括内置的防火墙规则、SSL 终止和 DDoS 防护。
-
适用于生产环境:由于其高级特性和性能,LoadBalancer 类型的 Service 非常适合生产环境。
-
可能涉及费用:使用云服务商的负载均衡器可能会产生额外的费用。
-
1.4.6.1 开源 Kubernetes 负载均衡器
1. MetalLB
-
介绍:MetalLB 是一个为 Kubernetes 集群设计的负载均衡器,特别适用于裸金属环境。它通过在集群中运行的组件来监控服务对象的变化,为 LoadBalancer 类型的服务提供实现。10^
-
工作原理:MetalLB 通过标准的路由协议(如 ARP、NDP 或 BGP)来实现负载均衡。它有两种工作模式:Layer2 模式和 BGP 模式。在 Layer2 模式下,MetalLB 会在集群中的一个节点上处理所有服务 IP 的流量,而在 BGP 模式下,它通过与网络路由器建立 BGP 会话来实现真正的负载均衡。2^11^
-
特点:MetalLB 易于部署,支持自动和静态 IP 地址分配,并且可以与 kube-proxy 紧密集成。
2. PureLB
-
介绍:PureLB 是另一种负载均衡器,它使用自定义资源(CRD)进行配置,由两部分组成:分配器负责分配 IP 地址,而 lbnodeagent 负责在所有节点上配置节点网络。
-
工作原理:PureLB 的 Layer2 工作模式特点在于它会在 Kubernetes 集群的受管节点上创建一个虚拟网卡(如 kube-lb0),并通过任意路由协议实现 ECMP(等价多路径),以进行负载均衡和流量分发。
-
特点:PureLB 可以根据单个 VIP 选择节点,并将多个 VIP 分散到不同节点,以此来避免节点负载失衡。
3. OpenELB
-
介绍:OpenELB 是由 KubeSphere 社区发起的云原生负载均衡器,现已成为 CNCF 沙箱项目。它适用于物理机和虚拟化环境,利用 BGP 和 ECMP 实现高性能和高可用性。
-
工作原理:OpenELB 支持两种工作模式:Layer2 模式和 BGP 模式。在 BGP 模式下,它通过与集群的边界路由器建立 BGP 连接来实现高效的流量调度和管理。OpenELB 还支持 ECMP,允许在多条路径之间进行负载均衡和流量分发。6^
-
特点:OpenELB 易于与 Kubernetes 集成,提供了丰富的监控和日志功能,支持动态服务发现、流量调度和安全防护等高级特性。
特性/项目 | MetalLB | PureLB | OpenELB |
---|---|---|---|
介绍 | 适用于裸金属环境的负载均衡器 | 使用 CRD 配置的负载均衡器 | 云原生负载均衡器,CNCF 沙箱项目 |
支持环境 | 裸金属、云环境 | 裸金属、云环境 | 物理机、虚拟化环境 |
工作原理 | ARP/NDP/BGP | 任意路由协议 | BGP/ECMP |
Layer2 模式 | 支持 | 支持 | 支持 |
BGP 模式 | 支持 | 不适用 | 支持 |
IP 地址分配 | 自动和静态 | 不明确 | 动态服务发现 |
节点负载均衡 | 不适用 | 支持 | 支持 |
与 kube-proxy 集成 | 紧密集成 | 不明确 | 支持 |
监控和日志 | 基本 | 不明确 | 丰富 |
安全性 | 基本 | 不明确 | 支持 |
高级特性 | 基本 | 不明确 | 高级特性支持 |
社区活跃度 | 高 | 中 | 高 |
1.4.6.2 LoadBalancer 类型的 Service 的定义示例可能如下:
apiVersion: v1 kind: Service metadata: name: my-loadbalancer-service spec: type: LoadBalancer ports: - port: 80 # Service 的端口 targetPort: 8080 # 后端 Pod 的端口 selector: app: my-app # 选择具有该标签的 Pods
1.4.7 ExternalName类型的Service
-
ExternalName类型的Service用于引入集群外部的服务,它通过 externalName 属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。
-
ExternalName 类型的 Service 在 Kubernetes 中用于将一个 DNS 名称映射到一个外部的服务,而不需要在集群内创建任何代理或负载均衡器。这种方式非常适合于将集群内部的应用程序指向外部的数据库、API 服务或其他不在 Kubernetes 管理下的资源。
-
ExternalName 类型 Service 的一些关键特点:
-
无 Cluster IP:ExternalName 类型的 Service 不分配 Cluster IP。
-
DNS 解析:在 Kubernetes 集群内部,当对这种类型的 Service 进行 DNS 解析时,将直接返回
externalName
字段指定的值。
-
简单配置:只需要指定一个
externalName
和一个可选的port
。
-
直接访问:Pods 可以直接通过 Service 名称访问外部服务,就像访问集群内的其他服务一样。
-
适用于外部资源:适合于引用那些不在 Kubernetes 控制范围内的外部服务。
-
无需特殊网络配置:不需要任何特殊的网络配置或云服务提供商支持。
-
-
配置实例
apiVersion: v1 kind: Service metadata: name: my-external-service spec: type: ExternalName externalName: api.example.com # 外部服务的地址 ports: - port: 443 # 外部服务监听的端口