您自己的Kubernetes控制器-用Java开发

一篇文章中 ,我们为创建自己的自定义Kubernetes控制器奠定了基础。 我们详细介绍了控制器是什么,它的唯一要求是能够与HTTP / JSON进行通信。 在本文中,我们将最终开始开发它。

技术堆栈可以是Python,NodeJS或Ruby。 由于此博客的名称为“ A Java Geek”,因此选择Java是正常的。

作为一个用例,我们将实现sidecar模式:每当对pod进行调度时,也会随之调度sidecar pod。 如果将前者删除,则后者也必须删除。

选择合适的工具

为了用Java执行REST调用,首先需要生成绑定。 有几种方法可以得到这些:。 最繁琐的操作是手动完成:一个需要仔细掌握所有可能的JSON请求和响应组合,开发相应的Java对象,选择JSON序列化框架以及HTTP客户端。 。 第二个最佳选择是使用专有代码生成器,例如SwaggerApiary 。 这就要求API提供程序以一种可能的格式提供模型。 不利的一面是,需要使用相关工具。 有时,格式或多或少是开放的,例如OpenAPI规范 。 在这种情况下,可以从实现该格式的工具中选择该工具。 。 在最好的情况下,已经提供了绑定。

Kubernetes就是这种情况:该项目为各种语言提供了自己的绑定 。 问题在于语言包装器与REST API非常接近,太接近我的口味了。 例如,这是列出所有名称空间中所有pod的方式:

ApiClientclient=Config.defaultClient();
CoreV1Apicore=newCoreV1Api(client);
V1PodListpods=
    core.listPodForAllNamespaces(null,null,null,null,null,null,null,null); (1)
  1. 注意所有需要传递的null参数

这就是“包装器代码非常接近REST API”的意思。幸运的是,还有另一个选择。Fabric8组织在Github上提供了流畅的Java API。与上述代码等效的代码是:

KubernetesClientclient=newDefaultKubernetesClient();
PodListpods=client.pods().inAnyNamespace().list(); (1)
  1. 流利,无需传递无用的null参数

Fabric8快速概述

简而言之,使用Fabric8的API,所有Kubernetes资源都可以在KubernetesClient实例上使用, 例如

  • client.namespaces()
  • client.services()
  • client.nodes()
  • 等等

根据资源的性质,它可以由名称空间限定范围-或不可以:

  • client.pods().inAnyNamespace()
  • client.pods().inNamespace("ns")

此时,可以调用动词:

列出所有命名空间中的所有Pod
client.pods().inAnyNamespace().list();
删除名称空间ns中的所有pod
client.pods().delete(client.pods().inNamespace("ns").list().getItems());
创建一个名为ns的新名称空间
client.namespaces()
  .createNew()
    .withApiVersion("v1")
    .withNewMetadata()
      .withName("ns")
    .endMetadata()
  .done();

实施控制回路

请记住,Kubernetes控制器只是一个控制循环,它监视集群的状态,并将其与所需状态进行协调。 为了了解调度/删除事件,需要Observer模式。 应用程序将订阅此类事件,并且相关回调将在发生时触发。

此类图是API的非常简化的图:

Watcher简化的类图

实际实现观察者只是以下几行的事情:

publicclassDummyWatcherimplementsWatcher<Pod>{

  @Override
  publicvoideventReceived(Actionaction,Podpod){
    switch(action){
      caseADDED: (1)
        break;
      caseMODIFIED: (2)
        break;
      caseDELETED: (3)
        break;
      caseERROR: (4)
        break;
    }
  }

  @Override
  publicvoidonClose(KubernetesClientExceptioncause){
                              (5)
  }
}

client.pods()
  .inAnyNamespace()
  .watch(DummyWatcher());
  1. 添加新广告连播时起作用
  2. 在修改现有广告连播时采取行动
  3. 删除广告连播时采取行动
  4. 发生错误时采取行动
  5. 清理任何资源。 如果客户端正确关闭, cause将为null

细腻的细节

至此,我们拥有了实现sidecar模式所需的一切。 我不会显示完整的代码- 可以在GitHub上找到 ,但是需要强调一些关键的事情。

标记边车

从本质上讲,观察者需要在添加新的吊舱时添加一个Sidecar吊舱,并在移除它时删除该Sidecare。 这种基本方法行不通:如果安排了Sidecar吊舱,则将触发观察者,并且它将向Sidecar添加新的Sidecar吊舱。 而且这种情况还会持续下去......因此,``标记''小汽车吊舱至关重要。 当检测到此类Pod时,不应触发创建逻辑。

有几种标记边车吊舱的方法:

  • 在sidecar pod的名称后加上特定的字符串, 例如 sidecar
  • 添加特定标签:
    client.pods()
      .inNamespace("ns")
      .createNew()
        .withNewMetadata()
          .addToLabels("sidecar","true")
        .endMetadata()
      .done();

连同吊舱一起卸下边车

吊舱应只有一个侧车。 如上所述,应在添加Pod时创建它,而在删除后者时应删除它。

因此,应将对主吊舱的引用添加到侧车中。 这样,当吊舱被删除时-当它不是小杂物时,我们应该找到分配的小杂物并将其删除。

第一种幼稚的方法是在删除主吊舱时显式删除边车。 但是,这是一项繁重的工作,花了很多时间。 Kubernetes允许将Pod的生命周期绑定到另一个Pod的生命周期。 然后,删除逻辑由Kubernetes本身处理。 这由ownerReference的概念ownerReference

该API使其易于实现:

client.pods()
  .inNamespace("ns")
  .createNew()
    .withNewMetadata()
      .addNewOwnerReference()
        .withApiVersion("v1")
        .withKind("Pod")
        .withName(podName)
        .withUid(pod.getMetadata().getUid())
      .endOwnerReference()
    .endMetadata()
  .done();

始终保持边车

添加Sidecar并不意味着它将永远保持这种方式。 例如,可以删除属于部署的吊舱。 部署的目标是重新创建一个Pod,以达到所需的副本数。

同样,如果在保留主吊舱的同时删除了边车,则应使用正确的自己的引用生成新的边车。

结论

在这篇文章中,我们描述了如何在JVM上使用Java语言实现Kubernetes控制器。 借助Fabric8的API,它变得非常简单。 主要问题来自调度/删除逻辑中的极端情况。 在本系列的下一篇也是最后一篇文章中,我们将最终看到如何部署和运行代码。

可以在Github上以Maven格式找到此文章的完整源代码。

翻译自: https://blog.frankel.ch/your-own-kubernetes-controller/2/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值