Skip to content

四层代理Service

四层负载均衡Service基本介绍

在Kubernetes中,四层代理(Layer·4·Proxy)是指基于网络层(传输层)的代理,用于将网络请 求从一个源地址路由到一个目标地址。在Kubernetes中,Service(服务)作为四层代理的一种实现方 式,用于实现服务的负载均衡和服务发现。 Service是Kubernetes中的一个资源对象,它定义了一组具有相同标签的Pod的逻辑集合,并为 这组Pod分配了一个虚拟IP(Cluster·IP)。Service充当了网络终结点,客户端可以通过访问Service 的虚拟IP来访问后端的Pod。

以下是Service的基本介绍: - Cluster·IP:每个Service都分配了一个Cluster·IP,它是一个虚拟的内部IP地址,用于在集群内部进行访问。这个虚拟IP是由Kubernetes自动分配的,并且与Service对象一一对应。

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   71d
$ kubectl describe svc kubernetes
Name:              kubernetes
Namespace:         default
Labels:            component=apiserver
                   provider=kubernetes
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.0.1
IPs:               10.96.0.1
Port:              https  443/TCP
TargetPort:        6443/TCP
Endpoints:         192.168.59.63:6443
Session Affinity:  None
Events:            <none
# 也就是访问 10.96.0.1 时就可以访问 192.168.59.63:6443 这个服务
  • 端口映射:Service可以映射一个或多个端口到后端Pod的端口。这意味着客户端可以通过访问Service的某个端口来访问后端Pod的应用程序。
  • 负载均衡:Service使用四层代理实现负载均衡,将来自客户端的请求均匀地分发到后端的Pod。当多个Pod属于同一个Service时,Service会自动将请求路由到可用的Pod上,以实现负载均衡。
  • DNS解析:每个Service都会自动注册到Kubernetes集群的内置DNS中,通过服务名称可以解 析出Service的虚拟IP。这样,客户端可以使用服务名称作为域名来访问Service,而无需知道具体的 虚拟IP地址。
$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   71d
# 其完整的域名 kubernetes.default.svc.cluster.local
# 其靠 kube-dns 将这个域名解析为 10.96.0.1 
$ kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   71d

Service与pod之间的关系

alt text

为什么不直接请求Pod的ip?而是要创建Service? Pod有生命周期,pod里面的镜像要更新,更新镜像就需要重新创建pod,那如果pod不是共享 物理机ip,pod重新创建ip就会变化,就没法访问了。

在Kubernetes中创建Service时,会使用标签选择器(Label·Selector)来筛选符合条件的 Pod,并为这些Pod创建与Service同名的Endpoint对象。Endpoint对象记录了Service所关联的 后端Pod的网络地址信息。

$ kubectl api-resources | grep Endpoints
endpoints                         ep           v1                                     true         Endpoints
$ kubectl get ep
NAME         ENDPOINTS            AGE
kubernetes   192.168.59.63:6443   71d

当Pod的地址发生变化时时(例如Pod重启、扩缩容导致Pod重新调度等),Endpoint对象会相应 地更新Pod 地址

当前端客户端发起请求到Service的虚拟IP时,Service通过查找与其关联的Endpoint对象,获 取到当前可用的后端Pod的地址列表。然后,由负载均衡组件(如kube-proxy)根据特定的负载均衡 算法(例如轮询、随机等)从地址列表中选择一个目标Pod,将请求转发到该Pod进行访问。

这样的架构使得前端客户端无需直接关注后端Pod的变化和调度情况,而是通过Service来访问后 端应用程序。Service提供了抽象和透明的方式来进行负载均衡和服务发现,确保了应用程序的高可用性 和可伸缩性。

# 如果配了防火墙,则此处可看转移规则
$ ipvsadm -Ln 

Kubernetes集群中的ip地址

  1. Pod·IP地址:每个运行的Pod都会分配一个独立的IP地址。Pod·IP地址是集群内部的IP 地址,用于Pod之间的通信。
  2. Service·Cluster·IP地址:Service对象分配的虚拟IP地址称为Cluster·IP。Cluster·IP是 集群内部的IP地址,用于在集群内部进行服务发现和访问。客户端可以通过访问Service的 Cluster·IP地址来访问与该Service相关联的一组Pod。
  3. Node·IP地址:Node(节点)是Kubernetes集群中的工作节点,每个节点都有一个IP 地址。Node·IP地址用于与集群外部的网络进行通信,例如从外部访问集群中的服务。Node IP地址可以是物理节点的IP地址或云提供商分配的虚拟IP地址。

这三类IP地址在Kubernetes集群中扮演不同的角色: 1. Pod·IP地址用于Pod之间的通信,实现了容器间的网络互通。 2. Service·Cluster·IP地址用于提供服务的访问入口,客户端可以通过访问Service的Cluster IP地址来访问与之关联的一组Pod。 3. Node·IP地址用于与集群外部的网络进行通信,允许外部流量进入集群或从集群中流出。

创建Service资源

kubectl explain svc.spec

  • allocateLoadBalancerNodePorts(boolean):表示是否动态分配负载均衡器的节点端口。
  • clusterlP·(string):表示Service的Cluster·IP地址,用于集群内部访问Service,默认由系统自动分配。
  • clusterlPs·([]string):表示Service的多个Cluster·IP地址,用于集群内部访问Service.
  • externallPs·([]string):表示将Service公开到集群外部的外部IP地址列表。
  • externalName·(string):表示Service的外部名称,用于将Service映射到外部DNS 名称。
  • externalTrafficPolicy·(string):·表示Service外部流量的负载均衡策略,可选值为"Local"或"Cluster"。
  • healthCheckNodePort·(integer):表示健康检查的节点端口。A
  • ipFamilies·([]string):表示Service支持的IP地址族列表。
  • ipFamilyPolicy·(string):表示Service的IP地址族策略,可选值为"SingleStack或"PreferDualStack"。
  • IbadBalancerlP·(string):表示分配给负载均衡器的IP地址。←
  • loadBalancerSourceRanges·([lstring):表示允许访问负载均衡器的源IP地址范围。
  • ports·([object)::定义Service监听的端口映射配置,包括协议、端口号和目标端口等。
  • publishNotReadyAddresses·(boolean):表示是否将未就绪的Pod的地址也发布给 Service。
  • selector(map[string]string):标签选择器,用于选择与Service关联的后端Pod。
  • sessionAffinity·(string):表示会话亲和性的策略,可选值为"None"、“ClientIP"或"ClientiP".
  • topologyKeys·([]string):表示用于服务拓扑感知的键列表。
  • type·(string):表示Service 的类型,可选值为"ClusterlP"、“NodePort"、"LoadBalancer"或"ExternalName".

Service的type类型

kubectl explain svc.spec.type

Kubernetes中的Service类型定义了不同的访问方式和应用场景。以下是几种常见的Service类 以及它们的应用场景:

  1. ClusterlP:
  2. 类型:ClusterlP是默认的Service类型。
  3. 应用场景:适用于集群内部的服务发现和访问。。ClusterlP将为Service分配一个虚拟 的Cluster·IP地址,只能在集群内部访问。通过该地址,其他Pod或Service可以访 问与之关联的一组Pod。

  4. NodePort:

  5. 类型:NodePort类型将Service公开到集群节点上的某个固定端口。
  6. 应用场景:适用于需要从集群外部访问Service的场景。i通过指定NodePort类型 Kubernetes会为Service分配一个随机的高端口号,并将该端口映射到每个节点上。 从外部网络,可以通过:的方式访问Service。

  7. LoadBalancer:

  8. 类型:LoadBalancer类型通过云服务提供商的负载均衡器将Service公开到外部网络。
  9. 应用场景:适用于需要高可用性和负载均衡的场景。通过LoadBalancer类型, Kubernetes将与云服务提供商集成,自动创建外部负载均衡器,,并将流量分发到 Service关联的Pod。外部客户端可以通过负载均衡器的公共IP访问Service。

  10. ExternalName:

  11. 类型:ExternalName类型是一种将Service映射到外部DNS名称的方式。个
  12. 应用场景:适用于将Service与外部服务集成的场景。通过ExternalName类型, Service不会分配Cluster·IP或NodePort,而是直接映射到一个外部DNS名称。兰 集群内部的Pod或Service访问该Service时,DNS解析将会直接返回该外部DNS 名称对应的IP地址。

根据应用需求和场景,选择适当的Service类型可以实现不同的访问方式和网络架构。例如,如果需 要在集群内部实现服务发现和访问,可以使用ClusterlP;如果需要从集群外部访问Service,可以选择 NodePort;如果需要高可用性和负载均衡,可以使用LoadBalancer;如果需要将Service与外部服务 集成,可以使用ExternalName。根据具体的业务需求,可以灵活选择合适的Service类型。


通过定义Service的端口,可以实现将流量从Service端口转发到后端Pod的容器端口。每个端口 定义可以映射到一个或多个后端Pod,实现负载均衡和服务发现的功能。根据实际需求,可以在 spec.ports字段中定义多个端口,以满足不同端口的访问需求。


创建ClusterlP类型的Service

clusterip.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      run: nginx
  replicas: 3
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

service_clusterip.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    run: nginx
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx
$ kubectl get pod -l run=nginx -owide
NAME                     READY   STATUS    RESTARTS       AGE   IP              NODE       NOMINATED NODE   READINESS GATES
nginx-7bd697c45f-7wqjn   1/1     Running   1 (105m ago)   14h   10.250.158.39   xuegod62   <none>           <none>
nginx-7bd697c45f-bhh52   1/1     Running   1 (105m ago)   14h   10.250.158.41   xuegod62   <none>           <none>
nginx-7bd697c45f-ln65j   1/1     Running   0              57m   10.250.158.33   xuegod62   <none>           <none>
$ kubectl get svc -l run=nginx -owide
NAME    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx   ClusterIP   10.100.102.187   <none>        80/TCP    26m   run=nginx
$ kubectl describe svc nginx
Name:              nginx
Namespace:         default
Labels:            run=nginx
Annotations:       <none>
Selector:          run=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.100.102.187
IPs:               10.100.102.187
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.250.158.33:80,10.250.158.39:80,10.250.158.41:80
Session Affinity:  None
Events:            <none>

路由规则保存在 ipvsadm -Ln 中。


创建NodePort类型的Service

vim nodepod.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-nodeport
spec:
  selector:
    matchLabels:
      run: nginx-nodeport
  replicas: 2
  template:
    metadata:
      labels:
        run: nginx-nodeport
    spec:
      containers:
      - name: my-nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

vim service_nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-nginx-nodeport
  labels:
    run: nginx-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30180
  selector:
    run: nginx-nodeport

这样每个node都有30180 并且映射到 pod的 80端口

创建ExternalName类型的Service

应用场景:跨名称空间访问 需求:default名称空间下的pod想要访问nginx名称空间下的pod服务

nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  namespace: nginx
spec:
  selector:
    matchLabels:
      run: nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

nginx-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30180
  selector:
    web: nginx

default.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: default
  namespace: default
spec:
  selector:
    matchLabels:
      app: busybox
  replicas: 2
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        command: ["/bin/sh", "-c", "sleep 3600"]

default_svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: nginx
spec:
  type: ExternalName
  externalName: nginx-svc.nginx.svc.cluster.local
  ports:
  - port: 80
    targetPort: 80
  selector:
    web: nginx

Service完整的dns名称: Service name.svc namespace.svc.cluster.local

相当于软连接 nginx-svc -> nginx-svc.nginx.svc.cluster.local

实战:映射外部服务案例分享

mysql.yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  type: ClusterIP
  ports:
    port: 3306

mysql_endpoint.yaml

apiVersion: v1
kind: Endpoints
metadata:
  name: mysql
subsets:
  addresses:
  - ip: 192.168.40.62
  ports:
    port: 3306

这样访问 mysql 域名就可以访问 192.168.40.62:3306 了

Comments