Kubelet v1.25.x源码——StatusManager

news2024/11/24 20:10:29

1. 环境说明

Kubernetes源码版本:remotes/origin/release-1.25
Kubernetes编译出来的Kubelet版本:Kubernetes v1.24.0-beta.0.2463+ee7799bab469d7
Kubernetes集群实验环境:使用Kubernetes v1.25.4二进制的方式搭建了一个单节点集群

K8S 单节点单节点搭建可以参考:Kubernetes v1.25 搭建单节点集群用于Debug K8S源码

Golang版本:go1.19.3 linux/amd64
IDEA版本:2022.2.3
Delve版本:1.9.1

[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]# dlv version
Delve Debugger
Version: 1.9.1
Build: $Id: d81b9fd12bfa603f3cf7a4bc842398bd61c42940 $
[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]# go version
go version go1.19.3 linux/amd64
[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]# kubectl version
WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short.  Use --output=yaml|json to get the full version.
Client Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.4", GitCommit:"872a965c6c6526caa949f0c6ac028ef7aff3fb78", GitTreeState:"clean", BuildDate:"2022-11-09T13:36:36Z", GoVersion:"go1.19.3", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v4.5.7
Server Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.4", GitCommit:"872a965c6c6526caa949f0c6ac028ef7aff3fb78", GitTreeState:"clean", BuildDate:"2022-11-09T13:29:58Z", GoVersion:"go1.19.3", Compiler:"gc", Platform:"linux/amd64"}
[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]# kubectl get nodes -owide
NAME          STATUS   ROLES    AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION                CONTAINER-RUNTIME
k8s-master1   Ready    <none>   31h   v1.25.4   192.168.11.71   <none>        CentOS Linux 7 (Core)   3.10.0-1160.80.1.el7.x86_64   containerd://1.6.10
[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]#
[root@k8s-master1 kubernetes]# kubectl get componentstatus
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE                         ERROR
etcd-0               Healthy   {"health":"true","reason":""}
controller-manager   Healthy   ok
scheduler            Healthy   ok
[root@k8s-master1 kubernetes]#

Kubelet启动参数配置如下:

[root@k8s-master1 kubernetes]# ps -ef|grep "/usr/local/bin/kubelet"
root       7972      1  6 07:06 ?        00:00:06 /usr/local/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig --kubeconfig=/etc/kubernetes/kubelet.kubeconfig --config=/etc/kubernetes/kubelet-conf.yml --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-labels=node.kubernetes.io/node= --v=8
root       9549   6424  0 07:07 pts/0    00:00:00 grep --color=auto /usr/local/bin/kubelet
[root@k8s-master1 kubernetes]#

Kubelet参数配置如下:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.pem
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
cgroupDriver: systemd
cgroupsPerQOS: true
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
  imagefs.available: 15%
  memory.available: 100Mi
  nodefs.available: 10%
  nodefs.inodesFree: 5%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s

2. 组件概览

StatusManager主要职责如下:

  • 1、StatusManager中维护了Pod的状态,该状态会和最新的v1.PodStatus保持同步
  • 2、于此同时,StatusManager会更新apiserver Pod的状态
    StatusManager组件

在这里插入图片描述

3. 源码分析

Manager

我们先来看看StatusManager的接口定义,如下:

  • Manager.Start:用于和apiserver同步状态
  • Manager.SetPodStatus:更新StatusManager缓存中该Pod的状态
  • Manager.SetContainerReadiness:更新StatusManager缓存中的Container状态,并且会触发状态更新
  • Manager.SetContainerStartup:更新StatusManager缓存中的Container的启动状态,并且会触发状态更新
  • Manager.TerminatePod:重置StatusManager缓存中的Container状态为Terminated,并且触发状态更新
  • Manager.RemoveOrphanedStatuses:扫描StatusManager.cache,并移除已经删除的Pod
  • PodStatusProvider.GetPodStatus:查询容器状态
// pkg/kubelet/status/status_manager.go

type Manager interface {
	PodStatusProvider
	Start()
	SetPodStatus(pod *v1.Pod, status v1.PodStatus)
	SetContainerReadiness(podUID types.UID, containerID kubecontainer.ContainerID, ready bool)
	SetContainerStartup(podUID types.UID, containerID kubecontainer.ContainerID, started bool)
	TerminatePod(pod *v1.Pod)
	RemoveOrphanedStatuses(podUIDs map[types.UID]bool)
}

type PodStatusProvider interface {
	GetPodStatus(uid types.UID) (v1.PodStatus, bool)
}

我们继续来看看StatusManager的具体实现,如下:

// pkg/kubelet/status/status_manager.go

type manager struct {
    // apiserver的客户端
	kubeClient clientset.Interface
	// PodManager用于维护StaticPod以及MirrorPod之间的映射
	podManager kubepod.Manager
	// Pod的状态缓存
	podStatuses      map[types.UID]versionedPodStatus
	// 读写锁,StatusManager的所有的方法都是线程安全的
	podStatusesLock  sync.RWMutex
	// Pod状态发生改变的消费方
	podStatusChannel chan podStatusSyncRequest
	// mirrorPod的版本缓存
	apiStatusVersions map[kubetypes.MirrorPodUID]uint64
	// 用于保证Pod可以被安全的删除
	podDeletionSafety PodDeletionSafetyProvider
}

我们继续看StatusManager的其余方法的实现

3.1. Start

Start

具体逻辑如下:

  • 1、首先判断是否已经初始化好了apiserver的客户端,如果没有初始化,直接退出。因为后续所有的的操作都需要和apiserver交互,所以这里直接退出也没啥
  • 2、只要从podStatusChannel拿到了同步请求,就调用syncPod同步
  • 3、每隔10s进行一次批量同步
// pkg/kubelet/status/status_manager.go

func (m *manager) Start() {
    // StatusManager本来就是用于更新Apiserver Pod状态的组件,现在如果apiserver的客户端没有初始化,那肯定需要直接退出
	if m.kubeClient == nil {
		klog.InfoS("Kubernetes client is nil, not starting status manager")
		return
	}

	klog.InfoS("Starting to sync pod status with apiserver")
    // 用于定期批量同步
	syncTicker := time.NewTicker(syncPeriod).C

    // 可以看到,StatusManager这辈子基本上是交代在这里了,就干两件事,其一是当有Pod更新的时候同步这个Pod, 其二就是每隔10秒钟批量同步
	go wait.Forever(func() {
		for {
			select {
			case syncRequest := <-m.podStatusChannel:
				klog.V(5).InfoS("Status Manager: syncing pod with status from podStatusChannel",
					"podUID", syncRequest.podUID,
					"statusVersion", syncRequest.status.version,
					"status", syncRequest.status.status)
				m.syncPod(syncRequest.podUID, syncRequest.status)
			case <-syncTicker:
				klog.V(5).InfoS("Status Manager: syncing batch")
				for i := len(m.podStatusChannel); i > 0; i-- {
					<-m.podStatusChannel
				}
				m.syncBatch()
			}
		}
	}, 0)
}

3.1.1. syncPod

syncPod

我们来看看SyncPod是如何把Pod的状态同步给apiserver的,主要逻辑如下:

  • 1、判断当前的Pod是否真的需要更新,如果不需要更新就直接返回
  • 2、根据当前PodUID,从apiserver中获取当前Pod
  • 3、如果从PodManager中获取的UID和当前的Pod UID不相等,说明StatusManager中的装状态不是最新的,直接删除老Pod的状态
  • 4、合并新老PodCondition给新的Pod
  • 5、更新apiserver中的Pod状态,更新StatusManager中的Pod状态
  • 6、如果当前Pod已经被删除,那么通过CRI接口删除底层Pod,然后调用apiserver的接口删除Pod
// pkg/kubelet/status/status_manager.go

func (m *manager) syncPod(uid types.UID, status versionedPodStatus) {
    // 先判断当前Pod是否需要更新,如果当前Pod的状态已经是最新的,那么直接退出,估计是通过版本来比较
	if !m.needsUpdate(uid, status) {
		klog.V(1).InfoS("Status for pod is up-to-date; skipping", "podUID", uid)
		return
	}

    // 从apiserver中获取当前Pod
	pod, err := m.kubeClient.CoreV1().Pods(status.podNamespace).Get(context.TODO(), status.podName, metav1.GetOptions{})
	if errors.IsNotFound(err) {// 如果当前Pod在apiserver中没有找到,说明这个Pod已经被删除,这里直接忽略更新
		return
	}
	if err != nil {
		return
	}

	translatedUID := m.podManager.TranslatePodUID(pod.UID)
	// 从这里可以看出,apiserver中的Pod一定是最新的
	if len(translatedUID) > 0 && translatedUID != kubetypes.ResolvedPodUID(uid) {
		klog.V(2).InfoS("Pod was deleted and then recreated, skipping status update",
			"pod", klog.KObj(pod),
			"oldPodUID", uid,
			"podUID", translatedUID)
		// 删除StatusManager中维护的老Pod的状态,相当于是更新StatusManager的Pod状态
		m.deletePodStatus(uid)
		return
	}

    // 合并新老Pod的Condition
	mergedStatus := mergePodStatus(pod.Status, status.status, m.podDeletionSafety.PodCouldHaveRunningContainers(pod))

    // 更新apiserver中该Pod的状态
	newPod, patchBytes, unchanged, err := statusutil.PatchPodStatus(context.TODO(), m.kubeClient, pod.Namespace, pod.Name, pod.UID, pod.Status, mergedStatus)
	klog.V(3).InfoS("Patch status for pod", "pod", klog.KObj(pod), "patch", string(patchBytes))

	if err != nil {
		klog.InfoS("Failed to update status for pod", "pod", klog.KObj(pod), "err", err)
		return
	}
	if unchanged {
		klog.V(3).InfoS("Status for pod is up-to-date", "pod", klog.KObj(pod), "statusVersion", status.version)
	} else {
		klog.V(3).InfoS("Status for pod updated successfully", "pod", klog.KObj(pod), "statusVersion", status.version, "status", mergedStatus)
		pod = newPod
	}

	m.apiStatusVersions[kubetypes.MirrorPodUID(pod.UID)] = status.version

    // 如果当前Pod需要被删除,那么就通知底层的CRI删除Pod
	if m.canBeDeleted(pod, status.status) {
		deleteOptions := metav1.DeleteOptions{
			GracePeriodSeconds: new(int64),
			Preconditions: metav1.NewUIDPreconditions(string(pod.UID)),
		}
		// 如果底层CRI删除Pod成功,就删除apiserver中保存的Pod
		err = m.kubeClient.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, deleteOptions)
		if err != nil {
			klog.InfoS("Failed to delete status for pod", "pod", klog.KObj(pod), "err", err)
			return
		}
		klog.V(3).InfoS("Pod fully terminated and removed from etcd", "pod", klog.KObj(pod))
		m.deletePodStatus(uid)
	}
}

3.1.1.1. needsUpdate

needsUpdate

needUpdate方法主要是用于判断当前需要更新的Pod是否需要更新

func (m *manager) needsUpdate(uid types.UID, status versionedPodStatus) bool {
    // 猜的没错,果然是比较Pod的版本
	latest, ok := m.apiStatusVersions[kubetypes.MirrorPodUID(uid)]
	// 如果StatusManager的缓存中没找到当前Pod或者版本较小,那么肯定需要更新
	if !ok || latest < status.version {
		return true
	}

    // 从PodManager中获取当前Pod
	pod, ok := m.podManager.GetPodByUID(uid)
	if !ok { // 如果PodManager中没有找到这个Pod,则直接返回`False`,为啥呀? 没看懂
		return false
	}
	// 如果在PodManager中找到了当前Pod,
	return m.canBeDeleted(pod, status.status)
}

func (m *manager) canBeDeleted(pod *v1.Pod, status v1.PodStatus) bool {
    // 如果当前Pod被删除了,或者是MirrorPod,则直接返回false
	if pod.DeletionTimestamp == nil || kubetypes.IsMirrorPod(pod) {
		return false
	}
	return m.podDeletionSafety.PodResourcesAreReclaimed(pod, status)
}

3.1.1.2. mergePodStatus

mergePodStatus

该方法主要是用于合并新老PodCondition

func mergePodStatus(oldPodStatus, newPodStatus v1.PodStatus, couldHaveRunningContainers bool) v1.PodStatus {
    // 给定切片的长度,防止动态扩容造成性能损失
	podConditions := make([]v1.PodCondition, 0, len(oldPodStatus.Conditions)+len(newPodStatus.Conditions))

	for _, c := range oldPodStatus.Conditions {
		if !kubetypes.PodConditionByKubelet(c.Type) {
			podConditions = append(podConditions, c)
		}
	}
	for _, c := range newPodStatus.Conditions {
		if kubetypes.PodConditionByKubelet(c.Type) {
			podConditions = append(podConditions, c)
		}
	}
	newPodStatus.Conditions = podConditions


	if !podutil.IsPodPhaseTerminal(oldPodStatus.Phase) && podutil.IsPodPhaseTerminal(newPodStatus.Phase) {
		if couldHaveRunningContainers {
			newPodStatus.Phase = oldPodStatus.Phase
			newPodStatus.Reason = oldPodStatus.Reason
			newPodStatus.Message = oldPodStatus.Message
		}
	}

	if podutil.IsPodPhaseTerminal(newPodStatus.Phase) {
		if podutil.IsPodReadyConditionTrue(newPodStatus) || podutil.IsContainersReadyConditionTrue(newPodStatus) {
			containersReadyCondition := generateContainersReadyConditionForTerminalPhase(newPodStatus.Phase)
			podutil.UpdatePodCondition(&newPodStatus, &containersReadyCondition)

			podReadyCondition := generatePodReadyConditionForTerminalPhase(newPodStatus.Phase)
			podutil.UpdatePodCondition(&newPodStatus, &podReadyCondition)
		}
	}

	return newPodStatus
}

3.1.2. syncBatch

syncBatch

继续看看syncBatch究竟干了啥:

// pkg/kubelet/status/status_manager.go

func (m *manager) syncBatch() {
	var updatedStatuses []podStatusSyncRequest
	podToMirror, mirrorToPod := m.podManager.GetUIDTranslations()
	func() { // Critical section
		m.podStatusesLock.RLock()
		defer m.podStatusesLock.RUnlock()

        // 如果当前Pod在StatusManager中没找到,并且也不是MirrorPod,说明当前Pod早已经被删除了,这里更新Map,是否内存空间
		for uid := range m.apiStatusVersions {
			_, hasPod := m.podStatuses[types.UID(uid)]
			_, hasMirror := mirrorToPod[uid]
			if !hasPod && !hasMirror {
				delete(m.apiStatusVersions, uid)
			}
		}

        // 遍历所有的Pod,看看他们是否需要更新
		for uid, status := range m.podStatuses {
			syncedUID := kubetypes.MirrorPodUID(uid)
			if mirrorUID, ok := podToMirror[kubetypes.ResolvedPodUID(uid)]; ok {
				if mirrorUID == "" {
					klog.V(5).InfoS("Static pod does not have a corresponding mirror pod; skipping",
						"podUID", uid,
						"pod", klog.KRef(status.podNamespace, status.podName))
					continue
				}
				syncedUID = mirrorUID
			}
			if m.needsUpdate(types.UID(syncedUID), status) {
				updatedStatuses = append(updatedStatuses, podStatusSyncRequest{uid, status})
			} else if m.needsReconcile(uid, status.status) {
				delete(m.apiStatusVersions, syncedUID)
				updatedStatuses = append(updatedStatuses, podStatusSyncRequest{uid, status})
			}
		}
	}()

	for _, update := range updatedStatuses {
		klog.V(5).InfoS("Status Manager: syncPod in syncbatch", "podUID", update.podUID)
		m.syncPod(update.podUID, update.status)
	}
}

3.2. SetPodStatus

TODO

3.3. SetContainerReadiness

TODO

3.4. SetContainerStartup

TODO

3.5. TerminatePod

TODO

3.6. RemoveOrphanedStatuses

TODO

4. Reference

Kubernetes源码分析——kubelet

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

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

相关文章

Linux redhat8.0 NFS共享目录

简介&#xff1a; NFS允许一个系统在网络上与它人共享目录和文件。通过使用NFS&#xff0c;用户和程序可以像访问本地文件一样访问远端系统上的文件。 作用&#xff1a; 多个机器共享一台CDROM或者其他设备。这对于在多台机器中安装软件来说更加便宜跟方便。 员工都可以操作…

流辰信息技术企业凭匠心,助力企业打造专属低代码解决方案!

从建立之初开始&#xff0c;流辰信息技术企业就凭借一股热劲和对低代码开发平台的执着热爱精神&#xff0c;在业界打磨升级&#xff0c;成为众多新老客户朋友青睐和信任的服务商。 在稳定发展的近些年中&#xff0c;流辰信息一直信奉品质和服务是紧跟市场发展步伐的法宝&#x…

maven工程,mybatis,spring

maven 简介&#xff1a;Maven主要用于解决导入依赖于Java类的jar和编译Java项目的主要问题&#xff0c;其使用pom文件将自动管理下载的jar包 创建maven工程 1. groupid:包名 artifactid:工程名 location:存放位置 2.结构 工程目录结构&#xff1a;srcmain主程序区java后端代…

ARM的八种工作模式

ARM有八种工作模式&#xff0c;有些处理器可能是七种&#xff0c;这个要看处理器的版本&#xff0c;早期的处理器如ARM9有七种工作模式&#xff0c;到了后来的Cortex系列新增了一种Secure Monitor模式。对工作模式的理解需要记住以下三点&#xff1a; 不同模式拥有不同的权限不…

基于冲突搜索算法的多机器人路径规划(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Java Request学习笔记

1.获取请求部分的信息方法&#xff1a; Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//设置响应的内容为html&#xff0c;指定编码为utf-8response.setContentType("text/html;…

如何利用ArcGIS探究环境与生态因子对水体、土壤、大气污染物等影响

如何利用ArcGIS实现电子地图可视化表达&#xff1f;如何利用ArcGIS分析空间数据&#xff1f;如何利用ArcGIS提升SCI论文的层次&#xff1f;制图是地理数据展现的直观形式&#xff0c;也是地理数据应用的必要基础。本次课程从ArcGIS的基本操作、ArcGIS 的空间数据分析及ArcGIS 的…

Biotin-PEG-Fluorescein,FITC-PEG-Biotin,荧光素peg生物素用于纳米粒子

英文名称&#xff1a;FITC-PEG-Biotin&#xff0c;Biotin-PEG-Fluorescein 中文名称&#xff1a;荧光素-聚乙二醇-生物素 FITC-PEG生物素是一种含有荧光素染料和生物素基团的线性异生物功能PEG试剂。荧光素通常被称为FITC&#xff0c;是生物学中常用的荧光示踪剂&#xff0c;…

[附源码]Node.js计算机毕业设计电影网站系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

Flask从入门到放弃三(g对象、Flask-Session的使用、Flask集成Mysql、Wtforms、信号)

文章标题一、g对象二、Flask-Session使用三、数据库连接池1&#xff09;Flask中集成mysql2&#xff09;上面代码存在的问题3&#xff09;使用数据库连接池4&#xff09;压力测试四、WTForms1&#xff09;Python代码2&#xff09;Hmtl代码五、信号一、g对象 介绍 g对象全称glo…

java instanceof关键字详解

instanceof描述 将从操作数堆栈中弹出objectref&#xff0c;它必须是引用类型。无符号indexbyte1和indexbyte2用于将索引构造到当前类的运行时常量池中&#xff08;2.6&#xff09;&#xff0c;其中索引的值为&#xff08;indexbyte1<<8&#xff09;|indexbyte2.索引处的…

node文件上传和下载

node文件上传和下载 一、准备项目 npm install -g express-generator express upload-download然后用vscode打开&#xff0c;安装依赖包&#xff0c;在装一个nodemon #安装依赖npm i# 安装nodemonpm i nodemon -g修改启动命令 启动项目&#xff0c;访问localhost&#xff1a;…

WebDAV之葫芦儿·派盘 + CloudBeats

CloudBeats 支持WebDAV方式连接葫芦儿派盘。 推荐一款手机云端音乐播放器,可以直接播放云盘中的音乐,不占用手机内存,同时也可以播放本地的音频文件,自动扫描,支持离线播放,可以自由创建音乐播放列表。 CloudBeats无需网络即可运行。将专辑,播放列表,文件夹或文件下…

艾美捷游离维多珠单抗ADA水平检测试剂盒,高灵敏度检测

艾美捷游离维多珠单抗ADA水平检测试剂盒用于测定维多利单抗&#xff08;如ENTYVIO&#xff09;的游离人抗体) 适用于EDTA血浆和血清。只用于体外诊断。 引言&#xff1a; 中度至重度活动型溃疡性结肠炎患者克罗恩病&#xff0c;常规治疗或肿瘤坏死因子α&#xff08;TNFα&…

pmp考试多长时间出成绩?

考完大概 6-8 周之后就会出陆续出成绩了&#xff0c;一开始没查到成绩的别着急&#xff0c;出成绩的时间会持续一周左右。 这次 2022年 6 月考的已经在陆续出成绩了&#xff0c;快来查查你过了没 一、查看是否通过 1、登录PMI 官网&#xff0c;点击“Log In” 如果忘记 PMI 的…

rancher 给k8s api 审计日志增加webhook

例子可以参考gosoon / k8s-audit-webhook或者omri86 / k8s-audit-webhook 编译运行。 然后将对应的audit-webhook.yaml&#xff0c;拷贝到/etc/kubernetes/目录下。 然后登录rancher 管控界面&#xff0c;进入Cluster Management 页面 找到对应的集群。 然后点击最后的三个点…

Servlet:狂神源码分析2

目录Demo结构源码分析总结Demo结构 源码分析 1.首先进入web.xml配置一下serlvet路由&#xff0c;也就是serlvet的name&#xff0c;class&#xff0c;以及对应的url-pattern <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http:/…

java计算机毕业设计ssm智慧小区团购系统4x45g(附源码、数据库)

java计算机毕业设计ssm智慧小区团购系统4x45g&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff0…

【云计算与大数据技术】流计算讲解及集群日志文件实时分析实战(附源码)

需要源码请点赞关注收藏后评论区留言私信~~ 一、流计算概述 在传统的数据处理流程中总是先收集数据,然后将数据放到 DB中。当人们需要的 时候通过DB对数据做query,得到答案或进行相关的处理。这样看起来虽然非常合理&#xff0c;采用类似于 MapReduce方式的离线处理并不能很好…

【TCP/IP】【调试】丢包、流不通、错包等问题查证手段系列之四——数通设备(交换机/路由器)的镜像

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;TCP/IP协议&…