用于基于JVM的应用程序的Typesafe Kubernetes清单DSL

在生产环境中管理Docker / Rocket容器(或其他任何东西)的群集充满了分布式系统的挑战。 幸运的是,围绕Kubernetes项目的一个引人入胜且非常活跃的社区正在利用Google,Red Hat和初创公司的多年经验为这些挑战提供指导,以应对这些挑战。 如果您还没有尝试过Kubernetes来管理Docker容器,那么就应该继续使用它!

在Fabric8社区中,我们正在围绕Kubernetes和企业Kubernetes / OpenShift平台以及在其之上进行开发人员体验。 我们有一个很棒的Web控制台用于管理Kubernetes集群,与集群进行交互的库集(包括具有类型安全DSL的kubernetes-client) , 开箱即用的Jenkins Workflow 一键式CI / CD支持, Helm .sh打包应用程序,API管理,混乱的猴子和许多其他东西。 请访问fabric8.io网站以获取更多信息。 Fabric8从较高的层次看似具有JVM倾向,但它不是Java特定的,可以应用于golang / node / python / any Language。 请随时加入并做出贡献!

与使用Kubernetes / OpenShift的客户合作时遇到的一个挑战是,目前没有任何好的工具可以生成Kubernetes JSON / YAML清单文件或自定义现有清单文件。 JSON(甚至YAML)对于手动编辑冒险来说很容易出错,因此我们需要更好的东西。

如果您是Java / JVM开发人员,那么您很幸运。 fabric8社区有一个很棒的类型安全DSL,用于自动生成Kubernetes清单文件 。

这是fluent API构建器外观的示例:

@KubernetesProvider
public KubernetesList create() {
  return new KubernetesListBuilder()
    .addNewReplicationControllerItem()
      .withNewMetadata()
        .withName("Hello-Controller")
      .endMetadata()
      .withNewSpec()
        .withReplicas(1)
        .addToSelector("component", "my-component")
        .withNewTemplate()
          .withNewSpec()
            .addNewContainer()
              .withName("my-container")
              .withImage("my/image")
            .endContainer()
          .endSpec()
        .endTemplate()
      .endSpec()
    .endReplicationControllerItem()
    .build();

在此博客中,我们将探讨其功能以及如何与fabric8 maven插件结合使用,使通过Kubernetes清单文件更好地管理和与Kubernetes API交互。 目的是要动手操作,因此请随时关注,或者如果您急于查看示例,请找到示例github repo

创建一个新项目

Fabric8 有一些快速入门和mvn原型 ,可以帮助您入门。 我们将从从maven项目创建一个项目开始,从中我们可以演示kubernetes typesafe dsl:

mvn原型:generate -DarchetypeGroupId = io.fabric8.archetypes -DarchetypeArtifactId = vertx-simplest-archetype -DarchetypeVersion = 2.2.93

按照交互式提示填写groupId / artifactId等。然后确保该项目可以使用mvn clean install构建

完成mvn构建后,您应该在target/classes目录中看到已生成kubernetes.jsonkubernetes.yml文件。 看一下kubernetes.json文件:

{
    "apiVersion" : "v1",
    "items" : [ {
      "apiVersion" : "v1",
      "kind" : "Service",
      "metadata" : {
        "annotations" : { },
        "labels" : {
          "container" : "java",
          "component" : "typesafe-kubernetes-dsl",
          "provider" : "fabric8",
          "project" : "typesafe-kubernetes-dsl",
          "version" : "1.0-SNAPSHOT",
          "group" : "quickstarts"
        },
        "name" : "typesafe-kubernetes-dsl"
      },
      "spec" : {
        "deprecatedPublicIPs" : [ ],
        "externalIPs" : [ ],
        "ports" : [ {
          "port" : 80,
          "protocol" : "TCP",
          "targetPort" : 8080
        } ],
        "selector" : {
          "container" : "java",
          "project" : "typesafe-kubernetes-dsl",
          "component" : "typesafe-kubernetes-dsl",
          "provider" : "fabric8",
          "group" : "quickstarts"
        },
        "type" : "LoadBalancer"
      }
    }, {
      "apiVersion" : "v1",
      "kind" : "ReplicationController",
      "metadata" : {
        "annotations" : { },
        "labels" : {
          "container" : "java",
          "component" : "typesafe-kubernetes-dsl",
          "provider" : "fabric8",
          "project" : "typesafe-kubernetes-dsl",
          "version" : "1.0-SNAPSHOT",
          "group" : "quickstarts"
        },
        "name" : "typesafe-kubernetes-dsl"
      },
      "spec" : {
        "replicas" : 1,
        "selector" : {
          "container" : "java",
          "component" : "typesafe-kubernetes-dsl",
          "provider" : "fabric8",
          "project" : "typesafe-kubernetes-dsl",
          "version" : "1.0-SNAPSHOT",
          "group" : "quickstarts"
        },
        "template" : {
          "metadata" : {
            "annotations" : { },
            "labels" : {
              "container" : "java",
              "component" : "typesafe-kubernetes-dsl",
              "provider" : "fabric8",
              "project" : "typesafe-kubernetes-dsl",
              "version" : "1.0-SNAPSHOT",
              "group" : "quickstarts"
            }
          },
          "spec" : {
            "containers" : [ {
              "args" : [ ],
              "command" : [ ],
              "env" : [ {
                "name" : "KUBERNETES_NAMESPACE",
                "valueFrom" : {
                  "fieldRef" : {
                    "fieldPath" : "metadata.namespace"
                  }
                }
              } ],
              "image" : "fabric8/typesafe-kubernetes-dsl:1.0-SNAPSHOT",
              "name" : "typesafe-kubernetes-dsl",
              "ports" : [ ],
              "securityContext" : { },
              "volumeMounts" : [ ]
            } ],
            "imagePullSecrets" : [ ],
            "nodeSelector" : { },
            "volumes" : [ ]
          }
        }
      }
    } ],
    "kind" : "List"
  }

怎么会这样?

fabric8-maven-插件

Kubernetes对象的类型安全,无json声明的第一个选项是通过mvn属性使用fabric8-maven-plugin的配置。 如果查看maven pom.xml的部分,您将看到fabric8-maven-plugin用于帮助自动生成kubernetes.json的一些配置:


  fabric8/java-jboss-openjdk8-jdk:1.0.10
  fabric8/
  ${fabric8.dockerUser}${project.artifactId}:${project.version}
  
  ${project.artifactId}
  java
  quickstarts
  vertx
  
  
  ${project.artifactId}
  80
  8080
  LoadBalancer

好极了! 填写简单的属性以使我们的kubernetes最完美,并且它(和所有值)都是最完美的一部分! 您还可以通过属性为maven插件指定环境变量和OpenShift模板属性。 查看有关更多信息的文档以及可用于配置清单文件生成的特定属性。

但是,您可能会注意到,只有最常用的构造(服务,复制控制器,…和服务帐户)才在maven插件中具有有用的属性。 这应该可以使我们达到80%。 但是,如果我们要添加/自定义作为该mvn插件的一部分生成的kubernetes.json文件,该怎么办? 或者,如果我们有手工制作但想要类型安全编辑的自己的kubernetes.json文件,该怎么办? 或者,如果我们只想使用类型安全的方式从头开始生成它100%,该怎么办?

类型安全DSL

我们可以使用kubernetes-generator实现来完成此任务,该实现基本上是一个Java注释处理器工厂,我们使用它来生成/增强kubernetes.json / yml文件。 (注意,对于yml生成并指定显式文件名,您将需要使用fabric8版本2.2.89或更高版本,否则将强制采用json和kubernetes.json的文件名)。

将以下内容添加到您的Maven pom.xml


    io.fabric8
    kubernetes-generator
  

对于此示例,假设我们要将持久卷详细信息添加到我们的Kubernetes.json / yml文件中。 这样做的关键是简单地创建一个POJO并使用@KubernetesModelProcessor对其进行注释,如下所示:

@KubernetesModelProcessor
public class PersistentVolumeKubernetesModelProcessor {
    
}

现在,在该新类中,我们可以修改或向kubernetes.json / yml文件添加新组件。 我们通过遵循“访客”模式来做到这一点。 在我们遍历Kubernetes最完整文件中的对象并将其提供给您的方法以供您根据需要进行操作时,请考虑一下。 实际上,尽管这是在后台发生的事情,但是如果您不愿意,您不会伪造处理Kubernetes清单中的每个对象。 您只需处理/扩展/增强您感兴趣的对象。 为此,您可以指定方法的参数以采用某些类型的对象(即,您感兴趣的对象)。 例如,如果您的资源列表具有一个ReplicationController,并且您想为其模板规范添加更多Pod,则可以这样声明您的方法:

public void on(ReplicationControllerSpecBuilder builder) {
          
  }

注意参数类型。 这样,我们只能挑选出要操作的模型的特定部分。 同样,如果您只想PodSpec:

public void on(PodSpecBuilder builder) {

  }

一些有用的构建器对象:

  • KubernetesListBuilder
  • ReplicationControllerBuilder
  • ReplicationControllerSpecBuilder
  • PodSpecBuilder
  • ServiceSpecBuilder
  • IngressRuleBuilder
  • PersistentVolumeBuilder
  • DaemonSetBuilder

OpenShift的一些有用的构建器对象:

  • TemplateBuilder
  • RouteBuilder
  • OAuthAccessTaskBuilder
  • OAuthClientBuilder
  • 项目构建器
  • 部署策略构建器

在我们的示例项目中,我们将使用以下实现将一组持久卷,声明和安装添加到我们现有的kubernetes清单资源中:

public void on(KubernetesListBuilder builder){
        builder.addNewPersistentVolumeClaimItem()
                .withNewMetadata()
                  .withName("typesafe-dsl-pv")
                  .addToLabels("provider", "fabric8")
                  .addToLabels("project", "typesafe-dsl")
                  .addToLabels("group", "demo")
                .endMetadata()
                .withNewSpec()
                  .withAccessModes("ReadWriteOnce")
                  .withResources(getResourceRequirement())
                .endSpec()
                .endPersistentVolumeClaimItem()
                .build();
  }

记下如何使用句子结构(特定于域的语言)将流畅的DSL链接在一起。 在上面的代码片段中,“访问KubernetesListBuilder,并添加一个新的PersistentVolumeClaim对象并指定标签,访问模式和资源(有关如何计算资源,请参见完整的源代码)。

现在,我们需要在我们的kubernetes清单中添加volume / volume mount配置。 对于现有资源,我们将像这样编辑现有资源描述(并且还将通过名称选择特定的ContainerBuilder!)

public void withPodTemplate(PodTemplateSpecBuilder builder) {
        builder.withSpec(builder.getSpec())
                .editSpec()
                  .addNewVolume()
                    .withName("typesafe-kubernetes-dsl-volume")
                    .withPersistentVolumeClaim(getPersistentVolumeClaimSource())
                   .endVolume()
               .endSpec()
               .build();
    }

    private PersistentVolumeClaimVolumeSource getPersistentVolumeClaimSource() {
        PersistentVolumeClaimVolumeSource rc = new PersistentVolumeClaimVolumeSource("typesafe-kubernetes-dsl-pvc", false);
        return rc;
    }

    @Named("typesafe-kubernetes-dsl")
    public void withVolumeMounts(ContainerBuilder builder) {
        builder.withVolumeMounts(new VolumeMount("/deployments/target/placeorder", "typesafe-kubernetes-dsl-volume", false))
                .build();
    }
    }

现在,当您执行mvn全新安装时,您应该看到kubernetes.json / yml已通过添加永久卷,卷挂载等进行了正确修改。漂亮的DSL,是吗?

从头开始生成Kubernetes DSL

您还可以使用此类型安全DSL生成kubernetes JSON / YML。 如果您不想使用mvn插件(即您正在使用gradle,sbt等),这将非常有用。 为此,我们用@KubernetesProvider注释方法 ,并使用与以前相同的构建器对象。 例如,在我们的上述项目中,我们创建了持久卷声明并将其添加到我们的kubernetes清单文件中。 用户可以将那些应用到他们的项目中,但是用户通常不管理后台PersistentVolumes,而构建/发行版或集群/项目管理员可能会这样做。 因此,将PersistentVolume元数据分离到其自己的清单文件中并分别交付是有意义的。

@KubernetesProvider("typesafe-kubernetes-dsl-pv.yml")
    public KubernetesList buildList() {
        return new KubernetesListBuilder().addNewPersistentVolumeItem()
                .withNewMetadata()
                    .withName("typesafe-kubernetes-dsl-pv")
                    .addToLabels("provider", "fabric8")
                    .addToLabels("project", "typesafe-kubernetes-dsl")
                    .addToLabels("group", "demo")
                .endMetadata()
                .withNewSpec()
                    .addToCapacity("storage", new Quantity("100Ki"))
                    .addToAccessModes("ReadWriteOnce")
                    .withHostPath(new HostPathVolumeSource("/home/vagrant/camel"))
                .endSpec()
                .endPersistentVolumeItem()
                .build();
    }

现在,当您运行Maven构建时,应该会看到kubernetes.json / yaml以及typesafe-kubernetes-dsl-pv.yml,其中包含我们的PersistentVolume的YAML文件。

请检查Fabric8 typesafe DSL注释处理和示例项目,以与此博客文章一起使用 。 希望收到您的反馈@ fabric8io或@christianposta

翻译自: https://www.javacodegeeks.com/2016/01/typesafe-kubernetes-manifest-dsl-jvm-based-apps.html

你可能感兴趣的:(java,python,大数据,docker,kubernetes)