K8s网络管理 flannel

news2024/11/24 5:31:20

K8s网络管理 flannel

  • 1 网络管理
    • 1.1 Service
      • 1.1.1 网络体系
      • 1.1.2 工作模型
      • 1.1.3 SVC实践
      • 1.1.4 IPVS实践
    • 1.2 其他资源
      • 1.2.1 域名服务
      • 1.2.2 CoreDNS
      • 1.2.3 无头服务
  • 2 容器网络
    • 2.1 网络方案
    • 2.2 flannel
    • 2.3 主机网络

1 网络管理

1.1 Service

1.1.1 网络体系

应用流程

资源对象体系
在这里插入图片描述

通过对Pod及其管理资源RC和Deployment的实践,我们知道,我们所有的应用服务都是工作在pod资源中,由于每个Pod都有独立的ip地址,大量的动态创建和销毁操作,虽然pod资源的数量是控制住了,但是由于pod重新启动,导致他的IP可能发生了变化,假设我们这里有前段应用的pod和后端应用的pod,那么再剧烈变动的场景中,两个应用该如何自由通信呢?难道是让我们以类似nginx负载均衡的方式手工定制pod ip然后进行一一管理么?但是这是做不到的。

Kubernetes集群就为我们提供了这样的一个对象–Service,它定义了一组Pod的逻辑集合和一个用于访问它们的策略,它可以基于标签的方式自动找到对应的pod应用,而无需关心pod的ip地址变化与否,从而实现了类似负载均衡的效果.
这个资源在master端的Controller组件中,由Service Controller 来进行统一管理。

service

service是Kubernetes里最核心的资源对象之一,每一个Service都是一个完整的业务服务,我们之前学到的Pod、RC、Deployment等资源对象都是为Service服务的。他们之间的关系如下图:

在这里插入图片描述

Kubernetes 的 Service定义了一个服务的访问入口地址,前端的应用Pod通过Service访问其背后一组有Pod副本组成的集群示例,Service通过Label Selector访问指定的后端Pod,RC保证Service的服务能力和服务质量处于预期状态。

Service是Kubernetes中最高一级的抽象资源对象,每个Service提供一个独立的服务,集群Service彼此间使用TCP/IP进行通信,将不同的服务组合在一起运行起来,就行了我们所谓的"系统",效果如下图

在这里插入图片描述

Pod入口

我们知道每个Pod都有一个专用的IP地址,加上Pod内部容器的Port端口,就组成了一个访问Pod专用的EndPoint(Pod IP+Container Port),从而实现了用户外部资源访问Pod内部应用的效果。这个EndPoint资源在master端的Controller组件中,由EndPoint Controller 来进行统一管理。

kube-proxy

Pod是工作在不同的Node节点上,而Node节点上有一个kube-proxy组件,它本身就是一个软件负载均衡器,在内部有一套专有的负载均衡与会话保持机制,可以达到,接收到所有对Service请求,进而转发到后端的某个具体的Pod实例上,相应该请求。
– kube-proxy 其实就是 Service Controller位于各节点上的agent。

service表现

Kubernetes给Service分配一个全局唯一的虚拟ip地址–cluster IP,它不存在任何网络设备上,Service通过内容的标签选择器,指定向应该Service的Pod资源,请求发给cluster IP,后端的Pod资源收到请求后,就会响应请求。

这种情况下,每个Service都有一个全局唯一通信地址,整个系统的内部服务间调用就变成了最基础的TCP/IP网络通信问题。如果我们的集群内部的服务想要和外部的网络进行通信,方法很多,比如:
NodePort类型,通过在所有结点上增加一个对外的端口,用于接入集群外部请求
ingress类型,通过集群附加服务功能,将外部的域名流量转交到集群内部。

service 和 endpoint

1 当创建 Service资源的时候,最重要的就是为Service指定能够提供服务的标签选择器,
2 Service Controller就会根据标签选择器创建一个同名的Endpoint资源对象。
3 Endpoint Controller开始介入,使用Endpoint的标签选择器(继承自Service标签选择器),筛选符合条件的pod资源
4 Endpoint Controller 将符合要求的pod资源绑定到 Endpoint上,并告知给Service资源,谁可以正常提供服务。
5 Service 根据自身的cluster IP向外提供由Endpoint提供的服务资源。

– 所以Service 其实就是为动态的一组pod资源对象 提供一个固定的访问入口。

1.1.2 工作模型

Service对象,对于当前集群的节点来说,本质上就是工作节点的一些iptables或ipvs规则,这些规则由kube-proxy进行实时维护,站在kubernetes的发展脉络上来说,kube-proxy将请求代理至相应端点的方式有三种:userspace/iptables/ipvs。目前我们主要用的是 iptables/ipvs 两种。

在这里插入图片描述

userspace模型是k8s(1.1-1.2)最早的一种工作模型,作用就是将service的策略转换成iptables规则,这些规则仅仅做请求的拦截,而不对请求进行调度处理。
该模型中,请求流量到达内核空间后,由套接字送往用户空间的kube-proxy,再由它送回内核空间,并调度至后端Pod。因为涉及到来回转发,效率不高,另外用户空间的转发,默认开启了会话粘滞,会导致流量转发给无效的pod上。

iptables模式是k8s(1.2-至今)默认的一种模式,作用是将service的策略转换成iptables规则,不仅仅包括拦截,还包括调度,捕获到达ClusterIP和Port的流量,并重定向至当前Service的代理的后端Pod资源。性能比userspace更加高效和可靠
缺点
不会在后端Pod无响应时自动重定向,而userspace可以
中量级k8s集群(service有几百个)能够承受,但是大量级k8s集群(service有几千个)维护达几万条规则,难度较大

ipvs是自1.8版本引入,1.11版本起为默认设置,通过内核的Netlink接口创建相应的ipvs规则.
请求流量的转发和调度功能由ipvs实现,余下的其他功能仍由iptables完成。ipvs流量转发速度快,规则同步性能好,且支持众多调度算法,如rr/lc/dh/sh/sed/nq等。

注意:
对于我们kubeadm方式安装k8s集群来说,他会首先检测当前主机上是否已经包含了ipvs模块,如果加载了,就直接用ipvs模式,如果没有加载ipvs模块的话,会自动使用iptables模式。

service类型

对于k8s来说,内部服务的自由通信可以满足我们环境的稳定运行,但是我们作为一个平台,其核心功能还是将平台内部的服务发布到外部环境,那么在k8s环境平台上,Service主要有四种样式来满足我们的需求,种类如下:

ClusterIP
这是service默认的服务暴露模式,主要针对的对象是集群内部。

NodePort
在ClusterIP的基础上,以:方式对外提供服务,默认端口范围沿用Docker初期的随机端口范围 30000~32767,但是NodePort设定的时候,会在集群所有节点上实现相同的端口。

LoadBalancer
基于NodePort之上,使用运营商负载均衡器方式实现对外提供服务底层是基于IaaS云创建一个k8s云,同时该平台也支持LBaaS产品服务。

ExternalName
当前k8s集群依赖集群外部的服务,那么通过externalName将外部主机引入到k8s集群内部,外部主机名以 DNS方式解析为一个 CNAME记录给k8s集群的其他主机来使用这种Service既不会有ClusterIP,也不会有NodePort.而且依赖于内部的CoreDNS功能

1.1.3 SVC实践

apiVersion: v1
kind: Service
metadata:
  name: …
  namespace: …
  labels:
    key1: value1
    key2: value2
spec:
  type <string>   					# Service类型,默认为ClusterIP
  selector <map[string]string>  	# 等值类型的标签选择器,内含“与”逻辑
  ports:  						    # Service的端口对象列表
  - name <string>  					# 端口名称
    protocol <string>  				# 协议,目前仅支持TCP、UDP和SCTP,默认为TCP
    port <integer>  				# Service的端口号
    targetPort  <string>  			# 后端目标进程的端口号或名称,名称需由Pod规范定义
    nodePort <integer>  			# 节点端口号,仅适用于NodePort和LoadBalancer类型
  clusterIP  <string>  				# Service的集群IP,建议由系统自动分配
  externalTrafficPolicy  <string> 	# 外部流量策略处理方式,Local表示由当前节点处理,Cluster表示向集群范围调度
  loadBalancerIP  <string>  		# 外部负载均衡器使用的IP地址,仅适用于LoadBlancer
  externalName <string>  			# 外部服务名称,该名称将作为Service的DNS CNAME值
创建一个应用
[root@k8s-master ~]# kubectl create deployment nginx --image=kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1

创建多种类型svc
[root@k8s-master ~]# kubectl expose deployment nginx --port=80
[root@k8s-master ~]# kubectl expose deployment nginx --name=svc-default --port=80
[root@k8s-master ~]# kubectl expose deployment nginx --name=svc-nodeport --port=80 --type=NodePort
[root@k8s-master ~]# kubectl expose deployment nginx --name=svc-loadblancer --port=80 --type=LoadBalancer

查看效果
[root@k8s-master1 ~]# kubectl get svc
NAME             TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
nginx            ClusterIP      10.99.127.240     <none>        80/TCP           4m31s
svc-default      ClusterIP      10.103.14.139    <none>        80/TCP           3m2s
svc-loadblancer  LoadBalancer   10.104.22.177    <pending>     80:30778/TCP     2m44s
svc-nodeport     NodePort       10.100.144.140   <none>        80:32335/TCP     2m54s

资源对象方式

查看对象标签
[root@k8s-master1 ~]# kubectl get pod --show-labels
NAME                                      READY   STATUS        RESTARTS   AGE     LABELS
nginx-f44f65dc-pbht6                      1/1     Running       0          8h      app=nginx,pod-template-hash=f44f65dc
定制资源清单文件
[root@k8s-master1 ~]# mkdir /data/kubernetes/service -p ; cd /data/kubernetes/service
[root@k8s-master1 /data/kubernetes/service]# vim 01_k8s-service_test.yml
apiVersion: v1
kind: Service
metadata:
  name: test-nginx-service
spec:
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    
应用资源清单文件
[root@k8s-master1 /data/kubernetes/service]# kubectl  apply -f 01_k8s-service_test.yml
service/test-nginx-service created
查看service效果
[root@k8s-master1 /data/kubernetes/service]# kubectl  describe svc test-nginx-service
Name:              test-nginx-service
...
IP:                10.109.240.56
IPs:               10.109.240.56
Port:              http  80/TCP
TargetPort:        8000/TCP
Endpoints:         10.244.3.123:8000
...

访问service
[root@k8s-master1 /data/kubernetes/service]# curl -s  10.109.240.56 -I | head -n1
HTTP/1.1 200 OK

NodePort实践

NodePort会在所有的节点主机上,暴露一个指定或者随机的端口,供外部的服务能够正常的访问pod内部的资源。

[root@k8s-master1 /data/kubernetes/service]# vim 02_k8s-service_nodePort.yml
apiVersion: v1
kind: Service
metadata:
  name: test-nginx-nodeport
spec:
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    nodePort: 30080
    
应用资源清单文件
[root@k8s-master1 /data/kubernetes/service]# kubectl  apply -f 02_k8s-service_nodePort.yml
service/test-nginx-nodeport created
检查效果
[root@k8s-master1 /data/kubernetes/service]# kubectl  describe  svc test-nginx-nodeport
Name:                     test-nginx-nodeport
...
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.102.1.177
IPs:                      10.102.1.177
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  30080/TCP
...

访问效果
[root@k8s-master1 /data/kubernetes/service]# curl 10.120.120.12:30080             Hello Nginx, nginx-6944855df5-8zjdn-1.23.0

1.1.4 IPVS实践

ipvs会在每个节点上创建一个名为kube-ipvs0的虚拟接口,并将集群所有Service对象的ClusterIP和ExternalIP都配置在该接口;
- 所以每增加一个ClusterIP 或者 EternalIP,就相当于为 kube-ipvs0 关联了一个地址罢了。
kube-proxy为每个service生成一个虚拟服务器( IPVS Virtual Server)的定义。

基本流程
当前节点接收到外部流量后,如果该数据包是交给当前节点上的clusterIP,则会直接将数据包交给kube-ipvs0,而这个接口是内核虚拟出来的,而kube-proxy定义的VS直接关联到kube-ipvs0上。
如果是本地节点pod发送的请求,基本上属于本地通信,效率是非常高的。
默认情况下,这里的ipvs使用的是nat转发模型,而且支持更多的后端调度算法。仅仅在涉及到源地址转换的场景中,会涉及到极少量的iptables规则(应该不会超过20条)

前提:当前操作系统需要提前加载ipvs模块
yum install ipvsadm -y

kube-proxy

对于k8s来说,默认情况下,支持的规则是 iptables,我们可以通过多种方式对我们的代理模式进行更改,因为这些规则都是基于kube-proxy来定制的,所以,我们如果要更改代理模式的话,就需要调整kube-proxy的属性。

在k8s集群中,关于kube-proxy的所有属性信息,我们可以通过一个 configmap 的资源对象来了解一下

[root@k8s-master1 ~]# kubectl describe configmap kube-proxy -n kube-system
Name:         kube-proxy
Namespace:    kube-system
Labels:       app=kube-proxy
...
iptables:
  masqueradeAll: false				# 这个属性打开的话,会对所有的请求都进行源地址转换
  ...
ipvs:
  excludeCIDRs: null
  minSyncPeriod: 0s
  scheduler: ""						# 调度算法,默认是randomrobin
  ...
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: ""							# 默认没有指定,就是使用 iptables 规则

查看默认模式

通过kube-proxy-b8dpc的pod日志查看模式
[root@k9s-master1 /data/kubernetes/service]# kubectl logs kube-proxy-b8dpc -n kube-system
...
I0719 08:39:44.410078       1 server_others.go:561] "Unknown proxy mode, assuming iptables proxy" proxyMode=""
I0719 08:39:44.462438       1 server_others.go:206] "Using iptables Proxier"
...
清理所有svc
[root@k8s-master1 /data/kubernetes/service]# for i in $(kubectl  get svc | egrep -v 'NAME|kubernetes' | awk '{print $1}')
> do
> kubectl delete svc $i
> done

修改kube-proxy模式

我们在测试环境中,临时修改一下configmap中proxy的基本属性 - 临时环境推荐
[root@k8s-master1 /data/kubernetes/service]# kubectl edit configmap kube-proxy -n kube-system
	...
    mode: "ipvs"
    ...
    
重启所有的kube-proxy pod对象 
[root@k8s-master1 /data/kubernetes/service]# kubectl delete pod -n kube-system -l k8s-app=kube-proxy
通过kube-proxy的pod日志查看模式
[root@k8s-master1 /data/kubernetes/service]# kubectl logs kube-proxy-fb9pz -n kube-system
...
I0721 10:31:40.408116       1 server_others.go:269] "Using ipvs Proxier"
I0721 10:31:40.408155       1 server_others.go:271] "Creating dualStackProxier for ipvs"
...
安装参考命令
[root@k8s-master1 /data/kubernetes/service]# yum install ipvsadm -y

查看规则效果
[root@k8s-master1 /data/kubernetes/service]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.1:30443 rr
  -> 10.244.3.2:8443              Masq    1      0          0
  ...
创建一个service
[root@k8s-master1 /data/kubernetes/service]# kubectl  apply -f 02_k8s-service_nodePort.yml
service/test-nginx-nodeport created

查看svc的ip
[root@k8s-master1 /data/kubernetes/service]# kubectl  get svc test-nginx-nodeport -o wide
NAME                         TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE   SELECTOR
test-nginx-nodeport   NodePort   10.106.138.242   <none>        80:30080/TCP   23s   app=nginx

查看ipvsadm规则
[root@k8s-master1 /data/kubernetes/service]# ipvsadm -Ln | grep -A1 10.106.138.242
TCP  10.106.138.242:80 rr
  -> 10.244.3.64:80               Masq    1      0          0
  
查看防火墙规则
[root@k8s-master1 /data/kubernetes/service]# iptables -t nat -S KUBE-NODE-PORT
-N KUBE-NODE-PORT
-A KUBE-NODE-PORT -p tcp -m comment --comment "Kubernetes nodeport TCP port for masquerade purpose" -m set --match-set KUBE-NODE-PORT-TCP dst -j KUBE-MARK-MASQ
结果显示:
	没有生成对应的防火墙规则

1.2 其他资源

1.2.1 域名服务

简介

在传统的系统部署中,服务运行在一个固定的已知的 IP 和端口上,如果一个服务需要调用另外一个服务,可以通过地址直接调用,但是,在虚拟化或容器话的环境中,以k8s集群为例,如果存在个位数个service我们可以很快的找到对应的clusterip地址,进而找到指定的资源,虽然ip地址不容易记住,因为service在创建的时候会为每个clusterip分配一个名称,我们同样可以根据这个名称找到对应的服务。但是,如果我们的集群中有1000个Service,我们如何找到指定的service呢?

虽然我们可以借助于传统的DNS机制来实现,但是在k8s集群中,服务实例的启动和销毁是很频繁的,服务地址在动态的变化,所以传统的方式配置DNS解析记录就不太好实现了。所以针对于这种场景,我们如果需要将请求发送到动态变化的服务实例上,可以通过一下两个步骤来实现:
服务注册 — 创建服务实例后,主动将当前服务实例的信息,存储到一个集中式的服务管理中心。
服务发现 — 当A服务需要找未知的B服务时,先去服务管理中心查找B服务地址,然后根据该地址找到B服务

DNS方案

专用于kubernetes集群中的服务注册和发现的解决方案就是KubeDNS。kubeDNS自从k8s诞生以来,其方案的具体实现样式前后经历了三代,分别是 SkyDNS、KubeDNS、CoreDNS(目前默认的)。

Kubelet会为创建的每一个容器于/etc/resolv.conf配置文件中生成DNS查询客户端依赖到的必要配置,相关的配置信息源自于kubelet的配置参数,容器的DNS服务器由clusterDNS参数的值设定,它的取值为kube-system名称空间中的Service对象kube-dns的ClusterIP,默认为10.96.0.10.
DNS搜索域的值由clusterDomain参数的值设定,若部署Kubernetes集群时未特别指定,其值将为cluster.local、svc.cluster.local和NAMESPACENAME.svc.cluster.local

kubeadm 1.23.8 环境初始化配置文件中与dns相关的检索信息
[root@k8s-master1 ~]# grep -A1 networking /data/kubernetes/cluster_init/kubeadm_init_1.23.8.yml
networking:
  dnsDomain: cluster.local

资源对象的dns记录

对于kubernetes的内部资源对象来说,为了更好的绕过变化频率更高的ip地址的限制,它可以在内部以dns记录的方式进行对象发现,dns记录具有标准的名称格式:
资源对象名.命名空间名.svc.cluster.local

域名测试

创建一个svc记录

[root@k8s-master1 /data/kubernetes/service]# kubectl  apply -f 01_k8s-service_test.yml
service/test-nginx-service created
[root@k8s-master1 /data/kubernetes/service]# kubectl  get svc test-nginx-service -o wide             NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
test-nginx-service   ClusterIP   10.97.135.126   <none>        80/TCP    23s   app=nginx

资源对象的名称记录

查看本地的pod效果
[root@k8s-master1 ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6944855df5-8zjdn   1/1     Running   0          38m

查看pod内部的resolv.conf文件
[root@k8s-master1 ~]# kubectl  exec -it nginx-6944855df5-8zjdn -- cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local localhost
options ndots:5
可以看到:
	资源对象的查看dns的后缀主要有四种:
		default.svc.cluster.local
		svc.cluster.local
		cluster.local
		localhost
查看内部的资源对象完整域名
[root@k8s-master1 /data/kubernetes/service]# kubectl  exec -it nginx-6944855df5-8zjdn -- /bin/bash
root@nginx-6944855df5-8zjdn:/# curl
curl: try 'curl --help' or 'curl --manual' for more information
root@nginx-6944855df5-8zjdn:/# curl test-nginx-service
Hello Nginx, nginx-6944855df5-8zjdn-1.23.0
root@nginx-6944855df5-8zjdn:/# curl test-nginx-service.default.svc.cluster.local
Hello Nginx, nginx-6944855df5-8zjdn-1.23.0
root@nginx-6944855df5-8zjdn:/# curl test-nginx-service.default.svc.cluster.local.
Hello Nginx, nginx-6944855df5-8zjdn-1.23.0

内部dns测试效果

安装dns测试工具
root@nginx-6944855df5-8zjdn:/# apt update
root@nginx-6944855df5-8zjdn:/# apt install dnsutils -y
资源对象的查看效果
root@nginx-6944855df5-8zjdn:/# nslookup test-nginx-service
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   test-nginx-service.default.svc.cluster.local
Address: 10.97.135.126

root@nginx-6944855df5-8zjdn:/# nslookup 10.97.135.126
126.135.97.10.in-addr.arpa      name = test-nginx-service.default.svc.cluster.local.

root@nginx-6944855df5-8zjdn:/# nslookup kubernetes
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   kubernetes.default.svc.cluster.local
Address: 10.96.0.1
查看跨命名空间的资源对象
[root@k8s-master1 ~]# kubectl  get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   2d2h

回到pod终端查看效果
root@nginx-6944855df5-8zjdn:/# nslookup kube-dns.kube-system.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   kube-dns.kube-system.svc.cluster.local
Address: 10.96.0.10

root@nginx-6944855df5-8zjdn:/# nslookup 10.96.0.10
10.0.96.10.in-addr.arpa name = kube-dns.kube-system.svc.cluster.local.

root@nginx-6944855df5-8zjdn:/# nslookup kube-dns
Server:         10.96.0.10
Address:        10.96.0.10#53

** server can't find kube-dns: NXDOMAIN
结果显示:
	对于跨命名空间的资源对象必须使用完整的名称格式

pod资源对象解析

查看pod资源对象
[root@k8s-master1 ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            ...
nginx-6944855df5-8zjdn   1/1     Running   0          56m   10.244.3.64   ...

构造资源对象名,pod的ip名称转换
root@nginx-6944855df5-8zjdn:/# nslookup 10-244-3-64.test-nginx-service.default.svc.cluster.local.           Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   10-244-3-64.test-nginx-service.default.svc.cluster.local
Address: 10.244.3.64

1.2.2 CoreDNS

基础知识

简介

coredns是一个用go语言编写的开源的DNS服务,coredns是首批加入CNCF组织的云原生开源项目,并且作为已经在CNCF毕业的项目,coredns还是目前kubernetes中默认的dns服务。同时,由于coredns可以集成插件,它还能够实现服务发现的功能。

coredns和其他的诸如bind、knot、powerdns、unbound等DNS服务不同的是:coredns非常的灵活,并且几乎把所有的核心功能实现都外包给了插件。如果你想要在coredns中加入Prometheus的监控支持,那么只需要安装对应的prometheus插件并且启用即可。


**配置解析**

```powershell
coredns的配置依然是存放在 configmap中
[root@k8s-master1 ~]# kubectl get cm coredns -n kube-system
NAME      DATA   AGE
coredns   1      2d2h
查看配置详情
[root@k8s-master1 ~]# kubectl describe cm coredns -n kube-system
Name:         coredns
Namespace:    kube-system
Labels:       <none>
Annotations:  <none>

Data
====
Corefile:
----
.:53 {
    errors
    health {											# 健康检测
       lameduck 5s
    }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {	# 解析配置
       pods insecure
       fallthrough in-addr.arpa ip6.arpa
       ttl 30
    }
    prometheus :9153
    forward . /etc/resolv.conf {						# 转发配置	
       max_concurrent 1000
    }
    cache 30
    loop
    reload												# 自动加载
    loadbalance
}

BinaryData
====
Events:  <none>
其他属性和示例:
	except 	domain					排除的域名

添加dns解析
    hosts {
        192.168.8.100 www.example.com
        fallthrough			# 在CoreDNS里面表示如果自己无法处理,则交由下个插件处理。
    }
修改配置文件
[root@k8s-master1 ~]# kubectl edit cm coredns -n kube-system
...
        forward . /etc/resolv.conf {
           max_concurrent 1000
           except www.baidu.com.
        }
        hosts {
           10.0.0.20 harbor.test.com
           fallthrough
        }
        ...
        注意:
        	多个dns地址间用空格隔开
        	排除的域名最好在末尾添加 “.”,对于之前的旧版本来说可能会出现无法保存的现象

测试效果

同步dns的配置信息
[root@k8s-master1 ~]# kubectl delete pod -l k8s-app=kube-dns -n kube-system
pod "coredns-5d555c984-fmrht" deleted
pod "coredns-5d555c984-scvjh" deleted
 
删除旧pod,使用新pod测试 (可以忽略)
pod中测试效果
[root@k8s-master1 /data/kubernetes/service]# kubectl  exec -it nginx-6944855df5-9s4bd -- /bin/bash
root@nginx-6944855df5-9s4bd:/# apt update ; apt install dnsutils -y

测试效果
root@nginx-6944855df5-9s4bd:/# nslookup www.baidu.com
Server:         10.96.0.10
Address:        10.96.0.10#53

** server can't find www.baidu.com: SERVFAIL

root@nginx-6944855df5-9s4bd:/# nslookup harbor.test.com
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   harbor.test.com
Address: 10.0.0.20

1.2.3 无头服务

简介

在kubernetes中,有一种特殊的无头service,它本身是Service,但是没有ClusterIP,这种svc在一些有状态的场景中非常重要。

无头服务场景下,k8s会将一个集群内部的所有成员提供唯一的DNS域名来作为每个成员的网络标识,集群内部成员之间使用域名通信,这个时候,就特别依赖service的selector属性配置了。
无头服务管理的域名是如下的格式:$(service_name).$(k8s_namespace).svc.cluster.local

pod资源对象的解析记录

dns解析记录
	A记录
    	<a>-<b>-<c>-<d>.<service>.<ns>.svc.<zone>  A  PodIP
关键点:
      svc_name的解析结果从常规Service的ClusterIP,转为各个Pod的IP地址;
      反解,则从常规的clusterip解析为service name,转为从podip到hostname, <a>-<b>-<c>-<d>.<service>.<ns>.svc.<zone>
      <hostname>指的是a-b-c-d格式,而非Pod自己的主机名; 
扩展pod对象数量
[root@k8s-master1 ~]# kubectl scale deployment nginx --replicas=3
deployment.apps/nginx scaled
[root@k8s-master1 ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            ...
nginx-6944855df5-9m44b   1/1     Running   0          5s    10.244.2.4   ...
nginx-6944855df5-9s4bd   1/1     Running   0          14m   10.244.3.2   kubernetes-node3   <none>           <none>
nginx-6944855df5-mswl4   1/1     Running   0          5s    10.244.1.3   kubernetes-node2   <none>           <none>

定制无头服务

手工定制无头服务
[root@k8s-master1 ~]# kubectl create service clusterip service-headless --clusterip="None"
service/service-headless-cmd created

查看无头服务
[root@k8s-master1 ~]# kubectl get svc service-headless
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service-headless       ClusterIP   None         <none>        <none>    6s
资源清单文件定制无头服务
[root@k8s-master1 /data/kubernetes/service]# cat 03_k8s-service_headless.yml
apiVersion: v1
kind: Service
metadata:
  name: test-nginx-headless
spec:
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
  clusterIP: "None"
  
应用资源对象
[root@k8s-master1 /data/kubernetes/service]# kubectl  apply -f 03_k8s-service_headless.yml
service/test-nginx-headless created
[root@k8s-master1 /data/kubernetes/service]# kubectl  get svc
NAME                         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes                   ClusterIP   10.96.0.1    <none>        443/TCP   6m33s
service-headless             ClusterIP   None         <none>        <none>    61s
test-nginx-headless          ClusterIP   None         <none>        80/TCP    7s
进入测试容器
[root@k8s-master1 /data/kubernetes/service]# kubectl exec -it nginx-fd669dcb-h5d9d -- /bin/bash
root@nginx-fd669dcb-h5d9d:/# nslookup test-nginx-headless
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   test-nginx-headless.default.svc.cluster.local
Address: 10.244.2.4
Name:   test-nginx-headless.default.svc.cluster.local
Address: 10.244.1.3
Name:   test-nginx-headless.default.svc.cluster.local
Address: 10.244.3.2
结果显式:
	由于我们没有对域名做定向解析,那么找请求的时候,就像无头苍蝇似的,到处乱串

2 容器网络

2.1 网络方案

容器实现网络访问样式

在这里插入图片描述

  1. 虚拟网桥: 本质上NAT 效率低下
    brdige,用纯软件的方式实现一个虚拟网络,用一个虚拟网卡接入到我们虚拟网桥上去。这样就能保证每一个容器和每一个pod都能有一个专用的网络接口,从而实现每一主机组件有网络接口。

  2. 多路复用: 内核级别的vlan模块
    MacVLAN,借助于linux内核级的VLAN模块,在同一个物理网卡上配置多个 MAC 地址,每个 MAC 给一个Pod使用,然后借助物理网卡中的MacVLAN机制进行跨节点之间进行通信了。

  3. 硬件交换:
    很多网卡都已经支持"单根IOV的虚拟化"了,借助于单根IOV(SR-IOV)的方式,直接在物理主机虚拟出多个接口来,通信性能很高,然后每个虚拟网卡能够分配给容器使用。

容器网络方案

任何一种能够让容器自由通信的网络解决方案,必须包含三个功能:
1 构建一个网络
2 将容器接入到这个网络中
3 实时维护所有节点上的路由信息,实现容器的通信

CNI方案

  1. 所有节点的内核都启用了VXLAN的功能模块
    每个节点都启动一个cni网卡,并维护所有节点所在的网段的路由列表
  2. node上的pod发出请求到达cni0
    根据内核的路由列表判断对端网段的节点位置
    经由 隧道设备 对数据包进行封装标识,对端节点的隧道设备解封标识数据包,
    当前数据包一看当前节点的路由表发现有自身的ip地址,这直接交给本地的pod
  3. 多个节点上的路由表信息维护,就是各种网络解决方案的工作位置

注意:
我们可以部署多个网络解决方案,但是对于CNI来说,只会有一个生效。如果网络信息过多会导致冲突

在这里插入图片描述

CNI插件

根据我们刚才对pod通信的回顾,多节点内的pod通信,k8s是通过CNI接口来实现网络通信的。CNI基本思想:创建容器时,先创建好网络名称空间,然后调用CNI插件配置这个网络,而后启动容器内的进程

CNI插件类别:main、meta、ipam
main,实现某种特定的网络功能,如loopback、bridge、macvlan、ipvlan
meta,自身不提供任何网络实现,而是调用其他插件,如flannel
ipam,仅用于分配IP地址,不提供网络实现

常见方案

Flannel
提供叠加网络,基于linux TUN/TAP,使用UDP封装IP报文来创建叠加网络,并借助etcd维护网络分配情况

Calico
基于BGP的三层网络,支持网络策略实现网络的访问控制。在每台机器上运行一个vRouter,利用内核转发数据包,并借助iptables实现防火墙等功能

kube-router
K8s网络一体化解决方案,可取代kube-proxy实现基于ipvs的Service,支持网络策略、完美兼容BGP的高级特性

其他网络解决方案:
Canal: 由Flannel和Calico联合发布的一个统一网络插件,支持网络策略
Weave Net: 多主机容器的网络方案,支持去中心化的控制平面
Contiv:思科方案,直接提供多租户网络,支持L2(VLAN)、L3(BGP)、Overlay(VXLAN)

更多的解决方案,大家可以参考:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

2.2 flannel

信息查看

查看网络配置

[root@k8s-master1 ~]# cat /etc/kubernetes/manifests/kube-controller-manager.yaml
apiVersion: v1
...
spec:
  containers:
  - command:
    - kube-controller-manager
    - --allocate-node-cidrs=true		
    ...
    - --cluster-cidr=10.244.0.0/16
 配置解析:
 	allocate-node-cidrs属性表示,每增加一个新的节点,都从cluster-cidr子网中切分一个新的子网网段分配给对应的节点上。
    这些相关的网络状态属性信息,会经过 kube-apiserver 存储到etcd中。

CNI配置

使用CNI插件编排网络,Pod初始化或删除时,kubelet会调用默认CNI插件,创建虚拟设备接口附加到相关的底层网络,设置IP、路由并映射到Pod对象网络名称空间.
kubelet在/etc/cni/net.d目录查找cni json配置文件,基于type属性到/opt/cni/bin中查找相关插件的二进制文件,然后调用相应插件设置网络

网段配置

查看网段配置
[root@k8s-master1 ~]# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

网段分配原理

分配原理解读
集群的 kube-controller-manager 负责控制每个节点的网段分配
集群的 etcd 负责存储所有节点的网络配置存储
集群的 flannel 负责各个节点的路由表定制及其数据包的拆分和封装
– 所以flannel各个节点是平等的,仅负责数据平面的操作。网络功能相对来说比较简单。
另外一种插件 calico相对于flannel来说,多了一个控制节点,来管控所有的网络节点的服务进程。

flannel网络

pod效果
[root@k8s-master1 ~]# kubectl get pod -n kube-flannel  -o wide
NAME                    READY STATUS    ... IP          NODE               ...
kube-flannel-ds-b6hxm   1/1   Running   ... 10.0.0.15   kubernetes-node1   ...
kube-flannel-ds-bx7rq   1/1   Running   ... 10.0.0.12   kubernetes-master1 ...
kube-flannel-ds-hqwrk   1/1   Running   ... 10.0.0.13   kubernetes-master2 ...
kube-flannel-ds-npcw6   1/1   Running   ... 10.0.0.17   kubernetes-node3   ...
kube-flannel-ds-sx427   1/1   Running   ... 10.0.0.14   kubernetes-master3 ...
kube-flannel-ds-v5f4p   1/1   Running   ... 10.0.0.16   kubernetes-node2   ...
网卡效果
[root@k8s-master1 ~]# for i in {12..17};do ssh root@10.0.0.$i ifconfig | grep -A1 flannel ;done
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.0.0  netmask 255.255.255.255  broadcast 0.0.0.0
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.4.0  netmask 255.255.255.255  broadcast 0.0.0.0
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.5.0  netmask 255.255.255.255  broadcast 0.0.0.0
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.0  netmask 255.255.255.255  broadcast 0.0.0.0
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.2.0  netmask 255.255.255.255  broadcast 0.0.0.0
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.3.0  netmask 255.255.255.255  broadcast 0.0.0.0
注意:
	flannel.1 后面的.1 就是 vxlan的网络标识。便于隧道正常通信。
路由效果
[root@k8s-master1 ~]# ip route list | grep flannel
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink
10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink
10.244.5.0/24 via 10.244.5.0 dev flannel.1 onlink

flannel模型

  1. vxlan模型
    pod与Pod经由隧道封装后通信,各节点彼此间能通信就行,不要求在同一个二层网络, 这是默认采用的方式

  2. host-gw模型
    Pod与Pod不经隧道封装而直接通信,要求各节点位于同一个二层网络

  3. vxlan directrouting模型
    它是vxlan和host-gw自由组合的一种模型
    位于同一个二层网络上的、但不同节点上的Pod间通信,无须隧道封装;但非同一个二层网络上的节点上的Pod间通信,仍须隧道封装

在这里插入图片描述

vxlan原理

  1. 节点上的pod通过虚拟网卡对,连接到cni0的虚拟网络交换机上
    当有外部网络通信的时候,借助于 flannel.1网卡向外发出数据包
  2. 经过 flannel.1 网卡的数据包,借助于flanneld实现数据包的封装和解封
    最后送给宿主机的物理接口,发送出去
  3. 对于pod来说,它以为是通过 flannel.x -> vxlan tunnel -> flannel.x 实现数据通信
    因为它们的隧道标识都是".1",所以认为是一个vxlan,直接路由过去了,没有意识到底层的通信机制。

注意: 由于这种方式,是对数据报文进行了多次的封装,降低了当个数据包的有效载荷。所以效率降低了

host-gw原理

  1. 节点上的pod通过虚拟网卡对,连接到cni0的虚拟网络交换机上。
  2. pod向外通信的时候,到达CNI0的时候,不再直接交给flannel.1由flanneld来进行打包处理了。
  3. cni0直接借助于内核中的路由表,通过宿主机的网卡交给同网段的其他主机节点
  4. 对端节点查看内核中的路由表,发现目标就是当前节点,所以交给对应的cni0,进而找到对应的pod。

2.3 主机网络

配置文件
我们在部署flannel的时候,有一个配置文件,在这个配置文件中的configmap中就定义了虚拟网络的接入功能。

[root@kubernetes-master1 /data/kubernetes/flannel]# cat kube-flannel.yml
kind: ConfigMap
...
data:
  cni-conf.json: |						cni插件的功能配置
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",			基于flannel实现网络通信
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",			来实现端口映射的功能
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |						flannel的网址分配			
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"					来新节点的时候,基于vxlan从network中获取子网
      }
    }

路由表信息

[root@k8s-master1 ~]# ip route list | grep flannel
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
10.244.3.0/24 via 10.244.3.0 dev flannel.1 onlink
10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink
10.244.5.0/24 via 10.244.5.0 dev flannel.1 onlink

结果显示:
	如果数据包的目标是当前节点,这直接通过cni来进行处理
	如果数据包的目标是其他节点,这根据路由配置,交给对应节点上的flannel.1网卡来进行处理
	然后交给配套的flanneld对数据包进行封装

数据包转发解析

发现目标地址
[root@k8s-master1 /data/kubernetes/flannel]# ip neigh | grep flannel
10.244.2.0 dev flannel.1 lladdr ca:d2:1b:08:40:c2 PERMANENT
10.244.5.0 dev flannel.1 lladdr 92:d8:04:76:cf:af PERMANENT
10.244.1.0 dev flannel.1 lladdr ae:08:68:7d:fa:31 PERMANENT
10.244.4.0 dev flannel.1 lladdr c2:ef:c2:a6:aa:04 PERMANENT
10.244.3.0 dev flannel.1 lladdr ee:dd:92:60:41:8a PERMANENT

转发给指定主机
[root@k8s-master1 /data/kubernetes/flannel]# bridge fdb show flannel.1 | grep flannel.1
ee:dd:92:60:41:8a dev flannel.1 dst 10.0.0.17 self permanent
ae:08:68:7d:fa:31 dev flannel.1 dst 10.0.0.15 self permanent
c2:ef:c2:a6:aa:04 dev flannel.1 dst 10.0.0.13 self permanent
ca:d2:1b:08:40:c2 dev flannel.1 dst 10.0.0.16 self permanent
92:d8:04:76:cf:af dev flannel.1 dst 10.0.0.14 self permanent

测试效果

准备工作
[root@k8s-master1 ~]# kubectl scale deployment nginx --replicas=1
deployment.apps/nginx scaled

开启测试容器
[root@k8s-master1 ~]# kubectl run flannel-test --image="kubernetes-register.test.com/test/busybox:1.28" -it --rm --command -- /bin/sh

pod现状
[root@k8s-master1 ~]# kubectl get pod -o wide
NAME                  ...  IP           NODE             ...
flannel-test          ...  10.244.1.4   kubernetes-node1 ...
nginx-fd669dcb-h5d9d  ...  10.244.3.2   kubernetes-node3 ...
测试容器发送测试数据
[root@k8s-master1 ~]# kubectl run flannel-test --image="kubernetes-register.test.com/test/busybox:1.28" -it --rm --command -- /bin/sh
If you don't see a command prompt, try pressing enter.
/ # ping -c 1 10.244.3.2
PING 10.244.3.2 (10.244.3.2): 56 data bytes
64 bytes from 10.244.3.2: seq=0 ttl=62 time=1.479 ms
...

Flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口,我们在node3上抓取node1的数据包
[root@k8s-node3 ~]# tcpdump -i eth0 -en host 10.0.0.15  and udp port 8472
...
07:46:40.104526 00:50:56:37:14:42 > 00:50:56:37:ed:73, ethertype IPv4 (0x0800), length 148: 10.0.0.17.53162 > 10.0.0.15.otv: OTV, flags [I] (0x08), overlay 0, instance 1
ee:dd:92:60:41:8a > ae:08:68:7d:fa:31, ethertype IPv4 (0x0800), length 98: 10.244.3.2 > 10.244.1.4: ICMP echo reply, id 1792, seq 0, length 64

结果显示:
	这里面每一条数据,都包括了两层ip数据包

host-gw实践

修改flannel的配置文件,将其转换为 host-gw 模型。
[root@k8s-master1 ~]# kubectl get cm -n kube-flannel
NAME               DATA   AGE
kube-flannel-cfg   2      82m

修改资源配置文件
[root@k8s-master1 ~]# kubectl  edit cm kube-flannel-cfg -n kube-flannel
...
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }
    
重启pod
[root@k8s-master1 ~]# kubectl  delete pod -n kube-flannel -l app
[root@k8s-master1 ~]# kubectl get pod -n kube-flannel
NAME                    READY   STATUS    RESTARTS   AGE
kube-flannel-ds-5dw2r   1/1     Running   0          21s
kube-flannel-ds-d9bh2   1/1     Running   0          21s
kube-flannel-ds-jvn2f   1/1     Running   0          21s
kube-flannel-ds-kcrb4   1/1     Running   0          22s
kube-flannel-ds-ptlx8   1/1     Running   0          21s
kube-flannel-ds-wxqd7   1/1     Running   0          22s

检查信息

查看路由信息
[root@k8s-master1 ~]# ip route list | grep 244
10.244.1.0/24 via 10.0.0.15 dev eth0
10.244.2.0/24 via 10.0.0.16 dev eth0
10.244.3.0/24 via 10.0.0.17 dev eth0
10.244.4.0/24 via 10.0.0.13 dev eth0
10.244.5.0/24 via 10.0.0.14 dev eth0

在flannel-test中继续wget测试
/ # wget 10.244.3.2
Connecting to 10.244.3.2 (10.244.3.2:80)
index.html           100% |***|    41   0:00:00 ETA
在node3中继续抓包
[root@k8s-node3 ~]# tcpdump -i eth0 -nn host 10.244.1.4 and tcp port 80
...
08:01:46.930340 IP 10.244.3.2.80 > 10.244.1.4.59026: Flags [FP.], seq 232:273, ack 74, win 56, length 41: HTTP
08:01:46.930481 IP 10.244.1.4.59026 > 10.244.3.2.80: Flags [.], ack 232, win 58, length 0
08:01:46.931619 IP 10.244.1.4.59026 > 10.244.3.2.80: Flags [F.], seq 74, ack 274, win 58, length 0
08:01:46.931685 IP 10.244.3.2.80 > 10.244.1.4.59026: Flags [.], ack 75, win 56, length 0
结果显示:
	两个pod形成了直接的通信效果。

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

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

相关文章

Elasticsearch基础

Elasticsearch是搜索服务器 视频地址&#xff1a;https://www.bilibili.com/video/BV1Sy4y1G7LL/?p6&spm_id_from333.880.my_history.page.click&vd_sourcefc7fa697b97f292319c5b0cde4755484 下载地址&#xff1a;[https://artifacts.elastic.co/downloads/elasticse…

赋予品牌文化原力,从已有文化中挖掘

赋予品牌以文化原力&#xff0c;从已有文化中挖掘 华与华总结为&#xff1a; 寻找母体&#xff0c;寄生母体&#xff0c;成为母体&#xff0c;壮大母体 非常经典的例子&#xff1a;微信红包 趣讲大白话&#xff1a;文化有持续的力量 【趣讲信息科技186期】 *******************…

Redisson看门狗机制为什么比将锁设置成永久有效期更好?

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…

【测绘程序设计】C#伪距单点定位

文章目录 一、题目解读二、界面设计三、矩阵计算实现1、矩阵定义Matrix2、矩阵构造Matrix()3、单位矩阵MatrixE()4、加减乘操作符重载-*5、矩阵转置transposs()6、矩阵求逆Inverse() 四、数据存储结构设计1、Sat类存一颗卫星的数据2、Epoch类存一个历元的数据3、DataCenter类存…

Spring Cloud Alibaba - Sentinel(一)

目录 一、Sentinel介绍 1、什么是Sentinel 2、Sentinel好处 3、Sentinel下载和安装 二、搭建Sentinel项目 1、创建项目cloudalibaba-sentinel-service8401 三、Sentinel流控规则 1、流控规则基本介绍 2、新增流控 2.1、QPS直接失败案例 2.2、线程数直接失败案例 3、…

CF1839B Lamps

思路 这道题我们可以利用贪心的思想。 我们这里把 a i a_i ai​理解为第 a i a_i ai​层灯。 在层数相同的灯被破坏之后&#xff0c;灯的个数就会减少到 0 0 0&#xff0c;所以它一定不会影响到之后下一层的灯。 所以&#xff0c;我们贪心的思路是&#xff1a; 将每一层&am…

FreeRTOS学习(五)

延时函数 vTaskDelay()&#xff1a;相对延时&#xff0c;指每次延时都是从执行函数vTaskDelay()开始&#xff0c;直到延时指定的时间结束。vTaskDelayUntil()&#xff1a;绝对延时&#xff0c;整个任务的运行周期看成一个整体&#xff0c;适用于需要按照一定频率运行的任务。 …

卡尔曼滤波与组合导航原理笔记(一)第二部分 卡尔曼滤波方程的推导

文章目录 三、卡尔曼滤波1、随机系统状态空间模型2、状态预测3、状态量测4、增益矩阵K与状态估计5、Kalman滤波公式汇总6、Kalman滤波流程图1.划分为左右两部分&#xff08;一阶矩和二阶矩&#xff09;2.划分为上下两部分&#xff08;时间更新、量测更新&#xff09; 7、Kalman…

Windows11安装kohya_ss详细步骤(报错、踩坑)

文章目录 笔者环境所需环境安装kohya_ss方式一&#xff1a;带有GUI的kohya_ss仓库方式二&#xff1a;kohya_ss核心仓库 笔者环境 OS&#xff1a;windows11Python&#xff1a;3.10.6CUDA11.6 所需环境 Python3.10.6GitCUDA11.6 安装kohya_ss 方式一&#xff1a;带有GUI的ko…

mybatis执行流程分析

mybatis全局配置文件 mybatis全局配置文件中涉及的标签如下图所示 配置文件解析 public static void main(String[] args) throws IOException {// 读取配置文件InputStream is Resources.getResourceAsStream("org/apache/ibatis/builder/MapperConfig1.xml");//…

chatgpt赋能python:Python多种颜色——提升SEO排名的技巧

Python多种颜色——提升SEO排名的技巧 在网站设计中&#xff0c;使用多种颜色可以提高用户体验和页面美观度。但你是否知道使用多种颜色还可以提高SEO排名呢&#xff1f;本文将介绍如何在Python代码中使用多种颜色来提高你的SEO排名。 什么是SEO&#xff1f; SEO的全称为“S…

chatgpt赋能python:Python备份文件夹:保障数据安全的最佳方法

Python备份文件夹&#xff1a;保障数据安全的最佳方法 数据备份是确保所有重要信息安全的关键部分。对于IT专业人士和计算机爱好者而言&#xff0c;文件夹备份是一项必不可少的任务。而Python是备份文件夹最流行的语言之一&#xff0c;因为它易于学习和使用。 在这篇文章中&am…

stable-diffusion基础问题记录

一、windows安装 1、启动 如果自己是anaconda&#xff0c;python版本不是3.10.6 conda create -n python_3_10_6 python3.10.6&#xff0c;创建一个这样的环境 修改webui-user.bat set PYTHOND:/software/Anaconda3/envs/python_3_10_6/python&#xff0c;把python换成这个…

【走进Linux的世界】Linux---基本指令(3)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 date指令cal指…

12代CPU启用SR-IOV vGPU,实现一台电脑当七台用

背景 虚拟桌面基础设施&#xff08;VDI&#xff09;技术一般部署在服务器&#xff0c;可以实现多个用户连接到服务器上的虚拟桌面。随着桌面计算机性能的日益提升&#xff0c;桌面计算机在性能在很多场景下已经非常富余&#xff0c;足够同时满足多个用户同时使用的需求。实际项…

Redis的持久化详解

目录 一、Redis的持久化二、RDB&#xff08;Redis DataBase&#xff09;1、RDB快照原理2、RDB配置3、redis.conf 其他一些配置4、RDB的备份恢复5、RDB优缺点 三、AOF&#xff08;Append Of File&#xff09;1、AOF原理2、AOF配置3、AOF的备份恢复4、重写流程5、AOF优缺点 四、A…

MySQL | Windows服务器部署ZIP免安装版MySQL8.0+数据库笔记

Windows服务器部署ZIP免安装版MySQL8.0数据库笔记 文章目录 Windows服务器部署ZIP免安装版MySQL8.0数据库笔记下载MySQL压缩包编写配置文件环境变量初始化数据库安装MySQL服务安装错误&#xff1a;VCRUNTIME140_1.dll 登录 MySQL 下载MySQL压缩包 打开官网的下载页面&#xff…

POI报表的入门

POI报表的入门 理解员工管理的的业务逻辑 能够说出Eureka和Feign的作用 理解报表的两种形式和POI的基本操作熟练使用POI完成Excel的导入导出操作 员工管理 需求分析 企业员工管理是人事资源管理系统中最重要的一个环节&#xff0c;分为对员工入职&#xff0c;转正&#xff0c;离…

chatgpt赋能python:Python如何处理AI文件

Python如何处理AI文件 什么是AI文件&#xff1f; AI文件是Adobe Illustrator的标准文件格式。它包含了图形设计师所创建的矢量图形&#xff0c;这些矢量图形可以根据需要进行缩放和文件大小的调整。AI文件是专业印刷和设计领域中最常用的格式之一。 为什么要处理AI文件&…

深入ReentrantReadWriteLock

ReentrantReadWriteLock出现的原因 首先synchronized和ReentrantLock都是互斥锁&#xff0c;一个线程在获取锁资源之后另一个线程只能等待假设有一种情况是读多写少&#xff0c;并且确保线程安全。可以使用ReentrantReadWriteLock实现ReentrantReadWriteLock的特点是读读不互斥…