Service

⭐️ 概念:Kubernetes Service 定义了这样-种抽象: 一个 Pod的逻辑分组, 一种可以访问它们的策略——通常称为微服务。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector

image-20240505142344726

⭐️Service能够提供负载均衡的能力,但是在使用上有以下限制:只提供4层负载均衡能力,而没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的,也就是说不能通过我们的主机名和域名来进行负载均衡,所以说要配合 ingress来实现7层负载均衡

⭐️ Service在k8s中有4种类型:

  • clusterIP:默认,就仅仅只分配集群内部的应该虚拟IP
  • NodePort:在 ClusterIP的基础上,service为每个 pod绑定上了对应端口,这样就可以通过访问对应端口来访问对应服务了。
  • LoadBalancer:在NodePort的基础上加上了云厂商提供的外部负载均衡服务器,并将请求转发到NodePort
  • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7或更高版本的 kube-dns才支持,也就是创建一个 svc把外部集群的ip和端口号填入 svcpod就可以直接访问 svc来服务外部集群,假如集群变了,则只需要修改 svc地址即可

image-20240505142406852


1️⃣ VIP和Service代理

  • Kubernetes集群中,每个 Node运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP (虛拟IP)的形式,而不是 ExternalName的形式。在 Kubernetes v1.0版本,代理完全在 userspace.在 Kubernetes v1.1版本,新增了 iptables代理,但并不是默认的运行模式。从 Kubernetes v1.2起,默认就是 iptables代理。在 Kubernetes v1.8.0-beta.0中,添加了 ipvs代理
  • Kubernetes 1.14版本开始默认使用ipvs代理
  • Kubernetes v1.0版本,Service 是"4层”(TCP/UDP over IP)概念。Kubernetes v1.1版本,新增了 Ingress API (beta 版),用来表示"7层”(HTTP)服务
⭐️ 代理模式分类

1、userspace代理模式

iptables规则来访问到 kube-proxy在去访问 serverPOD,你要访问就要经过 kube-proxy,这样 kube-porxy就负载很大,又要被监控写 iptables规则又要做代理。

image-20240505143249190

2、iptables代理模式

是直接通过 iptables规则来直接访问pod

image-20240505144328045

3、ipvs代理模式

⭐️ 是直接通过 ipvs规则来直接访问 pod这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建 ipvs规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs规则,以确保 ipvs状态与期望一致。访问服务时,流量将被重定向到其中-个后端 Pod

⭐️ 与 iptables类似,ipvsnetfilterhook功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外, ipvs 为负载均衡算法提供了更多选项,例如:

  1. rr : 轮询调度
  2. lc : 最小链接数
  3. dh : 目标哈希
  4. sh : 源哈希
  5. sed : 最短期望延迟
  6. nq: 不排队调度

假如上面这几项又一项未达到那么还是继续使用的 iptables规则


image-20240505144525965


2️⃣ ClusterIP

⭐️ clusterlP主要在每个 node节点使用 iptables,将发向 clusterIP对应端口的数据,转发到 kube-proxy中。然后 kube-proxy自己内部实现有负载均衡的方法,并可以查询到这个 service下对应 pod的地址和端口,进而把数据转发给对应的 pod的地址和端口

⭐️ 这里是由你的环境决定的你的环境是 ipvsclusterIP就是使用的 ipvs

⚠️ 为了实现图上的功能,主要需要以下几个组件的协同工作:

  • apiserver 用户通过 kubectl命令向 apiserver发送创建 service的命令,apiserver接收到请求后将数据存储到etcd中
  • kube-proxy kubernetes的每个节点中都有-一个叫做 kube-porxy的进程,这个进程负责感知 service,pod的变化,并将变化的信息写入本地的 iptables规则中
  • iptables/ipvs使用 NAT等技术将 virtuallP的流量转至 endpoint

3️⃣实列Service

1)创建pod

[root@master ~]# vim pod/svc-deploypod.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: svc-nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
      version: v1
  template:
    metadata:
      name: nginx
      labels:
        app: webapp
        version: v1
    spec:
      containers:
      - name: webapp
        image: harbor.tanc.com/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: http
          protocol: TCP

2)查看pod和deployment

[root@master ~]# kubectl get deploy
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
svc-nginx-deployment   3/3     3            3           7m55s

[root@master ~]# kubectl get pod
NAME                                   READY   STATUS    RESTARTS   AGE
svc-nginx-deployment-9bf97bf55-54tj4   1/1     Running   0          7m57s
svc-nginx-deployment-9bf97bf55-mx7sk   1/1     Running   0          7m57s
svc-nginx-deployment-9bf97bf55-rljqn   1/1     Running   0          7m57s

3)我们现在是可以用默认的 svc来访问到 pod

 [root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   22h

4)我们查看一下 ipvs

[root@master ~]# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.96.0.1:443 rr
  -> 192.168.100.10:6443          Masq    1      3          0     
TCP  10.96.0.10:53 rr
  -> 10.244.0.2:53                Masq    1      0          0     
  -> 10.244.0.3:53                Masq    1      0          0     
TCP  10.96.0.10:9153 rr
  -> 10.244.0.2:9153              Masq    1      0          0     
  -> 10.244.0.3:9153              Masq    1      0          0     
UDP  10.96.0.10:53 rr
  -> 10.244.0.2:53                Masq    1      0          0     
  -> 10.244.0.3:53                Masq    1      0          0     

5)在查看一下 pod ip,可以看到 ipvs内并没有映射 pod

[root@master ~]# kubectl get pod -o wide
NAME                                   READY   STATUS    RESTARTS   AGE     IP            NODE    NOMINATED NODE   READINESS GATES
svc-nginx-deployment-9bf97bf55-54tj4   1/1     Running   0          9m49s   10.244.2.34   node2   <none>           <none>
svc-nginx-deployment-9bf97bf55-mx7sk   1/1     Running   0          9m49s   10.244.1.45   node1   <none>           <none>
svc-nginx-deployment-9bf97bf55-rljqn   1/1     Running   0          9m49s   10.244.1.44   node1   <none>           <none>

6)创建 svc

[root@master ~]# vim pod/svc.yaml 

apiVersion: v1
kind: Service
metadata:
  name: svc-nginx
  labels:
    app: webapp
    version: v1
spec:
  type: ClusterIP
  selector:
    app: webapp
    version: v1
  ports:
  - port: 80
    targetPort: 80

创建后查看

[root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   22h
svc-nginx    ClusterIP   10.109.8.90   <none>        80/TCP    56s

查看 ipvs

[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
  TCP  10.109.8.90:80 rr
  -> 10.244.1.44:80               Masq    1      0          0     
  -> 10.244.1.45:80               Masq    1      0          0     
  -> 10.244.2.34:80               Masq    1      0          0         xxxxxxxxxx8 1[root@master ~]# ipvsadm -Ln2IP Virtual Server version 1.2.1 (size=4096)3Prot LocalAddress:Port Scheduler Flags4  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn5  TCP  10.109.8.90:80 rr6  -> 10.244.1.44:80               Masq    1      0          0         7  -> 10.244.1.45:80               Masq    1      0          0         8  -> 10.244.2.34:80               Masq    1      0          0         
  1. 访问
[root@master ~]# curl -L 10.109.8.90
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


4️⃣ Headless Service实列

⭐️ 有时你不想要负载均衡,以及单独的 Service IP,遇到这种情况、可以通过指定 ClusterIP的值为“None” 来创建 Headless Service。这类 Service并不会分配 Cluster IPKube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由,可以通过它来解决 hostnameip变化的问题。

1)创建 svc

[root@master ~]# cat pod/hd.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app: webapp
    version: v1
  ports:
  - name: nginx
    port: 80
    targetPort: 80
    protocol: TCP

2)查看 svc,我们可以看见是没有 ip地址的我们用 DNS解析一下 svc

[root@master ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes       ClusterIP   10.96.0.1     <none>        443/TCP   23h
nginx-headless   ClusterIP   None          <none>        80/TCP    17s
svc-nginx        ClusterIP   10.109.8.90   <none>        80/TCP    37m

pod是可以同时连接多个 svc

这个域名是 svc名+命名空间名+svc+cluster.local

[root@master ~]# dig -t A svc-nginx.default.svc.cluster.local. @10.244.0.2

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.5 <<>> -t A svc-nginx.default.svc.cluster.local. @10.244.0.2
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35197
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;svc-nginx.default.svc.cluster.local. IN	A

;; ANSWER SECTION:
svc-nginx.default.svc.cluster.local. 30	IN A	10.109.8.90

;; Query time: 0 msec
;; SERVER: 10.244.0.2#53(10.244.0.2)
;; WHEN: Sat Aug 21 04:26:55 EDT 2021
;; MSG SIZE  rcvd: 115

3)解析一下无头服务,可以看见 podip地址直接被解析出来了

[root@master ~]# dig -t A nginx-headless.default.svc.cluster.local. @10.244.0.2

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.5 <<>> -t A nginx-headless.default.svc.cluster.local. @10.244.0.2
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29123
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nginx-headless.default.svc.cluster.local. IN A

;; ANSWER SECTION:
nginx-headless.default.svc.cluster.local. 30 IN	A 10.244.1.45
nginx-headless.default.svc.cluster.local. 30 IN	A 10.244.2.34
nginx-headless.default.svc.cluster.local. 30 IN	A 10.244.1.44

;; Query time: 0 msec
;; SERVER: 10.244.0.2#53(10.244.0.2)
;; WHEN: Sat Aug 21 04:29:08 EDT 2021
;; MSG SIZE  rcvd: 237



5️⃣ NodePort

⭐️ nodePort的原理在于在 node上开了一个端口, 将向该端口的流量导入到 kube-proxy,然后由 kube-proxy进一步到给对应的 pod,也就是说它会在你的node节点上随机生成一个端口来映射service的端口

1)创建 svc

[root@master ~]# cat pod/nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
  labels:
    app: nodeport
spec:
  type: NodePort
  selector:
    app: webapp
    version: v1
  ports:
  - port: 80
    targetPort: 80

2)查看 svc可以看见80:30664

[root@master ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP        24h
nginx-headless   ClusterIP   None             <none>        80/TCP         49m
nginx-nodeport   NodePort    10.108.196.116   <none>        80:30664/TCP   5s
svc-nginx        ClusterIP   10.109.8.90      <none>        80/TCP         86m

3)打开浏览器访问--只要是集群的ip都可以

image-20240505150307959

4)查看 ipvs

[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
  TCP  192.168.100.10:30664 rr
  -> 10.244.1.44:80               Masq    1      0          0     
  -> 10.244.1.45:80               Masq    1      0          0     
  -> 10.244.2.34:80               Masq    1      1          0  

6️⃣ LoadBalancer

⭐️ loadBalancernodePort其实是同一种方式。区别在于 loadBalancernodePort多了一步,就是可以调用 cloud provider去创建 LB来向节点导流,但是前提是就是pod是在云服务器上

image-20240505150406491


7️⃣ ExternalName

⭐️这种类型的 Service通过返回 CNAME和它的值,可以将服务映射到 externalName字段的内容(例如:hub.atguigu.com )。ExternalName ServiceService的特例,它没有 selector,也没有定义任何的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务

⭐️当查询主机 my-service.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的 DNS服务将返回-一个值 my.database.example.comCNAME记录。访问这个服务的工作方式和其他的相

同,唯一不同的是重定向发生在 DNS层,而且不会进行代理或转发