Kubernetes:深入了解Service
2021-02-05 16:17
标签:track image uber meta comment 延时 done automake label 用这个配置文件创建一个 发现有一条默认的 第一行为 回到 下面查一下具有web标签的 目前是有三个 上文提到过,说白了就是 可以看到写了标签的位置有三处,作用如下。 流量转发用的是 应用部署到 上面的 查看访问结果 没问题,这个就是以 没问题,代理模式一共是两种,一种是 现在有一个问题,暴露端口的范围只是 如果真的用 用户请求→代理服务器→ 其实流量转发是由每个 看一下之前创建的 现在在一个 可以看到有两条规则,主要是第二条,意思是有请求要访问 三个 可以看到一个 大概就是这样,不难理解,每个 再来了解一下 其二这些规则都是从上到下逐条匹配的,如果你 相信很多人都用过 使用了 这个设备的 这种规则啥子意思应该都能看懂吧, 对比一下, 一般不会去动他,默认的就够用了 Kubernetes:深入了解Service 标签:track image uber meta comment 延时 done automake label 原文地址:https://www.cnblogs.com/opesn/p/13124284.htmlKubernetes:深入了解Service
说白了就是与外界连通,大概分为5个部分。
·Service定义。
·Pod与Service的关系
·Service类型。
·Service代理模式。
·DNS
部署一个应用,例如写一个deployment,里面有一个字段replicas来指定副本数,一个应用会用很多的副本分布在各个节点上,如果某一个pod由于某种原因崩溃掉了,replicas会帮你再启一个pod,保证副本数量,新启的这个pod肯定是和原来一样的,重启之后容器的IP就发生变化了,但是要怎么动态感知容器IP呢?这就引入了Service的概念,Service可以动态感知全部Pod的IP,并且能提供对外访问入口。
Service的设计目的主要是防止Pod失联,他能获取到所有Pod的IP,所以你不用管容器的IP是什么了,全部交给Service去做就可以了。可以定义Pod的访问策略,也提供了负载均衡的功能,帮你将请求转发到后端的一个pod中,可以支持三种模式,分别是ClusterIP、NodePort、LoadBalancer,Service的底层实现主要是由iptables和IPVS两种网络模式,这两种模式决定了怎么去转发流量。
Service 定义
[root@k8s01 yml]# nginx_service.yaml
apiVersion: v1 ## API 版本
kind: Service ## 绑定的资源对象为Service
metadata: ## Service源数据,指定service名称和工作空间
name: nginx-service ## Service名称
namespace: default ## Service工作空间
spec:
clusterIP: 10.0.0.213 ## 默认使用cluster IP
ports: ## port 来定义Service的入口和容器的入口
- name: http ##指定端口名称
port: 80 ## Service 入口
protocol: TCP ## 使用协议
targetPort: 80 ## pod端口,也就是容器端口
selector: ##标签选择器,通过标签来匹配要关联的Pod
app: web ##关联到标签为app:web 的pod
Service
[root@k8s01 yml]# kubectl create -f nginx_service.yaml
service/nginx-service created
[root@k8s01 yml]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1
Service
,他的作用是在集群内部访问apiserver
的,通过kubernetes
这个DNS
名称。Service
要想动态感知后端IP
的变化,他会用到一个名为endpoints
的控制器,也就是每个Service
会对应一个endpoints
控制器,endpoints
来帮他关联后端的pod
,Service
只是关联某一组Pod
,动态感知的具体实现时交给endpoints
控制器来完成的,下面看一下。[root@k8s01 yml]# kubectl create -f deployment.yaml
deployment.apps/nginx-deployment created
[root@k8s01 yml]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.10.91:6443,192.168.10.94:6443 3d22h
nginx-service 10.244.2.22:80 16s
apiserver
的IP
及端口,默认就会创建这一条Service
,第二行就是我们刚刚创建的那个。Service
,看上文类型为ClusterIP
,也是默认的Service
类型,分配的IP
是我们刚制定的213
,如不指定会有一个随机的IP
,那个deployment
之前删掉了,现在加回来。[root@k8s01 yml]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.10.91:6443,192.168.10.94:6443 3d22h
nginx-service 10.244.0.35:80,10.244.1.56:80,10.244.2.23:80 5m53s
[root@k8s01 yml]#
endpoints
这里决定了将数据包转发到哪里,关联到这个Service
是通过标签的,也就是上文的 selector: ##标签选择器,通过标签来匹配要关联的Pod
app: web ##关联到标签为app:web 的pod
pod
,通过下面的命令。[root@k8s01 yml]# kubectl get pod -l app=web -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-79cfcb457-hrzm5 1/1 Running 0 3m13s 10.244.2.23 k8s05
pod
,可以看到容器IP
和运行节点,容器IP
和上面的对比一下,也就是说如果现在有个容器崩了,他帮你又拉起一个容器,新容器新的IP
地址,由ENDPOINTS
控制器帮你感知到新启动的容器IP
和端口,并加入到相对应的Service
中,这块对用户来说完全是透明的,查看这个Service
的详细信息。[root@k8s01 yml]# kubectl describe services nginx-service
Name: nginx-service
Namespace: default
Labels:
Pod与Service的关系
Service
通过标签选择器来匹配一组Pod
,通常来说这组Pod
都是在deployment
里面定义的,现在看一下之前创建的nginx-deployment
标签那里是怎么写的,[root@k8s01 yml]# cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: web
spec:
selector:
matchLabels:
app: web
replicas: 3
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
[root@k8s01 yml]#
#第一处这个标签可以定义多个,但是不要重复
labels:
app: web
#此为控制器使用的标签匹配,
selector:
matchLabels:
app: web
#这个是Pod标签,如果控制器要匹配到这一组Pod,控制器标签和Pod标签要保证一致,才能匹配到,Service实现了Pod的负载均衡,提供的是四层负载均衡,也就是TCP/UDP级别的。
template:
metadata:
labels:
app: web
Service 类型
有三种类型,分别如下。
ClusterIP #默认类型,分配一个集群内部可以访问的虚拟IP(VIP),主要用于集群内部通讯
NodePort #在每个Node上分配一个端口作为外部访问入口。
LoadBalancer #工作在特定Cloud Provider上,如谷歌云,aws,OpenStack
ClusterIP
iptables/IPVS
去实现的,接收到请求后选一个Pod IP & Port
将流量转发过去,访问流程大概就是请求先到iptables/IPVS→Service→Pod
,Service
逻辑上将Pod
绑定到一起,对于用户是无感知的,你只需要知道Service
的IP
即可,这个主要是集群内部用的,下面的就是之前写的一个Cluster
类型的Service
[root@k8s01 yml]# cat nginx_service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
spec:
clusterIP: 10.0.0.213
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: web
NodePort
K8S
集群中,你面临的问题就是如何让用户访问到你部署的应用,这就会用到最常用的一种方式,NodePort
就会在每一个node
上暴露一个端口出去,作为访问的统一入口,基于上面的配置改一下。[root@k8s01 yml]# cat nginx_service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
nodePort: 30010
selector:
app: web
[root@k8s01 yml]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1
Service
就是将标签为nginx
的Pod
上的80
端口发布到了Node
节点的30010端口,
Node节点的端口范围是在
apiserver`中指定的,也就是这里。[root@k8s01 yml]# cat /opt/kubernetes/cfg/kube-apiserver.conf |grep service-node-port
--service-node-port-range=30000-32767 [root@k8s01 yml]#
[root@k8s01 yml]# for i in 192.168.10.{92,93,95}:30010;do curl -I $i;done
HTTP/1.1 200 OK
Server: nginx/1.17.10
Date: Fri, 29 May 2020 10:18:16 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
Connection: keep-alive
ETag: "5e95c66e-264"
Accept-Ranges: bytes
HTTP/1.1 200 OK
Server: nginx/1.17.10
Date: Fri, 29 May 2020 10:18:16 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
Connection: keep-alive
ETag: "5e95c66e-264"
Accept-Ranges: bytes
HTTP/1.1 200 OK
Server: nginx/1.17.10
Date: Fri, 29 May 2020 10:18:16 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
Connection: keep-alive
ETag: "5e95c66e-264"
Accept-Ranges: bytes
IPVS
方式去做的负载均衡,全部节点能用ipvsadm
看到规则,下面看一下,全部node
节点安装ipvsadm
,执行命令看一下。[root@k8s01 yml]# ansible nodes -m yum -a "name=ipvsadm state=installed"
[root@k8s01 yml]# ansible nodes -m shell -a "ipvsadm -ln | grep 30010 -A 3"
192.168.10.92 | CHANGED | rc=0 >>
TCP 192.168.10.92:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 10.244.0.0:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 10.244.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 127.0.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
TCP 172.17.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
192.168.10.93 | CHANGED | rc=0 >>
TCP 192.168.10.93:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 10.244.1.0:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 10.244.1.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 127.0.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
TCP 172.17.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
192.168.10.95 | CHANGED | rc=0 >>
TCP 192.168.10.95:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 10.244.2.0:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 10.244.2.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
--
TCP 127.0.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
TCP 172.17.0.1:30010 rr
-> 10.244.0.42:80 Masq 1 0 0
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.2.29:80 Masq 1 0 0
[root@k8s01 yml]#
iptables
,另一种就是现在用的ipvs
30000-50000
,所以实际应用的时候还会加一个代理,譬如一个nginx
,使用代理服务器作为访问入口,upstream
地址池随便写几个node-IP:port
就行了,Pod
少这样玩还可以,Pod
多了够你受的,Ingress
可以解决这个问题NodePort+
代理服务发布服务,NodePort
的访问流程如下。NodeIP:Port
→PodIP:Port
LoadBalancer
[root@k8s01 yml]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1
80
端口为Pod
端口,30010
端口是在Node
节点暴露的端口,所以外部访问的端口为30010
,我需要知道这个端口才能把代理和Service
关联起来,上文我指定了这个端口,不指定的话就是随机的,但是使用公有云的LoadBalancer
就不用了,他是自动帮你添加创建的Service
及生成的端口和每个NodeIp
到LoadBalancer
,LoadBalancer
提供特定云提供商底层LB
接口,直接用去调用就好,他和NodePort
的访问流程其实一样,就是他关联后端节点端口是自动的。service代理模式
Node
上部署的kube-proxy
组件完成的,网络代理有两种模式,一种是iptables
,另一种是IPVS
,目前默认使用的都是iptables
,现在IPVS
属于是稳定版,在部署的时候采用的就是IPVS
,看一下kube-proxy
的配置文件。[root@k8s02 ~]# cat /opt/kubernetes/cfg/kube-proxy.conf
KUBE_PROXY_OPTS="--logtostderr=false --v=2 --proxy-mode=ipvs --masquerade-all=true --log-dir=/opt/kubernetes/logs --config=/opt/kubernetes/cfg/kube-proxy-config.yml"
[root@k8s02 ~]#
iptables
安装升级iptables
[root@k8s02 tools]# yum -y install gcc make libnftnl-devel libmnl-devel autoconf automake libtool bison flex libnetfilter_conntrack-devel libnetfilter_queue-devel libpcap-devel
Loaded plugins: fastestmirror
[root@k8s02 tools]# wget wget https://www.netfilter.org/projects/iptables/files/iptables-1.6.2.tar.bz2
[root@k8s02 tools]# yum -y install conntrack
tar xf iptables-1.6.2.tar.bz2
cd iptables-1.6.2/
./autogen.sh
./configure
make -j4
make install
cd /usr/local/sbin
\cp iptables /sbin
\cp iptables-restore /sbin/
\cp iptables-save /sbin/
iptables
属于是一个内核级的防火墙,当你创建一个Service
的时候,kube-proxy
会帮你在所有Node
上生成iptables
规则,在Node
节点可以通过iptables-save
查看所有规则,规则的多少在于你Pod&Service
的数量,目前用的是ipvs
模式,现在将k8s02
节点切换至iptables
模式,默认用的就是iptables
模式,所以把配置文件中的指定删掉就行了,也就是这几行。[root@k8s02 ~]# cat /opt/kubernetes/cfg/kube-proxy.conf | grep mode -A 1
--proxy-mode=ipvs --masquerade-all=true [root@k8s03 opt]# cat /opt/kubernetes/cfg/kube-proxy-config.yml
mode: ipvs
ipvs:
scheduler: "rr"
[root@k8s02 ~]# systemctl restart kubelet.service
[root@k8s02 ~]# systemctl restart kube-proxy.service
Service
[root@k8s01 yml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1
Node-1
节点上检索一下iptables
规则,找带有 10.0.0.169 的规则。[root@k8s02 ~]# iptables-save | grep "10.0.0.169"
-A KUBE-SERVICES -d 10.0.0.169/32 -p tcp -m comment --comment "default/nginx-service:http cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.0.0.169/32 -p tcp -m comment --comment "default/nginx-service:http cluster IP" -m tcp --dport 80 -j KUBE-SVC-6IM33IEVEEV7U3GP
[root@k8s02 ~]#
10.0.0.169这个
IP,目标端口是
80的都会跳转到一个名为
KUBE-SVC-6IM33IEVEEV7U3GP的链中,这就是在你创建
Service的时候
iptables模式会先创建这条规则,然后我们再看一下
KUBE-SVC-6IM33IEVEEV7U3GP`链都有啥子规则,主要是下面的这三条。[root@k8s02 ~]# iptables-save | grep "KUBE-SVC-6IM33IEVEEV7U3GP"
-A KUBE-SVC-6IM33IEVEEV7U3GP -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-2TARFLZWM4OAEKXX
-A KUBE-SVC-6IM33IEVEEV7U3GP -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-3OSEFXMGUVYQSZI7
-A KUBE-SVC-6IM33IEVEEV7U3GP -j KUBE-SEP-6SCCYNK5UB2KUKJH
[root@k8s02 ~]#
Pod
会对应有三条链,这是一组规则的集合,这组规则就实现了怎么做负载均衡,你可以理解为规则有一个权重值,例如第一条--probability 0.33332999982
,这表示有30%
的几率会被匹配到这条规则上,iptables
的规则匹配是从上到下的,第二条有50%
的几率,如果上两条都没匹配到,那就走最后一条了,然后你又会发现这三条规则分别又关联了三条不一样的链,检索一下这三条链都写了什么。[root@k8s02 ~]# iptables-save |egrep "KUBE-SEP-2TARFLZWM4OAEKXX|KUBE-SEP-3OSEFXMGUVYQSZI7|KUBE-SEP-6SCCYNK5UB2KUKJH"
:KUBE-SEP-2TARFLZWM4OAEKXX - [0:0]
:KUBE-SEP-3OSEFXMGUVYQSZI7 - [0:0]
:KUBE-SEP-6SCCYNK5UB2KUKJH - [0:0]
-A KUBE-SEP-2TARFLZWM4OAEKXX -s 10.244.0.2/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-2TARFLZWM4OAEKXX -p tcp -m tcp -j DNAT --to-destination 10.244.0.2:80
-A KUBE-SEP-3OSEFXMGUVYQSZI7 -s 10.244.1.69/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-3OSEFXMGUVYQSZI7 -p tcp -m tcp -j DNAT --to-destination 10.244.1.69:80
-A KUBE-SEP-6SCCYNK5UB2KUKJH -s 10.244.2.32/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-6SCCYNK5UB2KUKJH -p tcp -m tcp -j DNAT --to-destination 10.244.2.32:80
-A KUBE-SVC-6IM33IEVEEV7U3GP -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-2TARFLZWM4OAEKXX
-A KUBE-SVC-6IM33IEVEEV7U3GP -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-3OSEFXMGUVYQSZI7
-A KUBE-SVC-6IM33IEVEEV7U3GP -j KUBE-SEP-6SCCYNK5UB2KUKJH
[root@k8s02 ~]#
DNAT
的操作,这个操作会将数据包转发到后面的IP:Port
上,上面可以看到一共是三个,这三个正是Service
代理的IP
和端口,看一下ENDPOINTS
控制器。[root@k8s01 ~]# kubectl get endpoints nginx-service
NAME ENDPOINTS AGE
nginx-service 10.244.0.2:80,10.244.1.69:80,10.244.2.32:80 2d16h
[root@k8s01 ~]#
endpoints
都有相对应的iptables
规则,这是由kube-proxy
组件搞定的,出现变化也会刷新这些规则,这就是使用iptables
做流量转发和负载均衡实现的方法,负载均衡就是用了iptables
一个权重值的概念来实现轮训转发,流量转发使用了DNAT
帮你转发到具体的Pod
中,下面来看看IPVS
是怎么工作的。IPVS
IPVS
模式是怎么工作的,在使用iptables
的情况下,会发现有两个问题,第一个创建一个Service
,iptables
就会创建很多规则,也会涉及到更新,例如我一个pod
挂掉了重新启了一个,IP
变了,iptables
的规则就会刷新,这是一个非增量式的更新,规则越多,消耗越大。Service&Pod
越来越多,那自然的你iptables
规则就越来越多,问题就是匹配会有延时,因为一个请求进来,iptables
会从上到下进行匹配,规则越多匹配越慢,这两个问题很致命,在大规模的情况下不建议使用iptables
模式,而是使用IPVS
模式。LVS
,其实LVS
就是通过IPVS
内核调度模块实现的负载均衡,LVS
只支持四层负载均衡,如果你用过LVS
,IPVS
你就很好理解了,一个内核级的调度模块。IPVS
模式之后,它也是由kube-proxy
来维护的,当你创建一个Service
,他会生成一个虚拟设备kube-ipvs0
,看一下。[root@k8s03 ~]# ip addr| grep ipvs
5: kube-ipvs0:
IP
绑定的正是所有Service
的IP
,也就是CLUSTER-IP
,都会被绑定到这里,可以看到之前nginx-service
使用的203
在这里,再看一下IPVS
规则,也找nginx-service
的。[root@k8s03 ~]# ipvsadm -ln | grep "10.0.0.169" -A 3
TCP 10.0.0.169:80 rr
-> 10.244.1.64:80 Masq 1 0 0
-> 10.244.1.66:80 Masq 1 0 0
-> 10.244.2.30:80 Masq 1 0 0
[root@k8s03 ~]#
kube-proxy
通过IPVS
去刷新规则,IPVS
会比iptables
的性能好很多,因为IPVS
是工作在内核态,而iptables
是工作在用户态,IPVS
维护成本也比较低,不像iptables
那么麻烦。iptables与IPVS小结
iptables
比较灵活,功能也很强大,但问题就是在于更新规则都是全量去更新的,规则越多,操作越慢,再就是规则匹配这块,从上到下匹配,规则越多,匹配越慢。IPVS
是工作在内核态的,有更好的性能,而且调度算法也支持很多,iptables
暂时只支持轮训,IPVS
可以设置很多调度算法,默认rr(轮训)
,还可以使用wrr(加权轮询)&lc(最小连接)&wlc(加权最小连接)&ip hash
,在实际生产环境中最好使用ipvs
,具体制定使用什么算法可以在配置文件中指定,使用如下参数--ipvs-scheduler=算法
文章标题:Kubernetes:深入了解Service
文章链接:http://soscw.com/index.php/essay/51412.html