Service
⭐️ 概念:Kubernetes Service
定义了这样-种抽象: 一个 Pod
的逻辑分组, 一种可以访问它们的策略——通常称为微服务。这一组 Pod
能够被 Service
访问到,通常是通过 Label Selector
⭐️Service
能够提供负载均衡的能力,但是在使用上有以下限制:只提供4层负载均衡能力,而没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的,也就是说不能通过我们的主机名和域名来进行负载均衡,所以说要配合 ingress
来实现7层负载均衡
⭐️ Service在k8s中有4种类型:
clusterIP
:默认,就仅仅只分配集群内部的应该虚拟IPNodePort
:在ClusterIP
的基础上,service
为每个pod
绑定上了对应端口,这样就可以通过访问对应端口来访问对应服务了。LoadBalancer
:在NodePort的基础上加上了云厂商提供的外部负载均衡服务器,并将请求转发到NodePortExternalName
:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有kubernetes 1.7
或更高版本的kube-dns
才支持,也就是创建一个svc
把外部集群的ip和端口号填入svc
,pod
就可以直接访问svc
来服务外部集群,假如集群变了,则只需要修改svc
地址即可
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
规则又要做代理。
2、iptables
代理模式
是直接通过 iptables
规则来直接访问pod
3、ipvs
代理模式
⭐️ 是直接通过 ipvs
规则来直接访问 pod
这种模式,kube-proxy
会监视 Kubernetes Service
对象和 Endpoints
,调用 netlink
接口以相应地创建 ipvs
规则并定期与 Kubernetes Service
对象和 Endpoints
对象同步 ipvs
规则,以确保 ipvs
状态与期望一致。访问服务时,流量将被重定向到其中-个后端 Pod
⭐️ 与 iptables
类似,ipvs
于 netfilter
的 hook
功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs
可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外, ipvs
为负载均衡算法提供了更多选项,例如:
rr
: 轮询调度lc
: 最小链接数dh
: 目标哈希sh
: 源哈希sed
: 最短期望延迟nq
: 不排队调度
假如上面这几项又一项未达到那么还是继续使用的
iptables
规则
2️⃣ ClusterIP
⭐️ clusterlP
主要在每个 node
节点使用 iptables
,将发向 clusterIP
对应端口的数据,转发到 kube-proxy
中。然后 kube-proxy
自己内部实现有负载均衡的方法,并可以查询到这个 service
下对应 pod
的地址和端口,进而把数据转发给对应的 pod
的地址和端口
⭐️ 这里是由你的环境决定的你的环境是 ipvs
那 clusterIP
就是使用的 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
- 访问
[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 IP
、Kube-proxy
不会处理它们,而且平台也不会为它们进行负载均衡和路由,可以通过它来解决 hostname
和 ip
变化的问题。
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)解析一下无头服务,可以看见 pod
的 ip
地址直接被解析出来了
[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都可以
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
⭐️ loadBalancer
和 nodePort
其实是同一种方式。区别在于 loadBalancer
比 nodePort
多了一步,就是可以调用 cloud provider
去创建 LB
来向节点导流,但是前提是就是pod是在云服务器上
7️⃣ ExternalName
⭐️这种类型的 Service
通过返回 CNAME
和它的值,可以将服务映射到 externalName
字段的内容(例如:hub.atguigu.com
)。ExternalName
Service
是 Service
的特例,它没有 selector
,也没有定义任何的端口和 Endpoint
。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
⭐️当查询主机 my-service.defalut.svc.cluster.local
( SVC_NAME.NAMESPACE.svc.cluster.local
)时,集群的 DNS
服务将返回-一个值 my.database.example.com
的 CNAME
记录。访问这个服务的工作方式和其他的相
同,唯一不同的是重定向发生在 DNS
层,而且不会进行代理或转发