Статьи

Typesafe Kubernetes-манифест DSL для приложений на основе JVM

Управление кластером контейнеров Docker / Rocket (или чего-либо еще) в производственной среде сопряжено с проблемами распределенных систем. К счастью, убедительное и очень энергичное сообщество вокруг проекта Kubernetes работает над этими проблемами, используя многолетний опыт работы в Google, Red Hat и стартапах, чтобы помочь другим. Если вы еще не пробовали Kubernetes для управления вашими контейнерами Docker, вам стоит заняться этим!

В сообществе Fabric8 мы работаем над опытом разработчиков на платформах Kubernetes и Enterprise Kubernetes / OpenShift, а также на их основе. У нас есть отличная веб-консоль для управления кластером Kubernetes, наборы библиотек для взаимодействия с кластером, в том числе клиент kubernetes с безопасным типом DSL, встроенная поддержка CI / CD одним щелчком с помощью Jenkins Workflow , Helm .sh упаковка приложений, управление API, Chaos Monkey и многие другие вкусности. Проверьте сайт fabric8.io для получения дополнительной информации. Может показаться, что Fabric8 имеет наклон JVM высокого уровня, но он не специфичен для Java и может применяться к golang / node / python / any Language. Пожалуйста, не стесняйтесь присоединиться и внести свой вклад!

Одна из проблем, с которой я столкнулся при работе с клиентами, использующими Kubernetes / OpenShift, заключается в том, что на данный момент нет хороших инструментов для создания файлов манифеста JSON / YAML Kubernetes или для настройки существующих файлов манифеста. JSON (и даже YAML) очень подвержены ошибкам при ручном редактировании, поэтому нам нужно что-то лучшее.

Если вы разработчик Java / JVM, вам повезло. Сообщество fabric8 имеет удивительный DSL для автоматической генерации файлов манифеста Kubernetes .

Вот пример того, как выглядит свободный конструктор API:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
@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 — управление и взаимодействие с API Kubernetes через файлы манифеста Kubernetes намного приятнее. Цель состоит в том, чтобы быть практическим, так что не стесняйтесь следовать, или, если вы спешите, чтобы увидеть примеры, найдите образец репозитория github

Создать новый проект

У Fabric8 есть множество быстрых стартов и архетипов mvn, чтобы вы начали. Мы начнем с создания проекта из проекта maven, из которого мы можем продемонстрировать kubernetes typesafe dsl:

mvn архетип: генерировать -DarchetypeGroupId = io.fabric8.archetypes -DarchetypeArtifactId = vertx-simplest-archetype -DarchetypeVersion = 2.2.93

Следуйте интерактивной подсказке, чтобы заполнить groupId / artifactId и т. Д. Затем убедитесь, что проект может быть mvn clean install

После того, как вы выполнили сборку mvn, вы должны увидеть в каталоге target/classes что были сгенерированы файлы kubernetes.json и kubernetes.yml . Взгляните на файл kubernetes.json:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
{
    "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-плагин

Первый вариант для безопасного для типов объявления без json ваших объектов Kubernetes — это использование конфигурации для fabric8-maven-plugin через свойства mvn. Если вы посмотрите в раздел <properties/> файла maven pom.xml, вы увидите некоторую конфигурацию, которую использует плагин fabric8-maven-plugin для автоматической генерации kubernetes.json:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<!-- Docker & Fabric8 Configs -->
  <docker.from>fabric8/java-jboss-openjdk8-jdk:1.0.10</docker.from>
  <fabric8.dockerUser>fabric8/</fabric8.dockerUser>
  <docker.image>${fabric8.dockerUser}${project.artifactId}:${project.version}</docker.image>
   
  <fabric8.label.component>${project.artifactId}</fabric8.label.component>
  <fabric8.label.container>java</fabric8.label.container>
  <fabric8.label.group>quickstarts</fabric8.label.group>
  <fabric8.iconRef>vertx</fabric8.iconRef>
   
   
  <fabric8.service.name>${project.artifactId}</fabric8.service.name>
  <fabric8.service.port>80</fabric8.service.port>
  <fabric8.service.containerPort>8080</fabric8.service.containerPort>
  <fabric8.service.type>LoadBalancer</fabric8.service.type>

Ура! Простые свойства, которые нужно заполнить, чтобы получить наш kubernetes manfiest, и это (и все значения) являются частью manfiest! Вы также можете указать переменные окружения и свойства шаблона OpenShift для плагина maven через свойства. Посмотрите документы для получения дополнительной информации об этом и конкретных свойствах, которые вы можете использовать для настройки генерации файлов манифеста.

Однако вы можете заметить, что только наиболее часто используемые конструкции (службы, контроллер репликации, … и учетные записи служб) имеют полезные свойства в подключаемом модуле maven. Это должно получить около 80%. Но что, если мы хотим добавить / настроить файл kubernetes.json, сгенерированный как часть этого плагина mvn? Или что, если у нас есть собственный файл kubernetes.json, который мы создали вручную, но хотим, чтобы редактирование было безопасным для типов? Или что, если мы просто хотим сгенерировать его на 100% с нуля, используя безопасный тип?

Тип безопасной DSL

Мы можем сделать это с помощью kubernetes-generator kubernetes из fabric8.io, которая в основном является фабрикой процессоров аннотаций Java, которую мы используем для генерации / дополнения файлов kubernetes.json / yml. (Обратите внимание, что для генерации yml и указания явных имен файлов вам нужно будет использовать fabric8 версии 2.2.89 или выше, в противном случае применяется предположение json и имя файла kubernetes.json).

Добавьте следующее в ваш maven pom.xml

1
2
3
4
<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>kubernetes-generator</artifactId>
  </dependency>

Допустим, в этом примере мы хотим добавить сведения о постоянном томе в наши файлы Kubernetes.json / yml. Ключом к этому является простое создание POJO и аннотирование его с помощью @KubernetesModelProcessor следующим образом:

1
2
3
4
@KubernetesModelProcessor
public class PersistentVolumeKubernetesModelProcessor {
     
}

Теперь в этом новом классе мы можем изменять или добавлять новые компоненты в файлы kubernetes.json / yml. Мы делаем это, следуя шаблону «посетитель». Подумайте, будет ли это, когда мы перебираем объекты в файле manfiest из Kubernetes, и предложим их вашим методам, чтобы вы могли работать по своему усмотрению. На самом деле, хотя это то, что происходит в фоновом режиме, вы не подделаны, чтобы иметь дело с каждым объектом в манифесте Kubernetes, если вы не хотите; вы просто работаете над / расширяете / увеличиваете интересующие вас объекты. Вы делаете это, определяя параметры ваших методов, чтобы брать определенные типы объектов (то есть, объекты, которые вас интересуют). Например, если ваш список ресурсов имеет ReplicationController, и вы хотите добавить больше модулей в его спецификацию шаблона, вы бы объявили свой метод следующим образом:

1
2
3
public void on(ReplicationControllerSpecBuilder builder) {
           
  }

Обратите внимание на тип параметра. Таким образом, мы можем выбрать только те части модели, над которыми мы хотим работать. Точно так же, если вы хотите только PodSpec:

1
2
3
public void on(PodSpecBuilder builder) {
 
  }

Некоторые полезные объекты-строители:

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

Некоторые полезные объекты-сборщики для OpenShift:

  • TemplateBuilder
  • RouteBuilder
  • OAuthAccessTaskBuilder
  • OAuthClientBuilder
  • ProjectBuilder
  • DeploymentStrategyBuilder

В нашем примере проекта мы будем использовать следующую реализацию для добавления набора постоянных томов, утверждений и монтирования в наш существующий ресурс манифеста kubernetes:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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. Для существующих ресурсов мы отредактируем существующие описания ресурсов, как это (и мы также выберем определенный ContainerBuilder по имени!)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
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 для создания JSON / YML для Kubernetes. Это было бы полезно, если вы не хотите использовать плагины mvn (т.е. вы используете gradle, sbt или что-то еще). Для этого мы аннотируем методы с помощью @KubernetesProvider и используем те же объекты компоновщика, что и раньше. Например, в нашем вышеупомянутом проекте мы создали постоянные заявки на объем и добавили их в наши файлы манифеста kubernetes. Пользователи смогут применять их к своим проектам, но пользователи обычно не управляют резервной копией PersistentVolumes, это может сделать администратор сборки / выпуска или кластера / проекта. Поэтому имеет смысл выделить метаданные PersistentVolume в собственный файл манифеста и доставить его отдельно.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@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, в котором будет наш YAML-файл для PersistentVolume.

Пожалуйста, ознакомьтесь с типом безопасной обработки аннотации DSL Fabric8 и примером проекта, чтобы согласиться с этим сообщением в блоге . Хотелось бы получить ваши отзывы @ fabric8io или @christianposta

Ссылка: Typesafe Kubernetes-манифест DSL для приложений на основе JVM от нашего партнера JCG Кристиана Поста в блоге