Kubernetes Service

1、概念

1.1 定义

Service 是一种 Kubernetes 资源,用于定义一组 Pod 的访问规则,主要解决以下问题:

  • Pod 是临时的,IP 地址会随着 Pod 重建而变化
  • 需要在动态变化的 Pod 集合上实现负载均衡
  • 需要提供稳定的网络端点供内部或外部访问

1.2 Service 的工作原理

flowchart TD A[External Traffic] --> B[Service VIP<br>10.108.132.81] B --> C[kube-proxy<br>on every Node] subgraph Cluster[Kubernetes Cluster] C --> D1[Pod A<br>192.168.1.5] C --> D2[Pod B<br>192.168.1.6] C --> D3[Pod C<br>192.168.1.7] end B -.->|DNS Query| E[CoreDNS] E --> F["EndpointSlice<br>192.168.1.5, .6, .7"] D1 & D2 & D3 --> G[Service Selector<br>app=my-app] G --> H[Deployment<br>with 3 Pods]

如图所示,其工作流程如下:

  1. 标签选择器 (Label Selector): Service 通过 selector 字段选择拥有特定标签的 Pod 作为它的后端端点 (Endpoints)。
  2. Endpoints/EndpointSlice API: Kubernetes 会自动创建与 Service 同名的 Endpoints 对象(或更新的 EndpointSlice 对象),并动态地维护一个包含所有匹配 Pod IP 和端口的目标列表。你可以通过 kubectl get endpoints <service-name> 查看。
  3. kube-proxy: 每个工作节点上运行的 kube-proxy 组件监听到 Service 和 Endpoints 的变化后,会在本机配置网络规则(例如 iptables 或 IPVS 规则),将这些规则指向实际的 Pod IPs。
  4. 稳定访问入口: Service 会被分配一个永不改变的虚拟 IP 地址,称为 ClusterIP。无论后端 Pod 如何变化,这个 IP 始终不变。
  5. CoreDNS: Kubernetes 的内置 DNS 服务会为每个 Service 创建一个 DNS 记录,格式为 <service-name>.<namespace>.svc.cluster.local。集群内的 Pod 可以通过这个 DNS 名称解析到 Service 的 ClusterIP。

当客户端(无论是集群内还是集群外)访问 Service 的虚拟 IP 或 DNS 名称时,请求会被 kube-proxy 设置的网络规则拦截,并通过负载均衡算法(默认是轮询)转发到后端一个健康的 Pod 上。

1.3 Service 与 Pod 的关系

  • 一对多关系:一个 Service 可以关联多个 Pod
  • 动态关联:当 Pod 被创建或销毁时,Service 会自动更新关联关系
  • 标签驱动:通过标签选择器实现关联,无需手动配置

2、Service类型

资源清单

kind: Service  # 资源类型
apiVersion: v1  # 资源版本
metadata: # 元数据
  name: service # 资源名称
  namespace: dev # 命名空间
spec: # 描述
  selector: # 标签选择器,用于确定当前service代理哪些pod
    app: nginx
  type: # Service类型,指定service的访问方式
  clusterIP:  # 虚拟服务的ip地址
  sessionAffinity: # session亲和性,支持ClientIP、None两个选项
  ports: # 端口信息
    - protocol: TCP 
      port: 3017  # service端口
      targetPort: 5003 # pod端口
      nodePort: 31122 # 主机端口
  • ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
  • NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
  • LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
  • ExternalName: 把集群外部的服务引入集群内部,直接使用

3、Service使用

3.1 数据准备

在使用service之前,首先利用Deployment创建出3个pod,注意要为pod设置app=nginx-pod的标签

创建deployment.yaml,内容如下:

apiVersion: apps/v1
kind: Deployment      
metadata:
  name: pc-deployment
  namespace: dev
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-master01 ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created

# 查看pod详情
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME                             READY   STATUS     IP            NODE     LABELS
pc-deployment-66cb59b984-8p84h   1/1     Running    10.244.1.39   node1    app=nginx-pod
pc-deployment-66cb59b984-vx8vx   1/1     Running    10.244.2.33   node2    app=nginx-pod
pc-deployment-66cb59b984-wnncx   1/1     Running    10.244.1.40   node1    app=nginx-pod

# 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的IP地址不一致)
# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
# echo "10.244.1.39" > /usr/share/nginx/html/index.html

#修改完毕之后,访问测试
[root@k8s-master01 ~]# curl 10.244.1.39
10.244.1.39
[root@k8s-master01 ~]# curl 10.244.2.33
10.244.2.33
[root@k8s-master01 ~]# curl 10.244.1.40
10.244.1.40

3.2 ClusterIP类型的Service

  • 在集群内部分配一个虚拟 IP 地址
  • 仅集群内部可访问
  • 用于集群内部服务间通信

创建service-clusterip.yaml文件

apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.97.97.97 # service的ip地址,如果不写,默认会生成一个
  type: ClusterIP
  ports:
  - port: 80  # Service端口       
    targetPort: 80 # pod端口
# 创建service
[root@k8s-master01 ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created

# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    13s   app=nginx-pod

# 查看service的详细信息
# 在这里有一个Endpoints列表,里面就是当前service可以负载到的服务入口
[root@k8s-master01 ~]# kubectl describe svc service-clusterip -n dev
Name:              service-clusterip
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                10.97.97.97
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>

# 查看ipvs的映射规则
[root@k8s-master01 ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 访问10.97.97.97:80观察效果
[root@k8s-master01 ~]# curl 10.97.97.97:80
10.244.2.33

# 删除service
[root@k8s-master01 ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted

3.3 HeadLiness类型的Service

kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。

主要使用场景:与 StatefulSet 搭配使用,StatefulSet 管理的每个 Pod 都有唯一且稳定的标识(如 web-0, web-1, web-2)。Headless Service 为这些 Pod 提供了稳定的 DNS 记录,格式为:<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local

创建service-headliness.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
spec:
  clusterIP: None   # 这是定义 Headless Service 的关键!
  ports:
  - port: 80        # Service 本身的端口
    name: web       # 端口名称
  selector:
    app: nginx      # 选择器,匹配具有标签 app: nginx 的 Pod
  # 不需要 type: ClusterIP,因为 clusterIP: None 已定义

3.4 NodePort类型的Service

如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过<任何节点的IP>:<NodePort>来访问service了。

创建service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # service类型
  ports:
  - port: 80
    nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配
    targetPort: 80
# 创建service
[root@k8s-master01 ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created

# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)       SELECTOR
service-nodeport   NodePort   10.105.64.191   <none>        80:30002/TCP  app=nginx-pod

# 接下来可以通过电脑主机的浏览器去访问集群中任意一个nodeip的30002端口,即可访问到pod

3.5 LoadBalancer类型的Service

LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。

当创建此类型 Service 时,Kubernetes 会自动向云平台申请并配置一个外部负载均衡器。该负载均衡器会将外部流量路由到后端的 NodePort 和 ClusterIP Service。

apiVersion: v1
kind: Service
metadata:
  name: my-loadbalancer-service
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

应用后,云平台会分配一个外部 IP(EXTERNAL-IP),你可以直接访问 http://

3.6 ExternalName类型的Service

ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。

apiVersion: v1
kind: Service
metadata:
  name: service-externalname
  namespace: dev
spec:
  type: ExternalName # service类型
  externalName: www.baidu.com  #改成ip地址也可以
# 创建service
[root@k8s-master01 ~]# kubectl  create -f service-externalname.yaml
service/service-externalname created

# 域名解析
[root@k8s-master01 ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com.          30      IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       30      IN      A       39.156.66.18
www.a.shifen.com.       30      IN      A       39.156.66.14
posted @ 2025-09-11 16:32  xclic  阅读(13)  评论(0)    收藏  举报