【云原生】kubernetes中的service原理、应用实战案例解析

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生k8s,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:云原生K8S,零基础到进阶实战
景天的主页:景天科技苑

文章目录

  • k8s四层负载均衡Service
    • 1.为什么要有Service?
    • 2.Service概述
    • 3.Service工作原理
      • 1.service负载分发策略
      • 2.k8s服务发现方式
      • 3.k8s服务发现原理
      • 4.endpoint controller
    • 4.kubernetes集群中有三类IP地址
      • 1、Node Network(节点网络)
      • 2、Pod network(pod 网络)
      • 3、Cluster Network(集群地址,也称为service network)
    • 5.创建Service资源
      • 1.Service的四种类型详解
      • 2.ExternalName
      • 3.ClusterIP
      • 4.NodePort
      • 5.LoadBalancer
    • 6.Service的端口
      • 1.创建Service:type类型是ClusterIP
      • 2.创建Service:type类型是NodePort
      • 3.创建Service:type类型是ExternalName
    • 7.k8s最佳实践:映射外部服务案例分享--service代理--自定义endpoints资源
    • 8.Service代理:kube-proxy组件详解
      • 1.kube-proxy组件介绍
      • 2.kube-proxy三种工作模式
      • 3.kube-proxy生成的iptables规则分析
    • 9.Service服务发现:coredns组件详解

k8s四层负载均衡Service

1.为什么要有Service?

在kubernetes中,Pod是有生命周期的,如果Pod重启它的IP很有可能会发生变化。
如果我们的服务都是将Pod的IP地址写死,Pod挂掉或者重启,和刚才重启的pod相关联的其他服务将会找不到它所关联的Pod,
为了解决这个问题,在kubernetes中定义了service资源对象,Service 定义了一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,
service是一组Pod的逻辑集合,这一组Pod能够被Service访问到,通常是通过Label Selector实现的。

1、pod ip经常变化,service是pod的代理,我们客户端访问,只需要访问service,就会把请求代理到Pod

2、pod ip在k8s集群之外无法访问,所以需要创建service,这个service可以在k8s集群外访问的。

2.Service概述

service是一个固定接入层,客户端可以通过访问service的ip和端口访问到service关联的后端pod,
这个service工作依赖于在kubernetes集群之上部署的一个附件,

就是kubernetes的dns服务(不同kubernetes版本的dns默认使用的也是不一样的,
1.11之前的版本使用的是kubeDNs,较新的版本使用的是coredns),

service的名称解析是依赖于dns附件的,因此在部署完k8s之后需要再部署dns附件,
kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico等)。
每个K8s节点上都有一个组件叫做kube-proxy,

kube-proxy这个组件将始终监视着apiserver中有关service资源的变动信息,
需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,
这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,

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

3.Service工作原理

service的ip和endpoints,pod的ip为什么能保存到防火墙规则里?是通过kube-proxy组件决定的
k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,
据此创建与Service同名的endpoints对象,当Pod 地址发生变化时,endpoint也会随之发生变化,
service接收前端client请求的时候,就会通过endpoint,找到转发到哪个Pod进行访问的地址。
(至于转发到哪个节点的Pod,由负载均衡kube-proxy决定)

若service定义中没有selector字段,service被创建时,endpoint controller不会自动创建endpoint。

1.service负载分发策略

service 负载分发策略有两种:

  • RoundRobin:轮询模式,即轮询将请求转发到后端的各个pod上(默认模式);

  • SessionAffinity:基于客户端IP地址进行会话保持的模式,第一次客户端访问后端某个pod,之后的请求都转发到这个pod上。

2.k8s服务发现方式

DNS:可以通过cluster add-on的方式轻松的创建KubeDNS来对集群内的Service进行服务发现————这也是k8s官方强烈推荐的方式。
为了让Pod中的容器可以使用kube-dns来解析域名,k8s会修改容器的/etc/resolv.conf配置。

3.k8s服务发现原理

endpoint
endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址。

service配置selector,endpoint controller才会自动创建对应的endpoint对象;否则,不会生成endpoint对象.

例如,k8s集群中创建一个名为k8s-classic-1113-d3的service,就会生成一个同名的endpoint对象,如下图所示。
其中ENDPOINTS就是service关联的pod的ip地址和端口。

4.endpoint controller

endpoint controller是k8s集群控制器的其中一个组件,其功能如下:

负责生成和维护所有endpoint对象的控制器

负责监听service和对应pod的变化

监听到service被删除,则删除和该service同名的endpoint对象

监听到新的service被创建,则根据新建service信息获取相关pod列表,然后创建对应endpoint对象

监听到service被更新,则根据更新后的service信息获取相关pod列表,然后更新对应endpoint对象

监听到pod事件,则更新对应的service的endpoint对象,将podIp记录到endpoint中

4.kubernetes集群中有三类IP地址

1、Node Network(节点网络)

  • 物理节点或者虚拟节点的网络,如ens33接口上的网路地址

2、Pod network(pod 网络)

  • 创建的Pod具有的IP地址

查看pod的ip

kubectl get pods -n kube-system -owide

在这里插入图片描述

Node Network和Pod network这两种网络地址是我们实实在在配置的,
其中节点网络地址是配置在节点接口之上,而pod网络地址是配置在pod资源之上的,
因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的

3、Cluster Network(集群地址,也称为service network)

  • 这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中。
kubectl get svc

在这里插入图片描述

5.创建Service资源

查看定义Service资源需要的字段有哪些?

[root@master01 ~ ]#kubectl explain service.spec
KIND:     Service
VERSION:  v1RESOURCE: spec <Object>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>topologyKeys	<[]string>type	<string>  #定义service的类型

service在实现负载均衡的时候还支持sessionAffinity,sessionAffinity
什么意思?会话联系,默认是none,随机调度的(基于iptables规则调度的);
如果我们定义sessionAffinity的client ip,那就表示把来自同一客户端的IP请求调度到同一个pod上,类似于nginx的会话保持

1.Service的四种类型详解

查看定义Service.spec.type需要的字段有哪些?

[root@master01 ~ ]#kubectl explain service.spec.type
KIND:     Service
VERSION:  v1FIELD:    type <string>DESCRIPTION:type determines how the Service is exposed. Defaults to ClusterIP. Validoptions are ExternalName, ClusterIP, NodePort, and LoadBalancer."ClusterIP" allocates a cluster-internal IP address for load-balancing toendpoints. Endpoints are determined by the selector or if that is notspecified, by manual construction of an Endpoints object or EndpointSliceobjects. If clusterIP is "None", no virtual IP is allocated and theendpoints are published as a set of endpoints rather than a virtual IP."NodePort" builds on ClusterIP and allocates a port on every node whichroutes to the same endpoints as the clusterIP. "LoadBalancer" builds onNodePort and creates an external load-balancer (if supported in the currentcloud) which routes to the same endpoints as the clusterIP. "ExternalName"aliases this service to the specified externalName. Several other fields donot apply to ExternalName services. More info:https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types

2.ExternalName

适用于k8s集群内部容器访问外部资源,它没有selector,也没有定义任何的端口和Endpoint。
以下Service 定义的是将prod名称空间中的my-service服务映射到my.database.example.com

kind: Service
apiVersion: v1
metadata:name: my-servicenamespace: prod
spec:type: ExternalNameexternalName: my.database.example.com

当查询主机 my-service.prod.svc.cluster.local 时,群集DNS将返回值为my.database.example.com的CNAME记录。

service的FQDN是:

<service_name>.<namespace>.svc.cluster.localmy-service.prod.svc.cluster.local

如果使用externalName时,集群内部访问my-service.prod.svc.cluster.local 就相当于访问externalName配置的
my.database.example.com,这个可以是是集群之外的域名

3.ClusterIP

通过k8s集群内部IP暴露服务,选择该值,服务只能够在集群内部访问,这也是默认的ServiceType。

查看系统默认的service type

kubectl get svc -n kube-system

在这里插入图片描述

4.NodePort

通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务。
通过请求<NodeIP>:<NodePort>可以把请求代理到内部的pod。
Client----->NodeIP:NodePort----->Service Ip:ServicePort----->PodIP:ContainerPort。

适用于集群外部访问内部资源,service会在宿主机上映射出一个端口

[root@k8s-master ~ ]#kubectl get svc -n h5-web
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
new-openaccount      NodePort    10.43.68.177    <none>        8898:8898/TCP    10d
opencardadmin        NodePort    10.43.248.155   <none>        8899:31180/TCP   14d

这种就是NodePort类型的service,会在宿主机映射出指定的端口

[root@k8s-master ~ ]#netstat -lanptu|grep 31180
tcp        0      0 0.0.0.0:31180           0.0.0.0:*               LISTEN      4171449/kube-proxy  

5.LoadBalancer

使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP 服务。

云提供商的负载均衡器,阿里云,华为云,腾讯云等

6.Service的端口

查看service的spec.ports字段如何定义?

[root@master01 ~]# kubectl explain service.spec.ports
KIND:     Service
VERSION:  v1
RESOURCE: ports <[]Object>
DESCRIPTION:The list of ports that are exposed by this service. More info:https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxiesServicePort contains information on service's port.
FIELDS: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可能不需要被外界访问,
只需被内部服务访问,那么就不需要设置NodePortport	<integer> -required-  #service的端口,这个是k8s集群内部服务可访问的端口protocol	<string>targetPort	<string>      # targetPort是pod上的端口
# targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上,
最后进入容器。与制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx暴露80端口。

1.创建Service:type类型是ClusterIP

1、创建Pod
#把nginx.tar.gz上传到node01和node02,手动解压

[root@node01 ~ ]#docker load -i nginx.tar.gz 
[root@node02 ~ ]#docker load -i nginx.tar.gz 
[root@master01 service ]#cat pod_test.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:name: my-nginx
spec:selector:matchLabels:run: my-nginxreplicas: 2template:metadata:labels:run: my-nginxspec:containers:- name: my-nginximage: nginximagePullPolicy: IfNotPresentports:- containerPort: 80  #pod中的容器需要暴露的端口

#更新资源清单文件

[root@master01 service ]#kubectl apply -f pod_test.yaml 
deployment.apps/my-nginx created[root@master01 service ]#kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
my-nginx-5684588fff-2nwtp   1/1     Running   0          11s
my-nginx-5684588fff-dcmgj   1/1     Running   0          11s
[root@master01 service ]#kubectl get pods -l run=my-nginx -owide
NAME                        READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
my-nginx-5684588fff-2nwtp   1/1     Running   0          8m24s   172.21.231.129   node02   <none>           <none>
my-nginx-5684588fff-dcmgj   1/1     Running   0          8m24s   172.29.55.7      node01   <none>           <none>

请求pod ip地址,查看结果

[root@master01 service ]#curl http://172.21.231.129
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
[root@master01 service ]#curl 172.29.55.7
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
[root@master01 service ]#kubectl exec -it my-nginx-5684588fff-2nwtp -- /bin/bash
root@my-nginx-5684588fff-2nwtp:/# ls -l
total 12root@my-nginx-5684588fff-2nwtp:/# curl 172.21.231.129
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>

需要注意的是,pod虽然定义了容器端口,但是不会使用调度到该节点上的80端口,
也不会使用任何特定的NAT规则去路由流量到Pod上。 这意味着可以在同一个节点上运行多个 Pod,
使用相同的容器端口,并且可以从集群中任何其他的Pod或节点上使用IP的方式访问到它们。

误删除其中一个Pod:

[root@master01 service ]#kubectl delete pods my-nginx-5684588fff-2nwtp
pod "my-nginx-5684588fff-2nwtp" deleted
[root@master01 service ]#kubectl get pods -owide
NAME                        READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
my-nginx-5684588fff-c5hnr   1/1     Running   0          6s     172.21.231.130   node02   <none>           <none>
my-nginx-5684588fff-dcmgj   1/1     Running   0          122m   172.29.55.7      node01   <none>           <none>

可见有生成一个新的pod,并且ip发生了变化,所以需要在pod前端加一个固定接入层。接下来创建service:

查看pod标签:

[root@master01 service ]#kubectl get pods --show-labels
NAME                        READY   STATUS    RESTARTS   AGE    LABELS
my-nginx-5684588fff-c5hnr   1/1     Running   0          2m     pod-template-hash=5684588fff,run=my-nginx
my-nginx-5684588fff-dcmgj   1/1     Running   0          123m   pod-template-hash=5684588fff,run=my-nginx

2、创建Service

[root@master01 service ]#vim service_test.yaml 
apiVersion: v1
kind: Service
metadata:name: my-nginxlabels:run: my-service
spec:type: ClusterIPports:- port: 80          #service的端口,暴露给k8s集群内部服务访问 protocol: TCPtargetPort: 80    #pod容器中定义的端口selector:run: my-nginx     #选择拥有run=my-nginx标签的pod

上述yaml文件将创建一个 Service,具有标签run=my-nginx的Pod,目标TCP端口 80,
并且在一个抽象的Service端口(targetPort:容器接收流量的端口;port:抽象的 Service 端口,
可以使任何其它 Pod访问该 Service 的端口)上暴露。

[root@master01 service ]#kubectl apply -f service_test.yaml 
service/my-nginx created
[root@master01 service ]#
[root@master01 service ]#
[root@master01 service ]#kubectl get svc
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   192.168.0.1       <none>        443/TCP   8h
my-nginx     ClusterIP   192.168.148.246   <none>        80/TCP    5s

#在k8s控制节点访问service的ip:端口就可以把请求代理到后端pod

root@master01 service ]#curl 192.168.148.246
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}

通过上面可以看到请求service IP:port跟直接访问pod ip:port看到的结果一样,
这就说明service可以把请求代理到它所关联的后端pod
注意:上面的192.168.148.246:80地址只能是在k8s集群内部可以访问,在外部无法访问,
比方说我们想要通过浏览器访问,那么是访问不通的,如果想要在k8s集群之外访问,
是需要把service type类型改成nodePort的

#查看service详细信息

[root@master01 service ]#kubectl describe svc my-nginx
Name:              my-nginx
Namespace:         default
Labels:            run=my-service
Annotations:       <none>
Selector:          run=my-nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                192.168.148.246
IPs:               192.168.148.246
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         172.21.231.130:80,172.29.55.7:80
Session Affinity:  None
Events:            <none>
[root@master01 service ]#kubectl get endpoints  my-nginx
NAME       ENDPOINTS                          AGE
my-nginx   172.21.231.130:80,172.29.55.7:80   4m14s
[root@master01 service ]#

service可以对外提供统一固定的ip地址,并将请求重定向至集群中的pod。
其中“将请求重定向至集群中的pod”就是通过endpoint与selector协同工作实现。
selector是用于选择pod,由selector选择出来的pod的ip地址和端口号,将会被记录在endpoint中。
endpoint便记录了所有pod的ip地址和端口号。当一个请求访问到service的ip地址时,
就会从endpoint中选择出一个ip地址和端口号,然后将请求重定向至pod中。
具体把请求代理到哪个pod,需要的就是kube-proxy的轮询实现的。
service不会直接到pod,service是直接到endpoint资源,就是地址加端口,再由endpoint再关联到pod。

service只要创建完成,我们就可以直接解析它的服务名,
每一个服务创建完成后都会在集群dns中动态添加一个资源记录,添加完成后我们就可以解析了,资源记录格式是:
SVC_NAME.NS_NAME.DOMAIN.LTD.
服务名.命名空间.域名后缀
集群默认的域名后缀是svc.cluster.local.
就像我们上面创建的my-nginx这个服务,它的完整名称解析就是
my-nginx.default.svc.cluster.local

[root@master01 service ]#kubectl exec -it my-nginx-5684588fff-c5hnr -- /bin/bash
root@my-nginx-5684588fff-c5hnr:/# 
root@my-nginx-5684588fff-c5hnr:/# curl my-nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>

Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort 属性中引用这些名称。
例如,我们可以通过以下方式将 Service 的 targetPort 绑定到 Pod 端口:

apiVersion: v1
kind: Pod
metadata:name: nginxlabels:app.kubernetes.io/name: proxy
spec:containers:- name: nginximage: nginx:stableports:- containerPort: 80name: http-web-svc---
apiVersion: v1
kind: Service
metadata:name: nginx-service
spec:selector:app.kubernetes.io/name: proxyports:- name: name-of-service-portprotocol: TCPport: 80targetPort: http-web-svc

2.创建Service:type类型是NodePort

1、创建一个pod资源

[root@master01 service ]#cat pod_nodeport.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:name: my-nginx-nodeport
spec:selector:matchLabels:run: my-nginx-nodeportreplicas: 2template:metadata:labels:run: my-nginx-nodeportspec:containers:- name: my-nginx-nodeport-containerimage: nginximagePullPolicy: IfNotPresentports:- containerPort: 80

每个控制器创建的pod的标签不要一样,要区分开

2、创建service,代理pod

[root@master01 service ]#cat service_nodeport.yaml 
apiVersion: v1
kind: Service
metadata:name: my-nginx-nodeportlabels:run: my-nginx-nodeport
spec:type: NodePortports:- port: 80protocol: TCPtargetPort: 80nodePort: 30380selector:run: my-nginx-nodeport
[root@master01 service ]#kubectl get svc
NAME                TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)        AGE
kubernetes          ClusterIP   192.168.0.1       <none>        443/TCP        12d
my-nginx            ClusterIP   192.168.148.246   <none>        80/TCP         12d
my-nginx-nodeport   NodePort    192.168.158.194   <none>        80:30380/TCP   13s

这样,在外部通过集群的任意ip:30380 就可以访问pod了,这种方式只能是通过kubeadm安装的集群可以,二进制安装的集群需要通过
node节点的ip:30380才能访问

http://10.10.0.10:30380/

[root@master01 service ]#curl 10.10.0.10:30380
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
服务请求走向:Client-node ip:30380->service ip:80-pod ip:container portClient ->192.168.1.63:30380->10.100.156.7:80->pod ip:80

3.创建Service:type类型是ExternalName

命令行创建service:
kubectl expose

应用场景:跨名称空间访问
需求:default名称空间下的client 服务想要访问nginx-ns名称空间下的nginx-svc服务
正常service只能代理本名称空间下的资源

需要把busybox.tar.gz上传到node02和node01上,手动解压,

[root@node01 ~ ]#docker load -i busybox.tar.gz
[root@node02 ~ ]#docker load -i busybox.tar.gz
[root@master01 service ]#cat client.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:name: client
spec: replicas: 1selector:matchLabels:app: busyboxtemplate:metadata:labels:app: busyboxspec:containers:- name: busyboximage: busyboximagePullPolicy: IfNotPresentcommand: ["/bin/sh","-c","sleep 36000"]
[root@master01 service ]#kubectl apply -f client.yaml 
deployment.apps/client created
[root@master01 service ]#kubectl get pods -owide
NAME                      READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
client-868c657c4f-qt8m5   1/1     Running   0          4s    172.21.231.129   node02   <none>           <none>
[root@master01 service ]#
[root@master01 service ]#cat client_svc.yaml 
apiVersion: v1
kind: Service
metadata:name: client-svc
spec:type: ExternalNameexternalName: nginx-svc.nginx-ns.svc.cluster.localports:- name: httpport: 80targetPort: 80

#该文件中指定了到 nginx-svc 的软链,让使用者感觉就好像调用自己命名空间的服务一样。
访问client-svc.default.svc.cluster.local 就相当于访问 nginx-svc.nginx-ns.svc.cluster.local

[root@master01 service ]#kubectl apply -f client_svc.yaml 
service/client-svc created
[root@master01 service ]#
[root@master01 service ]#
[root@master01 service ]#kubectl get svc
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP                            PORT(S)   AGE
client-svc   ExternalName   <none>        nginx-svc.nginx-ns.svc.cluster.local   80/TCP    4s
kubernetes   ClusterIP      192.168.0.1   <none>                                 443/TCP   43d

创建 nginx-ns名称空间

[root@master01 service ]#kubectl create ns nginx-ns
namespace/nginx-ns created
[root@master01 service ]#cat server_nginx.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:name: nginxnamespace: nginx-ns
spec: replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginximagePullPolicy: IfNotPresent
[root@master01 service ]#kubectl apply -f server_nginx.yaml 
deployment.apps/nginx created
[root@master01 service ]#kubectl get pods -n nginx-ns -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
nginx-787f54657b-2bcf5   1/1     Running   0          72s   172.21.231.130   node02   <none>           <none>
[root@master01 service ]#cat nginx_svc.yaml 
apiVersion: v1
kind: Service
metadata:name: nginx-svcnamespace: nginx-ns
spec:selector:app: nginxports:- name: httpprotocol: TCPport: 80targetPort: 80
[root@master01 service ]#kubectl apply -f nginx_svc.yaml 
service/nginx-svc created
[root@master01 service ]#
[root@master01 service ]#kubectl get svc -n nginx-ns
NAME        TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)   AGE
nginx-svc   ClusterIP   192.168.186.141   <none>        80/TCP    2s

#登录到client pod

[root@master01 service ]#kubectl exec -it client-868c657c4f-qt8m5 -- /bin/sh
/ # wget -q -O - client-svc.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>这是ExternalName service 测试,通过default名称空间的client-svc服务访问过来的/title>
</head>
<body>
</body>
</html>
/ # wget -q -O - nginx-svc.nginx-ns.svc.cluster.local
<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>这是ExternalName service 测试,通过default名称空间的client-svc服务访问过来的/title>
</head>
<body>
</body>
</html>

上面两个请求的结果一样

方便在一个名称空间访问其他名称空间资源。不用加全部域名,只需要访问servicename即可,不用指定名称空间

7.k8s最佳实践:映射外部服务案例分享–service代理–自定义endpoints资源

场景1:k8s集群引用外部的mysql数据库
数据库安装在了物理机上,使用service来代理

在node02上安装mysql数据库:

[root@node02 ~ ]#yum install mariadb-server.x86_64 -y[root@node02 ~ ]#systemctl start mariadb.service[root@node02 ~ ]#mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 5.5.68-MariaDB MariaDB ServerCopyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MariaDB [(none)]> quit
Bye

在控制节点,创建service代理mysql:

[root@master01 service ]#cat mysql_service.yaml 
apiVersion: v1
kind: Service
metadata:name: mysql
spec:type: ClusterIPports:- port: 3306

该service没定义标签选择器,不会创建出endpoints资源出来,不会找到任何pod

[root@master01 service ]#kubectl apply -f mysql_service.yaml 
service/mysql created
[root@master01 service ]#kubectl get svc
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP                            PORT(S)    AGE
client-svc   ExternalName   <none>           nginx-svc.nginx-ns.svc.cluster.local   80/TCP     60m
kubernetes   ClusterIP      192.168.0.1      <none>                                 443/TCP    44d
mysql        ClusterIP      192.168.91.155   <none>                                 3306/TCP   3s
[root@master01 service ]#kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                192.168.91.155
IPs:               192.168.91.155
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         <none>    #还没有endpoint
Session Affinity:  None
Events:            <none>

正常情况下会创建出与service同名的endpoints,由于没有选择器,现在不会创建出来。没关联到任何pod

[root@master01 service ]#kubectl get ep
NAME         ENDPOINTS         AGE
kubernetes   10.10.0.10:6443   44d
[root@master01 service ]#cat mysql_endpoint.yaml 
apiVersion: v1
kind: Endpoints
metadata:name: mysql          #这个名字必须和service的名字一致,不然找不到service
subsets:
- addresses:         - ip: 10.10.0.15     #这个ip需要时运行mysql机器的物理ipports:- port: 3306         #端口要是mysql暴露出的端口
[root@master01 service ]#kubectl apply -f mysql_endpoint.yaml 
endpoints/mysql created
[root@master01 service ]#kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                192.168.91.155
IPs:               192.168.91.155
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         10.10.0.15:3306   此时endpoints有关联到pod了,#这个就是定义的外部数据库
Session Affinity:  None
Events:            <none>

上面配置就是将外部IP地址和服务引入到k8s集群内部,由service作为一个代理来达到能够访问外部服务的目的。

8.Service代理:kube-proxy组件详解

1.kube-proxy组件介绍

Kubernetes service只是把应用对外提供服务的方式做了抽象,真正的应用跑在Pod中的container里,
我们的请求转到kubernetes nodes对应的nodePort上,那么nodePort上的请求是如何进一步转到提供后台服务的Pod的呢?
就是通过kube-proxy实现的:
kube-proxy部署在k8s的每一个Node节点上,是Kubernetes的核心组件,我们创建一个 service 的时候,
kube-proxy 会在iptables中追加一些规则,为我们实现路由与负载均衡的功能。
在k8s1.8之前,kube-proxy默认使用的是iptables模式,通过各个node节点上的iptables规则来实现service的负载均衡,
但是随着service数量的增大,iptables模式由于线性查找匹配、全量更新等特点,其性能会显著下降。
从k8s的1.8版本开始,kube-proxy引入了IPVS模式,IPVS模式与iptables同样基于Netfilter,但是采用的hash表,
因此当service数量达到一定规模时,hash查表的速度优势就会显现出来,从而提高service的服务性能。

service是一组pod的服务抽象,相当于一组pod的LB,负责将请求分发给对应的pod。service会为这个LB提供一个IP,
一般称为cluster IP。kube-proxy的作用主要是负责service的实现,具体来说,
就是实现了内部从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。

2.kube-proxy三种工作模式

1、Userspace方式:

Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,
由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,
并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,
由service iptables将请求转给各个节点中的Server Pod。
这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,
由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。
由于其需要来回在用户空间和内核空间交互通信,因此效率很差。
在Kubernetes 1.1版本之前,userspace是默认的代理模型。

2、iptables方式:
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,
因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。
另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折
iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型。

3、ipvs方式:

Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端
请求时到达内核空间时,根据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:不排队调度

如果某个服务后端pod发生变化,标签选择器适应的pod又多一个,
适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,
而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理

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

3.kube-proxy生成的iptables规则分析

service的type类型是ClusterIp,iptables规则分析

在k8s创建的service,虽然有ip地址,但是service的ip是虚拟的,
不存在物理机上的,是在iptables或者ipvs规则里的。

FQDN:(Fully Qualified Domain Name)全限定域名在k8s中创建service之后,service默认的FQDN是<service name>.<namespace>.svc.cluster.local,那么k8s集群内部的服务就可以通过FQDN访问

9.Service服务发现:coredns组件详解

DNS是什么?
DNS全称是Domain Name System:域名系统,是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解IP地址,
使得互联网的使用者不再需要直接接触很难阅读和理解的IP地址。
域名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,
如果没有了 DNS,那么可能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,
如果我们为 IP 地址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,
那么我们就可以保证集群对外提供的服务能够相对稳定地被其他客户端访问。
DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化的分布式数据库,存储着从域名到 IP 地址的映射。

CoreDNS?
CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,
所以很多开源项目以及工程师都会使用 CoreDNS 为集群提供服务发现的功能,
Kubernetes 就在集群中使用 CoreDNS 解决服务发现的问题。
作为一个加入 CNCF(Cloud Native Computing Foundation)的服务, CoreDNS 的实现非常简单。

验证coredns
#把dig.tar.gz上传到node02和node01机器上,手动解压:

[root@node02 ~]# ctr -n=k8s.io images import  dig.tar.gz 
[root@node01 ~]# ctr -n=k8s.io images import  dig.tar.gz 
[root@master01 ~]# cat dig.yaml 
apiVersion: v1
kind: Pod
metadata:name: dignamespace: default
spec:containers:- name: digimage:  xianchao/dig:latestimagePullPolicy: IfnotPresentcommand:- sleep- "3600"imagePullPolicy: IfNotPresentrestartPolicy: Always

#更新资源清单文件

[root@master01 ~]# kubectl apply -f dig.yaml 
pod/dig configured

#查看默认名称空间的kubernetes服务

[root@master01 ~]# kubectl get svc | grep kubernetes
kubernetes          ClusterIP      10.96.0.1        443/TCP        5d13h

#解析dns,如有以下返回说明dns安装成功

[root@master01 ~]# 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

kubernetes.default.svc.cluster.local
服务名.名称空间.默认后缀

在k8s中创建service之后,service默认的FQDN是<servicename>.<namespace>.svc.cluster.local,
那么k8s集群内部的服务就可以通过FQDN访问

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/13971.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DSPy - prompt 优化

文章目录 一、关于 DSPy与神经网络的类比常见问题解答**DSPy 优化器可以调整什么&#xff1f;****我应该如何使用 DSPy 完成我的任务&#xff1f;****如果我对提示或合成数据生成有更好的想法怎么办&#xff1f;**DSPy 代表什么&#xff1f; 二、安装三、文档A) 教程B) 指南C) …

全球手游4月战报,《Monopoly GO!》荣耀再续!全球手游畅销榜冠军

易采游戏网5月22日消息&#xff0c;在刚刚过去的四月里&#xff0c;全球移动游戏市场的角逐愈发激烈。根据最新发布的数据&#xff0c;Scopely旗下的经典游戏《Monopoly GO!》再次蝉联全球手游畅销榜首冠军宝座&#xff0c;展现了无与伦比的市场魅力与玩家黏度。 4月Scopely《M…

中霖教育怎么样?二建继续教育几年一次?

中霖为大家介绍&#xff1a; 根据相关规定&#xff0c;二级建造师执业资格注册证书设定有效期限为三年。为确保持证人员的专业能力&#xff0c;在规定的期限内需要完成规定的继续教育课程并参加考核&#xff0c;以此来维护其职业资质的连续性。 在执业资格证书的有效期满前&a…

redis小知识

AOF与RDB的区别 AOF (Append Only File) 和 RDB (Redis Database) 都是Redis中的持久化机制&#xff0c;但有以下几点不同之处&#xff1a; 内容格式&#xff1a;AOF 以日志的形式记录所有写操作命令&#xff0c;而 RDB 则是在指定的时间间隔内对数据库进行快照&#xff0c;将数…

WDW-100G 高温拉力试验机 技术方案书

一、整机外观图&#xff1a; 二、项目简介&#xff1a; 微机控制高温拉力试验机是电子技术与机械传动相结合的新型材料试验机&#xff0c;它具有宽广准确的加载速度和测力范围&#xff0c;对载荷、变形、位移的测量和控制有较高的精度和灵敏度&#xff0c;还可以进行等速加载、…

FFmpeg开发笔记(三十)解析H.264码流中的SPS帧和PPS帧

《FFmpeg开发实战&#xff1a;从零基础到短视频上线》一书的“2.1.1 音视频编码的发展历程”介绍了H.26x系列的视频编码标准&#xff0c;其中H.264至今仍在广泛使用&#xff0c;无论视频文件还是网络直播&#xff0c;H.264标准都占据着可观的市场份额。 之所以H.264取得了巨大…

CDLinux下载网站

CDlinux - Browse /CDlinux-ISO at SourceForge.net

用神经网络预测三角形的面积

周末遛狗时&#xff0c;我想起一个老问题&#xff1a;神经网络能预测三角形的面积吗&#xff1f; 神经网络非常擅长分类&#xff0c;例如根据花瓣长度和宽度以及萼片长度和宽度预测鸢尾花的种类&#xff08;setosa、versicolor 或 virginica&#xff09;。神经网络还擅长一些回…

2024中青杯A题数学建模成品文章数据代码分享

人工智能视域下养老辅助系统的构建 摘要 随着全球人口老龄化的加剧&#xff0c;养老问题已经成为一个世界性的社会问题&#xff0c;对社会各个方面产生了深远影响&#xff0c;包括劳动力市场、医疗保健和养老金制度等。人口结构变化对养老服务的质量和覆盖面提出了更高要求。特…

ARP基本原理

相关概念 ARP报文 ARP报文分为ARP请求报文和ARP应答报文&#xff0c;报文格式如图1所示。 图1 ARP报文格式 Ethernet Address of destination&#xff08;0–31&#xff09;和Ethernet Address of destination&#xff08;32–47&#xff09;分别表示Ethernet Address of dest…

【算法】前缀和——除自身以外数组的乘积

本节博客是用前缀和算法求解“除自身以外数组的乘积”&#xff0c;有需要借鉴即可。 目录 1.题目2.前缀和算法3.变量求解4.总结 1.题目 题目链接&#xff1a;LINK 2.前缀和算法 1.创建两个数组 第一个数组第i位置表示原数组[0,i-1]之积第二个数组第i位置表示原数组[i1,n-1]…

Hadoop 客户端 FileSystem加载过程

如何使用hadoop客户端 public class testCreate {public static void main(String[] args) throws IOException {System.setProperty("HADOOP_USER_NAME", "hdfs");String pathStr "/home/hdp/shanshajia";Path path new Path(pathStr);Confi…

在DAYU200上实现OpenHarmony跳转拨号界面

一、简介 日常生活中&#xff0c;打电话是最常见的交流方式之一&#xff0c;那么如何在OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;中进行电话服务相关的开发呢&#xff1f;今天我们可以一起来了解一下如何通过电话服务系统支持的API实现拨打电话的功能…

C#-根据日志等级进行日志的过滤输出

文章速览 概要具体实施创建Log系统动态修改日志等级 坚持记录实属不易&#xff0c;希望友善多金的码友能够随手点一个赞。 共同创建氛围更加良好的开发者社区&#xff01; 谢谢~ 概要 方便后期对软件进行维护&#xff0c;需要在一些关键处添加log日志输出&#xff0c;但时间长…

【408精华知识】指令周期的数据流

文章目录 一、取指周期二、间址周期三、执行周期&#xff08;一&#xff09;数据传送类指令(mov/load/store)&#xff08;二&#xff09;运算类指令(加/减/乘/除/移位/与/或)&#xff08;三&#xff09;转移类指令(jmp/jxxx) 四、中断周期 CPU每取出并且执行一条指令所需要的全…

二叉数之插入操作

首先是题目 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 value &#xff0c;将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 &#xff0c;新值和原始二叉搜索树中的任意节点值都不同。 注意&#xff0c;可能存在多种有效…

AcWing 217:绿豆蛙的归宿 ← 搜索算法

【题目来源】https://www.acwing.com/problem/content/219/【题目描述】 给出一个有向无环的连通图&#xff0c;起点为 1&#xff0c;终点为 N&#xff0c;每条边都有一个长度。 数据保证从起点出发能够到达图中所有的点&#xff0c;图中所有的点也都能够到达终点。 绿豆蛙从起…

Kreon: An Efficient Memory-Mapped Key-Value Store for Flash Storage——论文泛读

TOS 2021 Paper 论文阅读笔记整理 问题 持久的键值存储已经成为现代数据处理系统的数据访问路径中的一个主要组件。然而&#xff0c;它们表现出较高的CPU和I/O开销。 基于LSM Tree的键值存储中两个重要的CPU和I/O开销来源是&#xff1a; 在LSM Tree中使用压缩&#xff0c;不…

若依解决使用https上传文件返回http路径问题

若依通过HTTPS请求进行文件上传时却返回HTTP的文件链接地址&#xff0c;主要原因是使用了 request.getRequestURL 获取链接地址。 解决办法&#xff1a; 在nginx配置文件location处加上&#xff1a;proxy_set_header X-Forwarded-Scheme $scheme; 然后代码通过request.getHea…

MobaXterm下载虚拟机SSH链接超时解决(保姆级踩坑)

文章目录 为啥要用MobaXtermMobaXterm下载打开虚拟机ssh链接ssh连接失败排查linux配置windows配置 到这了&#xff0c;什么都干了&#xff0c;怎么还不成功&#xff1f; 更多相关内容可查看 在一个阳光明媚的下午&#xff0c;开启了无限踩坑的旅程 为啥要用MobaXterm 作为小编…