云原生之深入解析Kubernetes Operator的最佳实践和最常见的问题分析

news2024/9/21 18:49:35

一、Kubernetes Operator 简介

  • Kubernetes Operator 是通过连接主 API 并 watch 时间的一组进程,一般会 watch 有限的资源类型。当相关 watch 的 event 触发的时候,operator 做出响应并执行具体的动作。这可能仅限于与主 API 交互,但通常会涉及在其他一些系统上执行某些操作(可以是集群中或集群外资源)。
  • Kubernetes 操作员是连接到主 API 并监视事件的进程,通常是在有限数量的资源类型上。当相关事件发生时,操作员做出反应并执行特定的操作。

在这里插入图片描述

  • 操作符被实现为一组控制器,其中每个控制器监视一个特定的资源类型。当被监视资源上发生相关事件时,将启动一个协调周期。Operators 是控制器的集合,并且每个控制器 watch 指定的资源类型。当被 watched 的资源时间触发的时候,协调周期也将随之启动。在协调周期期间,控制器有责任检查当前状态是否与被 watched 资源描述的期望状态相匹配。有趣的是,根据设计,时间并不会传递到协调周期中,这将会强制地让你去考虑实例的整个状态,这种方法被称为基于水平触发而不是基于边缘触发(level-based, as opposed to edge-based)。这源自于电子电路的设计,水平触发是接收 event(例如中断)并对状态做出反应的理念,而基于边缘的触发是接收 event 并对状态变化做出反应的理念。
  • 水平触发虽说效率较低,因为它强制重新评估完整的状态,而不是仅仅关注改变了什么,但在信号可能丢失或多次重复传输的复杂不可靠环境中,这种方式被认为是更适用的。这种设计的选择会影响我们编写控制器代码的方式。如下提供了一个高层次的总结:

在这里插入图片描述

  • 当向API服务器发送请求时,特别是对于创建和删除请求,它们将会经历上述的阶段。需要注意的是,也可以指定 webhook 来执行请求的更改和验证。如果 operator 引入 CRD(custom resource definition),可能还必须定义这些 webhook。一般来说,operator 进程会开放一个端口来来实现 webhook endpoint。
  • 如果 operator 引入了一个新的 CRD,Operator SDK 将会协助你来搭建,为确保 CRD 符合 Kubernetes 扩展 API 的最佳实践,请遵循这些约定。这里所提到的所有的最佳实践都在 operator-utils 代码库中,并以可运行的例子体现。在 operator 项目中,也可以将 operator-utils 以 library 的方式导入,以此提供一些有用的工具。

二、创建 watches

  • 正如我们所说,控制器监视资源上的事件,这是通过对手表的抽象来实现的。watch 是一种接收某种类型(核心类型或 CRD)的机制。一般通过指定以下内容来创建 watch 机制:
    • 想要 watch 的资源类型;
    • handler 将被监视类型上的 events 映射到一个或多个调用协调周期的实例,监视类型和实例类型不必相同;
    • predicate 是一组能够过滤 events 且可自定义的函数。
  • 如下记录了以上提及的内容:

在这里插入图片描述

  • 通常来说,同一类型(kind)开启多个 watch 是可行的,因为 watch 是多路复用的。也应该尽可能多地尝试过滤 event,这边有个 predicate 例子,用来过滤 secret 资源上的 event,这里只对类型为 TLS 的 secret 资源 event 感兴趣:
isAnnotatedSecret := predicate.Funcs{
    UpdateFunc: func(e event.UpdateEvent) bool {
        oldSecret, ok := e.ObjectOld.(*corev1.Secret)
        if !ok {
            return false
        }
        newSecret, ok := e.ObjectNew.(*corev1.Secret)
        if !ok {
            return false
        }
        if newSecret.Type != util.TLSSecret {
            return false
        }
        oldValue, _ := e.MetaOld.GetAnnotations()[certInfoAnnotation]
        newValue, _ := e.MetaNew.GetAnnotations()[certInfoAnnotation]
        old := oldValue == "true"
        new := newValue == "true"
        // if the content has changed we trigger if the annotation is there
        if !reflect.DeepEqual(newSecret.Data[util.Cert], oldSecret.Data[util.Cert]) ||
            !reflect.DeepEqual(newSecret.Data[util.CA], oldSecret.Data[util.CA]) {
            return new
        }
        // otherwise we trigger if the annotation has changed
        return old != new
    },
    CreateFunc: func(e event.CreateEvent) bool {
        secret, ok := e.Object.(*corev1.Secret)
        if !ok {
            return false
        }
        if secret.Type != util.TLSSecret {
            return false
        }
        value, _ := e.Meta.GetAnnotations()[certInfoAnnotation]
        return value == "true"
    },
}
  • 一个非常常见的模式是观察创建(和拥有)资源上的 events,并且定期在拥有这些资源的 CR 上执行协调周期 reconcile cycle。为此,可以使用 EnqueueRequestForOwner handler,按照如下方式完成:
err = c.Watch(&source.Kind{Type: &examplev1alpha1.MyControlledType{}}, &handler.EnqueueRequestForOwner{})
  • 另一种不太常用的情况是将一个 events 传播到多个资源上。考虑一种情况,一个控制器注入了 TLS secret 的路由,同一个命名空间中的多个路由可以指向同一个 secret。如果 secret 发生了改变,需要更新所有路由。因此,需要在 secret 类型上创建一种 watch 机制,处理程序如下所示:
type enqueueRequestForReferecingRoutes struct {
        client.Client
}

// trigger a router reconcile event for those routes that reference this secret
func (e *enqueueRequestForReferecingRoutes) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
        routes, _ := matchSecret(e.Client, types.NamespacedName{
                Name:      evt.Meta.GetName(),
                Namespace: evt.Meta.GetNamespace(),
        })
        for _, route := range routes {
                q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
                        Namespace: route.GetNamespace(),
                        Name:      route.GetName(),
                }})
        }
}

// Update implements EventHandler
// trigger a router reconcile event for those routes that reference this secret
func (e *enqueueRequestForReferecingRoutes) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
        routes, _ := matchSecret(e.Client, types.NamespacedName{
                Name:      evt.MetaNew.GetName(),
                Namespace: evt.MetaNew.GetNamespace(),
        })
        for _, route := range routes {
                q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
                        Namespace: route.GetNamespace(),
                        Name:      route.GetName(),
                }})
        }
}

三、资源 Reconciliation Cycle

  • 协调周期 reconcile cycle 是在被 watch 的 event 传递后框架将控制权转交给我们地方。正如之前所解释的,在该 reconcile cycle 中没有获得相关时间类型的信息,是因为是基于水平触发的方式来工作。
  • 如下是一个管理 CRD 控制器的常见 reconcile cycle 的模型,和其他任何一个模型一样,它不会反映任何特定用例,但希望它将有助于解决编写 operator 时遇到的问题:

在这里插入图片描述

  • 从上图中可以看到,主要步骤是:
    • 检索感兴趣的 CR 实例;
    • 确认实例的有效性,不会在不合法实例上做任何事情;
    • 初始化实例:如果实例的某些值没有被初始化,会在这一步进行处理;
    • 判断实例的 deletion 状态,如果实例正在被删除,也需要做一些特殊的清理。
  • 管理控制器的业务逻辑,如果以上步骤均通过,最终可以管理和执行该实例的 reconcile 逻辑,这个逻辑每个控制器都不尽相同。

四、资源验证

  • 这里存在两种类型的校验:
    • 语法校验:通过定义 OpenAPI 规则来验证;
    • 语义校验:可以通过创建 ValidatingAdmissionConfiguration 来完成。
  • 注意:在控制器中不能校验 CR 合法性,一旦 CR 被 API Server 接受了,它就会存在 Etcd 中,CR 存在 Etcd 之后,管理该 CR 资源的控制器就无法拒绝它,如果这个 CR 是不合法的,控制器在尝试使用或处理它的时候将会发生错误。
  • 推荐:由于不能保证 ValidatingAdmissionConfiguration 被创建或正常工作,还是应该在控制器内部去验证 CR,如果 CR 不合法,应该避免创建无限错误循环。

① 语法校验

  • 可以按照Generating CRD的描述添加 OpenAPI 验证规则。
  • 推荐:尽可能多地为自定义资源模型进行语法校验,尽量使用语法校验,因为它相对简单,并且可以防止格式错误的 CR 存储在 etcd 中。

② 语义校验

  • 语义校验是为了确保字段具有合理的值,从而使整个资源记录是有意义的。语义验证业务逻辑取决于 CR 所代表的概念,并且必须由 operator 的开发人员进行编码实现。
  • 如果给定的 CR 需要语义校验,那么 operator 需要暴露一个 webhook,作为 operator deploymen 的一部分,ValidatingAdmissionConfiguration 也应该被创建。
  • 目前存在的局限性:
    • 在 OpenShift 3.11 中,ValidatingAdmissionConfigurations 还处于技术预览阶段(将从 4.1 开始支持);
    • Operator SDK 不支持脚手架形式的 webhook,可以使用 kubebuilder 来进行实现:
kubebuilder webhook --group crew --version v1 --kind FirstMate --type=mutating --operations=create,update

③ 验证控制器中的资源

  • 最好的方式是直接拒绝一个无效的 CR,而不是接受并保存在 Etcd 中,然后对它进行错误条件处理。当然也有可能的情况是,ValidatingAdmissionConfiguration 并没有被部署或者根本不可用,因此在控制器代码中进行语义校验仍然是一个很好的做法,应该做到的是,可以在 ValidatingAdmissionConfiguration 和控制器之间共享这部分结构化的代码。
  • 控制器中调用验证方法的代码如下所示:
if ok, err := r.IsValid(instance); !ok {
    return r.ManageError(instance, err)
}
  • 请注意,如果验证失败,按照错误管理部分中的描述来管理这个错误。IsValid 函数如下:
func (r *ReconcileMyCRD) IsValid(obj metav1.Object) (bool, error) {
    mycrd, ok := obj.(*examplev1alpha1.MyCRD)
 // validation logic
}

五、资源初始化

  • Kubernetes 的一个很好的惯例是用户只初始化他所需要的资源字段,其他的可以省略。但从编码人员和调试者的角度来说,实际上最好将所有的字段都初始化,这允许在编码的时候不必总是去校验字段是否被定义了,并且可以轻松地排除错误情况。
  • 为了初始化资源,这里有两个选项:
    • 在控制器中定义初始化方法;
    • 定义一个 MutatingAdmissionConfiguration(类似于 ValidatingAdmissionConfiguration 的程序);
  • 在控制器中定义一个初始化方法,代码应类似于此示例:
if ok := r.IsInitialized(instance); !ok {
    err := r.GetClient().Update(context.TODO(), instance)
    if err != nil {
        log.Error(err, "unable to update instance", "instance", instance)
        return r.ManageError(instance, err)
    }
    return reconcile.Result{}, nil
}
  • 如果 IsInitialized 方法的结果返回 true,更新 instance 并 return,这将会立即出发另一个 reconcile cycle,第二次调用 IsInitialized 方法将会返回 false,代码逻辑将会执行到下一部分。

① 资源 Finalization

  • 如果资源不属于操作员控制的 CR,但在删除该 CR 时需要采取措施,必须使用 finalizer。终结器提供了一种机制来通知 Kubernetes 控制平面,在执行标准 Kubernetes 垃圾收集逻辑之前需要执行一个操作。资源可以有一个或多个 finalizers,每一个控制器应该管理自己的 finalizer 并且忽略其他的。
  • 管理 finalizers 的伪代码算法:
    • 如果需要,在初始化方法中添加 finalizer。
    • 当资源被删除,检查此控制器拥有的 finalizer 是否存在。
      • 清理成功,移除 finalizer 并更新 CR;
      • 如果失败决定是重试还是放弃并可能留下垃圾(在某些情况下这是可以接受的);
      • 如果不存在,直接 return;
      • 如果存在,执行如下清理逻辑:如果清理逻辑需要添加额外的资源,需要记住的是,无法在正在删除的命名空间中创建其他资源,删除命名空间将会触发 finalizer 并删除其下所有资源。
  • 代码如下所示:
if util.IsBeingDeleted(instance) {
    if !util.HasFinalizer(instance, controllerName) {
        return reconcile.Result{}, nil
    }
    err := r.manageCleanUpLogic(instance)
    if err != nil {
        log.Error(err, "unable to delete instance", "instance", instance)
        return r.ManageError(instance, err)
    }
    util.RemoveFinalizer(instance, controllerName)
    err = r.GetClient().Update(context.TODO(), instance)
    if err != nil {
        log.Error(err, "unable to update instance", "instance", instance)
        return r.ManageError(instance, err)
    }
    return reconcile.Result{}, nil
}

② 资源所有权

  • 资源所有权是 Kubernetes 中的原生概念,它决定了资源如何被删除。默认情况下,当一个资源被删除的时候,它的子资源也也会被删除(可以设置 cascade=false 来关闭这种行为)。这种行为有助于确保资源的正确垃圾收集,尤其是当资源控制多级层次结构中的其他资源时(deployment-> repilcaset->pod)。
  • 建议:如果控制器创建资源并且它的生命周期与其他资源(kubernetes 核心资源或其他 CR)有关联,那么应该将此资源设置为其他资源的所有者,如下所示:
controllerutil.SetControllerReference(owner, obj, r.GetScheme())
  • 有关所有权的其他规则如下:
    • 父子资源必须位于同一命名空间中;
    • 命名空间资源可以拥有集群资源,一个对象可以有一个所有者列表,如果多个命名空间对象拥有相同的集群资源,则每个对象都应声明所有权,而不会覆盖其他对象的所有权;
    • 集群资源不能拥有命名空间资源;
    • 集群资源可以拥有另外一个集群资源。

六、状态管理

  • Status 是资源的一个标准部分,被用于报告资源的状态。在这里将使用 status 报告最后一次执行协调循环的结果,也可以在 Status 中添加更多的信息。
  • 在正常情况下,如果每次执行 reconcile cycle 的时候都要更新资源,这将触发更新时间,进而导致无限触发 reconcile cycle。因此,正如上面描述的那样,应该把 Status 作为子资源。使用这种方法,能够不增加 ResourceGeneration 元数据域的情况下更新资源的状态。
  • 使用如下命令更新状态:
err = r.Status().Update(context.Background(), instance)
  • 现在需要为 watch 机制写一个 predicate,用来丢弃不增加 ResourceGeneration 的更新事件,可以使用 GenerationChangePredicate 来完成此功能。上文提到过,在使用 finalizer 的时候,应该在初始化的时候设置,如果 finalizer 是初始化的唯一项,由于它是元数据项的一部分,因此 ResourceGeneration 不会递增。
  • 为了说明该用例,以下是 predicate 的修改版本:
type resourceGenerationOrFinalizerChangedPredicate struct {
 predicate.Funcs
}

// Update implements default UpdateEvent filter for validating resource version change
func (resourceGenerationOrFinalizerChangedPredicate) Update(e event.UpdateEvent) bool {
 if e.MetaNew.GetGeneration() == e.MetaOld.GetGeneration() && reflect.DeepEqual(e.MetaNew.GetFinalizers(), e.MetaOld.GetFinalizers()) {
  return false
 }
 return true
}
  • 假设 status 如下所示:
type MyCRStatus struct {
 // +kubebuilder:validation:Enum=Success,Failure
 Status     string      `json:"status,omitempty"`
 LastUpdate metav1.Time `json:"lastUpdate,omitempty"`
 Reason     string      `json:"reason,omitempty"`
}
  • 可以写一个函数来管理并保证 reconcile cycle 成功执行:
func (r *ReconcilerBase) ManageSuccess(obj metav1.Object) (reconcile.Result, error) {
 runtimeObj, ok := (obj).(runtime.Object)
 if !ok {
  log.Error(errors.New("not a runtime.Object"), "passed object was not a runtime.Object", "object", obj)
  return reconcile.Result{}, nil
 }
 if reconcileStatusAware, updateStatus := (obj).(apis.ReconcileStatusAware); updateStatus {
  status := apis.ReconcileStatus{
   LastUpdate: metav1.Now(),
   Reason:     "",
   Status:     "Success",
  }
  reconcileStatusAware.SetReconcileStatus(status)
  err := r.GetClient().Status().Update(context.Background(), runtimeObj)
  if err != nil {
   log.Error(err, "unable to update status")
   return reconcile.Result{
    RequeueAfter: time.Second,
    Requeue:      true,
   }, nil
  }
 } else {
  log.Info("object is not RecocileStatusAware, not setting status")
 }
 return reconcile.Result{}, nil
}

七、错误管理

  • 如果控制器进入了一个错误条件,并且在 reconcile 方法中返回了一个错误,operator 将会打印错误日志到标准输出,reconlie event 将会立即再次调度(默认的调度器实际上应该检测是否一遍又一遍地出现相同的错误,并增加相应的调度时间,以经验看来,这并没有发生)。如果错误一直存在,那么也将永远存在错误循环,而且这个错误条件对用户来说是不可见的。
  • 有两种方法可以通知用户发生了错误,它们可以同时使用:
    • 在对象的 status 字段中返回错误;
    • 生成一个 event 描述错误。
  • 此外,如果错误能够自解决,应该在一段周期时间后重新调度 reconcile cycle。通常来说,周期时间是呈指数增长的,因此在每次迭代中,reconcile event 周期会越来越长(例如每次增长时间量的两倍)。
  • 现在构建状态管理来处理错误条件:
func (r *ReconcilerBase) ManageError(obj metav1.Object, issue error) (reconcile.Result, error) {
    runtimeObj, ok := (obj).(runtime.Object)
    if !ok {
        log.Error(errors.New("not a runtime.Object"), "passed object was not a runtime.Object", "object", obj)
        return reconcile.Result{}, nil
    }
    
    var retryInterval time.Duration
    r.GetRecorder().Event(runtimeObj, "Warning", "ProcessingError", issue.Error())
    if reconcileStatusAware, updateStatus := (obj).(apis.ReconcileStatusAware); updateStatus {
        lastUpdate := reconcileStatusAware.GetReconcileStatus().LastUpdate.Time
        lastStatus := reconcileStatusAware.GetReconcileStatus().Status
        status := apis.ReconcileStatus{
            LastUpdate: metav1.Now(),
            Reason:     issue.Error(),
            Status:     "Failure",
        }

        reconcileStatusAware.SetReconcileStatus(status)
        err := r.GetClient().Status().Update(context.Background(), runtimeObj)
        if err != nil {
            log.Error(err, "unable to update status")
            return reconcile.Result{
                RequeueAfter: time.Second,
                Requeue:      true,
            }, nil
        }

        if lastUpdate.IsZero() || lastStatus == "Success" {
            retryInterval = time.Second
        } else {
            retryInterval = status.LastUpdate.Sub(lastUpdate).Round(time.Second)
        }
    } else {
        log.Info("object is not RecocileStatusAware, not setting status")
        retryInterval = time.Second
    }

    return reconcile.Result{
        RequeueAfter: time.Duration(math.Min(float64(retryInterval.Nanoseconds()*2), float64(time.Hour.Nanoseconds()*6))),
        Requeue:      true,
    }, nil
}
  • 注意,此函数会立即发送一个 event,然后使用错误条件更新状态,最后计算何时重新安排下一次 reconcile,该算法尝试将每个循环的时间加倍,最多到六个小时为止。六个小时是一个很好的上限时间,因为 event 大约持续 6 个小时,所以这应该确保始终有一个活动 event 描述当前的错误情况。

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

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

相关文章

HiveSql语法优化二 :join算法

Hive拥有多种join算法,包括Common Join,Map Join,Bucket Map Join,Sort Merge Buckt Map Join等,下面对每种join算法做简要说明: Common Join Common Join是Hive中最稳定的join算法,其通过一个M…

selenium+xpath爬取二手房标题

贝壳找房标题爬取需要注意的是,在页面中间有一个小广告 而他就在ul的li下面,当我们进行title所以输出时,会报错。 所以在进行页面解析之前必须把广告叉掉,不然也把广告那一部分的li给爬取下来了 所以,我们&#xff0…

听GPT 讲Rust源代码--src/tools(13)

File: rust/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs 在Rust源代码中,路径为rust/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs的文件是为了处理Rust代码中的不一致实现问题而存在的。…

微服务实战系列之ZooKeeper(中)

前言 昨日博主的第一篇ZooKeeper,对它自身具备的能力做了初步介绍。书接上文,马不停蹄,我们继续挖掘它内在的美,充分把握它的核心与脉络。 揭秘ZooKeeper Q:集群一致性协同是如何进行的 我们讲到分布式,…

【MySQL】启动 和 连接 MySQL

启动停止 mysql安装成功后在cmd输入 net start mysql80 //启动 net stop mysql80 //停止 mysql连接 方式1. 通过客户端去连接 方式2.使用cmd去连接 描述:-u是指定 用户 -p是指定密码 mysql -u root -p password

Github 2023-12-16开源项目日报Top10

根据Github Trendings的统计,今日(2023-12-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目2非开发语言项目2TypeScript项目1Jupyter Notebook项目1Go项目1PHP项目1JavaScript项目1C#项目1 精…

重置BIOS设置的方法通常有两种,可以通过BIOS菜单,也可也通过CMOS

你的计算机将系统时间和硬件设置等低级设置存储在CMOS中。这些设置在BIOS或UEFI设置菜单中进行配置。如果你遇到硬件兼容性问题或其他问题,你可能需要尝试清除CMOS。 清除CMOS会将BIOS或UEFI设置重置回出厂默认状态。在大多数情况下,你可以从BIOS菜单或按下背面的CMOS按钮来…

基于ssm日用品网站设计论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本日用品网站就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息&…

【MySQL】(DDL) 表操作-查询

查询: show tables ; //查询所有表名称 desc 表名称 ; //查询表结构 show create table 表名称; //查看创建表语句 create table 表名 ( 字段名1 字段类型1,字段名2 字段类型2) ; //创建表结构 示列: 1. show tables; use 数据库名; show tables …

时序预测 | Python实现LSTM电力需求预测

时序预测 | Python实现LSTM电力需求预测 目录 时序预测 | Python实现LSTM电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较。使用…

【Spring】08 BeanNameAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点,其中之一就是 Bean 生命周期中的回调接口。本文将聚焦于其中的一个接口 BeanNameAware,介绍它的作…

云原生之深入解析使用Telepresence轻松在本地调试和开发Kubernetes应用程序

一、 准备 telepresence 下载:https://www.telepresence.io/docs/latest/install/kubectl 下载:https://kubernetes.io/docs/tasks/tools/ 二、版本检测 $telepresence version Client: v2.5.3 (api v3) Root Daemon: not running User Daemon: not r…

【LeetCode刷题】-- 161.相隔为1的编辑距离

161.相隔为1的编辑距离 方法:一次遍历 首先,我们要确认字符串的长度不会相差太远。如果长度差了2个或更多字符,那么 s 和 t 就不可能是一次编辑之差的字符串。 接下来,我们假设 s 的长度总是短于或等于 t 的长度。如果不是这样&…

人工智能计算机视觉:解析现状与未来趋势

导言 随着人工智能的迅速发展,计算机视觉技术逐渐成为引领创新的关键领域。本文将深入探讨人工智能在计算机视觉方面的最新进展、关键挑战以及未来可能的趋势。 1. 简介 计算机视觉是人工智能的一个重要分支,其目标是使机器具备类似于人类视觉的能力。这…

Bifrost 中间件 X-Requested-With 系统身份认证绕过漏洞复现

0x01 产品简介 Bifrost是一款面向生产环境的 MySQL,MariaDB,kafka 同步到Redis,MongoDB,ClickHouse等服务的异构中间件 0x02 漏洞概述 Bifrost 中间件 X-Requested-With 存在身份认证绕过漏洞,未经身份认证的攻击者可未授权创建管理员权限账号,可通过删除请求头实现身…

gitee(ssh)同步本地

一、什么是码云 gitee Git的”廉价平替” > 服务器在国内,运行不费劲 在国内也形成了一定的规模 git上的一些项目插件等在码云上也可以找得到 二、创建仓库 三、删除仓库 四、仓库与本地同步 > 建立公钥 五、把仓库同步到本地 六、在本地仓库中创建vue项目…

Grafana Loki 快速尝鲜

Grafana Loki 是一个支持水平扩展、高可用的聚合日志系统,跟其他的聚合日志系统不同,Loki只对日志的元数据-标签进行索引,日志数据会被压缩并存储在对象存储中,甚至可以存储在本地文件系统中,能够有效降低成本&#xf…

oracle DG 三种应用机制

首先理解不管是哪种机制,oracle都不是从主库直接传归档文件到备库,而是通过网络将主库的redo数据传输到备库: 1、普通DG是主库发生日志切换,备库把接收到的redo数据在备库通过归档进程生成为归档文件进行应用 2、ADG则是备库把接收…

VRRP协议

一.基本概念 1.概念 VRRP能够在不改变组网的情况下,将多台路由器虚拟成一个虚拟路由器,通过配置虚拟路由器的IP地址为默认网关,实现网关的备份。协议版本:VRRPv2(常用)和VRRPv3:VRRPv2仅适用于…

netty-daxin-4(httpwebsocket)

文章目录 http服务端NettyHttpServerHelloWorldServerHandler 客户端ApiPost http 服务端 NettyHttpServer 可参考:GitHub上netty项目中的example包中的代码 import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.net…