引言:从Pod的脆弱性说起
- 在Kubernetes中,Pod是部署的最小单位,但它们本质上是短暂且动态的。一个Pod可能因为节点故障、滚动更新或自动扩缩容而在任何时候被销毁和重建,每次重建都会获得新的IP地址。试想一下,如果前端应用直接通过IP连接后端Pod,当后端Pod重启后,前端将完全无法访问服务------这显然是不可接受的。
- 这就是Kubernetes Service要解决的核心问题:为动态变化的Pod集合提供稳定的网络抽象层。
Service的本质:稳定网络抽象
什么是service
Service是Kubernetes中的一种API对象,它定义了一组Pod的逻辑集合和访问这组Pod的策略。简单来说,Service就是:
- 一个稳定的虚拟IP(ClusterIP)
- 一个永久的DNS名称
- 一套智能的负载均衡器
Service的核心价值
bash
# 没有Service:直接访问Pod(易断连)
前端Pod → 后端Pod IP (10.244.1.2)
后端Pod重启 → IP变为10.244.1.3 → 连接中断
# 有Service:通过稳定端点访问
前端Pod → Service (clusterIP: 10.200.3.26)
Service → 后端Pod (负载均衡) →持续可用
Service的底层工作原理
kube-proxy的两种模式
-
iptables模式(默认)
- 客户端请求 → iptables规则 → 随机选择的Pod
- 优点:性能好,规则简单
- 缺点:连接失败不会重试其他Pod
-
IPVS模式(推荐生产使用)
- 优点:支持更多负载均衡算法(rr, wrr, lc, wlc等)
- 优点:更好的性能和可扩展性
- 启用IPVS模式
bash
#更改kube-proxy配置
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
sed -e 's#mode: ""#mode: "ipvs"#' | \
kubectl apply -f - -n kube-system
#删除pod使其拉起后生效配置或 kubectl rollout restart
[root@master51 ~]# kubectl get pods -o wide -n kube-system -l k8s-app=kube-proxy
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-proxy-66dzn 1/1 Running 2 (3d6h ago) 7d3h 10.0.0.51 master51 <none> <none>
kube-proxy-9tjh8 1/1 Running 2 (3d6h ago) 7d3h 10.0.0.52 worker52 <none> <none>
kube-proxy-zg282 1/1 Running 0 22h 10.0.0.53 worker53 <none> <none>
[root@master51 ~]#
[root@master51 ~]# kubectl delete pods -n kube-system -l k8s-app=kube-proxy
pod "kube-proxy-66dzn" deleted
pod "kube-proxy-9tjh8" deleted
pod "kube-proxy-zg282" deleted
[root@master51 ~]#
[root@master51 ~]# kubectl get pods -o wide -n kube-system -l k8s-app=kube-proxy
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-proxy-9q28v 1/1 Running 0 3s 10.0.0.52 worker52 <none> <none>
kube-proxy-fcp25 1/1 Running 0 3s 10.0.0.51 master51 <none> <none>
kube-proxy-n8njm 1/1 Running 0 3s 10.0.0.53 worker53 <none> <none>
[root@master51 ~]#
Endpoint与EndpointSlice
Service通过Endpoints对象跟踪Pod变化
yaml
# 自动生成的Endpoints(若创建带selector的service,系统自动创建Endpoints并持续更新,包含所有匹配 Pod 的 IP:Port;若不带selector,则需要手动创建同名 Endpoints 来定义后端端点。)
apiVersion: v1
kind: Endpoints
metadata:
name: user-service
subsets:
- addresses:
- ip: 10.100.140.89
- ip: 10.100.140.90
- ip: 10.100.203.176
ports:
- port: 80
EndpointSlice(K8s 1.21+) :性能更优,支持更多端点
yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: user-service
addressType: IPv4
ports:
- name: http
port: 80
endpoints:
- addresses:
- "10.100.140.89"
conditions:
ready: true
DNS解析机制
Service DNS的命名约定:
-
同一命名空间:<
service-name> -
跨命名空间:<
service-name>.<namespace>.svc.<cluster-domain>cluster-domain默认是cluster.local,在使用kubeadm初始化集群是可以设置:kubeadm init --service-dns-domain=yourdomain.com
bash
# 解析流程
应用请求 user-service
→ CoreDNS查询
→ 返回ClusterIP (10.200.3.26)
→ 通过kube-proxy路由到具体Pod
Service的四种类型及其应用场景
ClusterIP(默认类型)
- 最适合内部服务通信
- 集群IP,一般用于K8S集群内部的服务代理。用于K8S集群内部各个服务之间相互访问。
yaml
apiVersion: v1
kind: Service
metadata:
name: svc-clusterip
spec:
# 配置端口映射
ports:
# 表示的是service的对外端口
- port: 90
# 表示Pod的内部端口
targetPort: 80
# 关联Pod的标签
selector:
app: web
# 指定svc的类型,仅集群内部可访问
type: ClusterIP
#测试验证
[root@master51 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.200.0.1 <none> 443/TCP 6d23h
svc-clusterip ClusterIP 10.200.3.26 <none> 90/TCP 4s
#访问方式
<cluster-ip>:<port>
curl 10.200.3.26:90
NodePort
- 开发测试与简单暴露
- 在ClusterIP的基础之上,在每一个worker节点添加了NAT规则。从而达到K8S外部客户端能够访问到K8S集群内部的Pod功能。
- 用于暴露服务到k8s集群集群外部,访问任意一个worker节点都可以访问到k8s集群内部的服务。
yaml
apiVersion: v1
kind: Service
metadata:
name: svc-nodeport
spec:
ports:
- port: 90
targetPort: 80
# 指定NodePort的端口范围,默认有效值为: 30000-32767
nodePort: 30080
selector:
app: web
type: NodePort
#测试验证
[root@master51 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.200.0.1 <none> 443/TCP 7d
svc-nodeport NodePort 10.200.236.205 <none> 90:30080/TCP 4s
#访问方式
集群内部:<cluster-ip>:<port>
curl 10.200.236.205:90
集群外部:<node-ip>:<port>
curl 10.0.0.51:30080
curl 10.0.0.52:30080
curl 10.0.0.53:30080
修改Service的nodePort端口号范围
bash
#修改静态Pod的资源清单
[root@master51 ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
12 spec:
13 containers:
14 - command:
15 - kube-apiserver
16 - --service-node-port-range=3000-50000
#移出再移回使其更快加载
[root@master51 ~]# mv /etc/kubernetes/manifests/kube-apiserver.yaml /opt/
[root@master51 ~]# mv /opt/kube-apiserver.yaml /etc/kubernetes/manifests/
#验证查看组件是否正常
[root@master51 ~]# kubectl get pods -n kube-system -o wide -l component=kube-apiserver
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-apiserver-master51 1/1 Running 1 (13s ago) 8s 10.0.0.51 master51 <none> <none>
[root@master51 ~]#
#最后创建service验证即可
Loadbanlacer
- 云原生应用的标配
- 一般情况下是用于云厂商环境,配合专门的SLB相关产品进行服务的代理。如果自行部署的k8s需要单独部署第三方插件(如:metallb)来实现此功能,详细可参考:juejin.cn/post/759508...
- LoadBalancer与NodePort的本质区别在于流量的智能化路由。 NodePort虽然提供了外部访问入口,但它只是一个静态的端口映射。客户端必须指定某个具体节点的IP,如果该节点故障,需要人工干预切换流量。 而LoadBalancer则提供了一个完全抽象的"虚拟IP"(VIP)。会自动将流量分发到所有健康的节点,并在节点故障时立即从后端池(NodeIP+NodePort)中移除该节点,实现全自动的高可用。
yaml
apiVersion: v1
kind: Service
metadata:
name: svc-loadbalancer
spec:
selector:
app: web
ports:
- port: 90 #service端口(集群内:clusterIP:90)
targetPort: 80 #pod端口
#当type为LoadBalancer时,如果不指定nodePort,K8s会自动分配一个(默认范围30000-32767)
nodePort: 30080 #节点端口(集群外:nodeIP:30080)
type: LoadBalancer
ExternalName
- 连接外部服务的桥梁
- 主要应用在将K8S集群外部的某个服务映射到K8S集群内部的Service,为外部服务提供统一的Kubernetes服务发现接口,这样可以不需要记住外部服务的地址
yaml
apiVersion: v1
kind: Service
metadata:
name: svc-externalname
spec:
externalName: www.baidu.com
#externalName:myproductiondatabase.example.com
ports:
- port: 80
type: ExternalName
#测试验证
#查看DNS服务器IP
[root@master51 ~]# kubectl get svc,po -n kube-system -l k8s-app=kube-dns -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kube-dns ClusterIP 10.200.0.10 <none> 53/UDP,53/TCP,9153/TCP 7d5h k8s-app=kube-dns
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/coredns-6d8c4cb4d-bzggh 1/1 Running 0 28h 10.100.160.135 master51 <none> <none>
pod/coredns-6d8c4cb4d-l6vfm 1/1 Running 0 28h 10.100.160.133 master51 <none> <none>
[root@master51 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.200.0.1 <none> 443/TCP 7d
svc-externalname ExternalName <none> www.baidu.com 80/TCP 3s
#验证ExternalName
[root@master51 ~]# dig @10.200.0.10 svc-externalname.default.svc.geniusc.com +short
www.baidu.com.
www.a.shifen.com.
110.242.70.57
110.242.69.21
CoreDNS组件补充
- CoreDNS是K8S集群内置的DNS服务器,如果是kubeadm方式部署的话,无需手动部署该组件,但二进制部署的话需要手动部署该组件。
- CoreDNS的作用就是将svc的名称解析为CLusterIP,也可以实现Pod的负载均衡,还可以为ExternalName提供地址解析功能。