K8S学习笔记之Service和kube-proxy

Service关键概念

为了方便访问Pod资源,k8s定义了一个统一访问入口:service资源对象。service是一个固定接入层,客户端可以通过访问service的IP和端口访问到service关联的后端pod。
在这里插入图片描述

为什么定义service对象?

1、pod ip经常变化,service是pod的代理,我们客户端访问,只需要访问service,就会把请求代理到Pod
2、pod ip在k8s集群之外无法访问,所以需要创建service,这个service可以在k8s集群外访问的。

Service对象解读

1.DNS插件(coreDNS):service的名称解析是依赖于dns附件的
2.网络插件(calico):kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件
3.kube-proxy组件:
始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态。

这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的

工作原理

1.k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,据此创建与Service同名的endpoint对象。
2.当Pod 地址发生变化时,endpoint也会随之发生变化。
3.service接收前端client请求的时候,就会通过endpoint,找到转发到哪个Pod进行访问的地址。(至于转发到哪个节点的Pod,由负载均衡kube-proxy决定)

资源清单解释

# kubectl explain service.spec
allocateLoadBalancerNodePorts	<boolean>
clusterIP	<string>      #动态分配的地址,也可以自己在创建的时候指定,创建之后就改不了
clusterIPs	<[]string>
externalIPs	<[]string>
externalName	<string>
externalTrafficPolicy	<string>
healthCheckNodePort	<integer>
ipFamilies	<[]string>
ipFamilyPolicy	<string>
loadBalancerIP	<string>
loadBalancerSourceRanges	<[]string>
ports	<[]Object>              #定义service端口,用来和后端pod建立联系
publishNotReadyAddresses	<boolean>
selector	<map[string]string> #通过标签选择器选择关联的pod有哪些
sessionAffinity	<string>
sessionAffinityConfig	<Object>
#service在实现负载均衡的时候还支持sessionAffinity,sessionAffinity
什么意思?会话联系,默认是none,随机调度的(基于iptables规则调度的);如果我们定义sessionAffinity的client ip,那就表示把来自同一客户端的IP请求调度到同一个pod上
topologyKeys	<[]string>
type	<string>                #定义service的类型

# kubectl explain service.spec.type
【四类service】
ExternalName, ClusterIP, NodePort, and LoadBalancer

# kubectl explain service.spec.ports
appProtocol	<string>
name	<string>             #定义端口的名字
nodePort	<integer>        #宿主机上映射的端口,比如一个Web应用需要被k8s集群之外的其他用户访问,那么需要配置type=NodePort,若配置nodePort=30001,那么其他机器就可以通过浏览器访问scheme://k8s集群中的任何一个节点ip:30001即可访问到该服务,例如http://192.168.1.63:30001。如果在k8s中部署MySQL数据库,MySQL可能不需要被外界访问,只需被内部服务访问,那么就不需要设置NodePort
port	<integer> -required-  #service的端口,这个是k8s集群内部服务可访问的端口
protocol	<string>          #协议(TCP默认、UDP、SCTP)
targetPort	<string>          #需要绑定的pod的端口

service的四种类型

## ExternalName
适用于k8s集群内部容器访问外部资源,它没有selector,也没有定义任何的端口和Endpoint。
以下Service 定义的是将prod名称空间中的my-service服务映射到my.database.example.com
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com
当查询主机 my-service.prod.svc.cluster.local 时,群集DNS将返回值为my.database.example.com的CNAME记录。
## ClusterIP
通过k8s集群内部IP暴露服务,选择该值,服务只能够在集群内部访问,这也是默认的ServiceType。
## NodePort
通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务。通过请求<NodeIP>:<NodePort>可以把请求代理到内部的pod。Client----->NodeIP:NodePort----->Service Ip:ServicePort----->PodIP:ContainerPort。
## LoadBalancer
使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP 服务。

字段详述

类型是ClusterIP

## 使用Deployment创建Pod,副本数是2,通过Pod的IP访问资源
vim deploy-test-myapp.yaml
# curl <pod-ip>:<80>

## 创建service,通过ClusterIP访问资源
vim svc-test-myapp.yaml
kubectl apply -f svc-test-myapp.yaml
# curl <cluster-ip>:<80>

# 查看详细信息,看到EndPoints有两个IP(对应副本数)
kubectl describe svc my-nginx
kubectl get ep my-nginx
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80          #service的端口,暴露给k8s集群内部服务访问
    protocol: TCP
    targetPort: 80    #pod容器中定义的端口
  selector:
    run: my-nginx     #选择拥有run=my-nginx标签的pod 【VIP】
## 资源转发流程
1.service可以对外提供统一固定的ip地址,并将请求重定向至集群中的pod。

2.其中“将请求重定向至集群中的pod”就是通过endpoint与selector协同工作实现。

3.selector是用于选择pod,由selector选择出来的pod的ip地址和端口号,将会被记录在endpoint中。endpoint便记录了所有pod的ip地址和端口号。

4.当一个请求访问到service的ip地址时,就会从endpoint中选择出一个ip地址和端口号,然后将请求重定向至pod中。(具体把请求代理到哪个pod,需要的就是kube-proxy的轮询实现的)

5.service不会直接到pod,service是直接到endpoint资源,就是地址加端口,再由endpoint再关联到pod。

类型是NodePort

## 创建Pod资源

## 创建service资源,允许宿主机访问
vim svc-test-NodePort.yaml

##【验证】
通过宿主机浏览器访问myapp的接口资源:<ip>:<30380>/path
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-nodeport
  labels:
    run: my-nginx-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30380
  selector:
    run: my-nginx-nodeport
服务请求走向:
Client-node ip:30380->service ip:80-pod ip:container port Client ->192.168.1.63:30380->10.100.156.7:80->pod ip:80

类型是ExternalName

## 跨名称空间访问
#default名称空间下的client 服务想要访问nginx-ns名称空间下的nginx-svc服务

## 创建名称空间test
kubectl create ns test

## 创建第一个Pod资源
vim deploy-test-busybox.yaml
## 创建第一个service资源
# 该文件中指定了到 nginx-svc 的软链,让使用者感觉就好像调用自己命名空间的服务一样。
vim svc-test-ExternalName.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: client
spec: 
  replicas: 1
  selector:
    matchLabels:
      app: busybox
  template:
   metadata:
    labels:
      app: busybox
   spec:
     containers:
     - name: busybox
       image: busybox
       command: ["/bin/sh","-c","sleep 36000"]
---
apiVersion: v1
kind: Service
metadata:
  name: client-svc
spec:
  type: ExternalName
  externalName: nginx-svc.test.svc.cluster.local
  ports:
  - name: http
    port: 80
    targetPort: 80
## 创建第二个Pod资源
vim deploy-test-nginx.yaml
## 创建第二个service资源
vim svc-test-normal.yaml

## 【验证】下面两个请求结果一致,说明client-svc已经将请求转到了nginx-svc
kubectl exec -it  client-76b6556d97-xk7mg -- /bin/sh
wget -q -O - client-svc.default.svc.cluster.local
wget -q -O - nginx-svc.test.svc.cluster.local
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
   metadata:
    labels:
      app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx
       imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: test
spec:
  selector:
    app: nginx
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 80

实践案例:引用外部MySQL

## 安装MySQL
yum install mariadb-server.x86_64 -y
systemctl start mariadb

vim svc-mysql.yaml
vim ep-mysql.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  type: ClusterIP
  ports:
  - port: 3306

--- 
apiVersion: v1
kind: Endpoints
metadata:
  name: mysql
subsets:
- addresses:
  - ip: 10.11.2.58
  ports:
  - port: 3306
# kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"mysql","namespace":"default"},"spec":{"ports":[{"port":3306}],"ty...
Selector:          <none>
Type:              ClusterIP
IP:                10.97.86.201
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         10.11.2.58:3306
Session Affinity:  None

三种IP

【1】Node Network(节点网络):物理节点或者虚拟节点的网络,如ens33接口上的网路地址
【2】Pod network(pod 网络),创建的Pod具有的IP地址
    Node Network和Pod network这两种网络地址是我们实实在在配置的,其中节点网络地址是配置在节点接口之上,而pod网络地址是配置在pod资源之上的,因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的
【3】Cluster Network(集群地址,也称为service network),这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中。

kube-proxy了解

Service只是将应用对外提供服的方式做了抽象,真正的应用跑在Pod中,我们的请求转发到NodePort上,然后kube-proxy解析存储在iptables或者IPVS中的规则,来实现负载均衡。kube-proxy实现了内部从pod到service和外部的从node port向service的访问。

1、kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。
2、kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也可以称为是Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。

kube-proxy三种工作模式

【Userspace方式】
Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,由service iptables将请求转给各个节点中的Server Pod。
【iptables方式】
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折
【ipvs方式】
客户端请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。与iptables类似,ipvs基于netfilter的hook功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtCfxFt7-1658732730761)(./pic/kube-proxy-01.png)]

[1]kube-proxy都通过watch的方式监控着apiserver写入etcd中关于Pod的最新状态信息。
[2]它一旦检查到一个Pod资源被删除了或新建了,它将立即将这些变化,反应再iptables或ipvs规则中。
[3]以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。
[4]自k8s1.11以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则。

iptables规则分析

在k8s创建的service,虽然有ip地址,但是service的ip是虚拟的,不存在物理机上的,是在iptables或者ipvs规则里的。通过上面可以看到之前创建的service,会通过kube-proxyiptables中生成一个规则,来实现流量路由,有一系列目标为 KUBE-SVC-xxx 链的规则,每条规则都会匹配某个目标 ip 与端口。也就是说访问某个 ip:port 的请求会由 KUBE-SVC-xxx 链来处理。这个目标 IP 其实就是service ip。

# 查看ClusterIP
kubectl get svc 
# 根据IP查找
iptables -t nat -L | grep 10.111.145.211

KUBE-MARK-MASQ  tcp  -- !10.244.0.0/16        10.111.145.211       /* default/springboot:server cluster IP */ tcp dpt:tproxy
KUBE-SVC-N6LLN4RK7MJQ5NNI  tcp  --  anywhere             10.111.145.211       /* default/springboot:server cluster IP */ tcp dpt:tproxy

# 根据上述信息继续查询
iptables -t nat -S | grep KUBE-SVC-N6LLN4RK7MJQ5NNI

-N KUBE-SVC-N6LLN4RK7MJQ5NNI
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/springboot:server" -m tcp --dport 31180 -j KUBE-SVC-N6LLN4RK7MJQ5NNI
-A KUBE-SERVICES -d 10.111.145.211/32 -p tcp -m comment --comment "default/springboot:server cluster IP" -m tcp --dport 8081 -j KUBE-SVC-N6LLN4RK7MJQ5NNI
-A KUBE-SVC-N6LLN4RK7MJQ5NNI -m comment --comment "default/springboot:server" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YG5CH4BKPZQUH2S6
-A KUBE-SVC-N6LLN4RK7MJQ5NNI -m comment --comment "default/springboot:server" -j KUBE-SEP-WVMWCPQS2EJEYDWI

# 查找出对应Pod的端口和IP
iptables -t nat -S | grep KUBE-SEP-WVMWCPQS2EJEYDWI
iptables -t nat -S | grep KUBE-SEP-YG5CH4BKPZQUH2S6

-N KUBE-SEP-YG5CH4BKPZQUH2S6
-A KUBE-SEP-YG5CH4BKPZQUH2S6 -s 10.244.30.91/32 -m comment --comment "default/springboot:server" -j KUBE-MARK-MASQ
-A KUBE-SEP-YG5CH4BKPZQUH2S6 -p tcp -m comment --comment "default/springboot:server" -m tcp -j DNAT --to-destination 10.244.30.91:8081
-A KUBE-SVC-N6LLN4RK7MJQ5NNI -m comment --comment "default/springboot:server" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YG5CH4BKPZQUH2S6

coreDNS了解

属于k8s的一个DNS插件服务,是一种常见的服务发现手段。

验证coredns

# 安装dig
vim pod-dig.yaml
# 解析dns,如有以下返回说明dns安装成功
kubectl exec -it dig -- nslookup kubernetes

Server:		10.96.0.10
Address:	10.96.0.10#53
Name:	kubernetes.default.svc.cluster.local
Address: 10.96.0.1
apiVersion: v1
kind: Pod
metadata:
  name: dig
  namespace: default
spec:
  containers:
  - name: dig
    image:  xianchao/dig:latest
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlgebraFly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值