Schreiben eines weiteren Kubernetes-Template-Tools


Wenn Sie mit der Kubernetes-Umgebung arbeiten, verwenden Sie wahrscheinlich mehrere vorhandene Vorlagen-Tools, von denen einige Teil von Paketmanagern wie Helm oder Ksonnet sind oder nur Vorlagen-Sprachen (Jinja2, Go-Vorlage usw.). Alle haben ihre eigenen Nachteile und Vorteile, und wir werden sie durchgehen und unser eigenes Tool schreiben, das versucht, die besten Funktionen zu kombinieren.


Warum also nicht Helm?


Es gibt eine Reihe von Artikeln, die Helm kritisieren (z. B. nur einen: Überlegen Sie zweimal, bevor Sie Helm verwenden ). Das Hauptproblem bei Helm ist, dass es mit Zeichenfolgendarstellungen funktioniert und Kubernetes-Manifeste (json) Objekte sind . Die wahre Hölle für einen Helm-Diagrammentwickler beginnt, wenn er yaml für ein yaml Manifest berechnen yaml . Manchmal sieht es so aus (es ist ein echtes Beispiel aus meinem Diagramm):


 spec: jobTemplate: spec: template: spec: containers: - name: my-awesome-container resources: {{ toYaml .Values.resources | indent 14 }} 


Aber Helm ist heute de facto der Standard für Kubernetes-Anwendungsverpackungen. Der Hauptvorteil von Helm ist eine große Community und eine große Anzahl öffentlicher Repositories mit Diagrammen. Und kürzlich haben Helm-Entwickler einen Helm-Hub angekündigt. Helm ist heute wie Docker - es ist nicht der einzige, aber es hat Gemeinschaft und Unterstützung.


Es gibt vielversprechende Änderungen mit der Veröffentlichung von Helm 3, aber niemand weiß, wann es sein könnte.


Abschließend Helm Vorteile:


  • Große Community und eine Reihe von öffentlichen Charts
  • (Relativ) menschenfreundliche Syntax. Zumindest ist es yaml + go template;)

Nachteile:


  • Arbeiten mit Zeichenfolgen und nicht mit Objekten
  • Begrenzte Anzahl von Operatoren und Funktionen, die Sie verwenden können

OK, dann vielleicht Ksonnet?


Wenn Sie Helm mit Ksonnet vergleichen, hat letzteres einen großen Vorteil, nämlich, dass es mit Objekten funktioniert. Ksonnet ist ein Tool, das auf der JSON-Vorlagensprache Jsonnet basiert. Ein weiteres cooles Feature von Ksonnet ist, dass es Kubernetes-API-kompatible Jsonnet-Bibliotheken enthält, die Sie in Ihre Vorlage importieren und mit Kubernetes-Objekten wie in jeder OOP-Sprache arbeiten können:


 local k = import "k.libsonnet"; local deployment = k.apps.v1beta1.deployment; local appDeployment = deployment .new( params.name, params.replicas, container .new(params.name, params.image) .withPorts(containerPort.new(targetPort)), labels); 

Sieht beeindruckend aus, nicht wahr?
Es ist etwas weniger ordentlich, wenn Sie nicht mit API-Objekten arbeiten, sondern nur mit yaml Objekten, die aus der yaml / json Datei importiert wurden:


 { global: {}, components: { "deployment-nginx-deployment-dkecx"+: { spec+: { replicas: 10, template+: { spec+: { containers+: [ { name: "nginx", image: "nginx:latest", ports: [ { containerPort: 80, }, ], }, ], }, }, }, }, }, } 

Aber es ist immer noch etwas und es ist besser als mit Saiten in Helm zu arbeiten. Der Nachteil von Ksonnet ist, dass es eine kleinere Community und weniger Pakete als Helm hat (obwohl Sie Helm-Diagramme in Ihr Ksonnet-Projekt importieren können, aber Sie werden mit ihnen als json-Objekte arbeiten, nicht als jsonnet-Bibliotheksobjekte). Und aufgrund einer kleineren Community und eines kleineren Beitrags fehlen einige Funktionen, wenn Sie versuchen, Ihr eigenes Diagramm zu schreiben. Eine davon habe ich selbst erlebt: Sie wissen, dass Sie in Helm eine ConfigMap aus einem Verzeichnis ConfigMap können, das eine Reihe von Konfigurationsdateien enthält:


 apiVersion: v1 kind: ConfigMap metadata: name: conf data: {{- (.Files.Glob "foo/*").AsConfig | nindent 2 }} 

Sie können sich meine Frustration vorstellen, als ich herausfand, dass es in Ksonnet keine solche Funktion gibt. Es gibt jedoch Problemumgehungen. Aber der Punkt ist, dass es nur ein Beispiel für die Situation ist, in der Sie glücklich Ihr Diagramm schreiben und dann plötzlich ein Mangel an einer Funktion Sie auf halbem Weg aufhält.
Insgesamt Vorteile von Ksonnet:


  • Arbeiten mit Objekten
  • Kubernetes-API-kompatible Jsonnet-Bibliotheken
  • Unterstützung für den Import von Helmdiagrammen

Nachteile:


  • Kleinere Community und geringere Anzahl von Ksonnet-nativen Paketen
  • Fehlende Funktionen, die Sie in Helm verwenden können
  • Neue Syntax => erhöhte Lernzeit => erhöhter Busfaktor
  • Syntax kann manchmal hässlich und weniger lesbar werden (insbesondere, wenn Problemumgehungen für fehlende Funktionen vorgenommen werden).

Stellen wir uns ein ideales Vorlagenwerkzeug vor


Hier sind einige Kriterien für das "ideale" Vorlagenwerkzeug:


  • Es sollte mit Objekten funktionieren, nicht mit Strings
  • Es sollte in der Lage sein, mit Kubernetes-API-kompatiblen Objekten zu arbeiten
  • Es sollte einen anständigen Satz von Funktionen für die Arbeit mit Strings haben
  • Es sollte gut mit json- und yaml-Formaten funktionieren
  • Es sollte menschenfreundlich sein
  • Es sollte einfach sein
  • Es sollte die Möglichkeit haben, vorhandene Helm-Diagramme zu importieren (da dies Realität ist und wir die Helm-Community nutzen möchten).

Das reicht fürs Erste. Ich ging diese Liste in meinem Kopf durch und dachte mir: Okay, warum nicht Python ausprobieren? Mal sehen, ob es unseren Kriterien entspricht:


  • Arbeiten Sie mit Objekten, nicht mit Zeichenfolgen . Ja, wir können dafür dict und dict verwenden.
  • Sie können mit Kubernetes-API-kompatiblen Objekten arbeiten . Ja, from kubernetes import client
  • Haben Sie einen anständigen Satz von Funktionen für die Arbeit mit Zeichenfolgen . Viel!
  • Arbeiten Sie gut mit den Formaten json und yaml . Sehr schön.
  • Menschenfreundlich Keine Scheiße.
  • Einfach Ja
  • Möglichkeit zum Importieren vorhandener Helmdiagramme . Das werden wir uns hinzufügen.

Ok, sieht vielversprechend aus. Ich habe mich entschlossen, ein einfaches Template-Tool auf die offizielle Python-Client-Bibliothek für Kubernetes zu schreiben, und jetzt möchte ich Ihnen zeigen, was dabei herausgekommen ist.


Treffen Sie Karavel


Dieses Tool ist weder besonders noch kompliziert. Ich habe gerade die Kubernetes-Bibliothek (die mir die Möglichkeit gab, mit Kubernetes-Objekten zu arbeiten) genommen und einige grundlegende Funktionen für vorhandene Helm-Diagramme geschrieben (damit man sie abrufen und in ein eigenes Diagramm einfügen kann). Also, lass uns eine Tour machen.
Zuallererst ist dieses Tool bei Github Repo verfügbar und dort finden Sie ein Verzeichnis mit Beispielen.


Schnellstart mit Docker-Image


Wenn Sie es ausprobieren möchten, verwenden Sie am einfachsten dieses Docker-Image :


 $ docker run greegorey/karavel -h usage: karavelcli.py [-h] subcommand ... optional arguments: -h, --help show this help message and exit list of subcommands: subcommand template generates manifests from template ensure ensure helm dependencies 

Wenn Sie Diagramme vorlegen möchten, müssen Sie natürlich das Verzeichnis Ihres Diagramms bereitstellen:


 $ cd example $ docker run -v $PWD:/chart greegorey/karavel template . 

Schauen wir uns also die Diagrammstruktur an. Es ist einem von Helm sehr ähnlich:


 $ cd example $ tree . . ├── dependencies ├── prod.yaml ├── requirements.yaml ├── templates │  ├── custom-resource.py │  ├── deployment.py │  └── service-helm.py └── values.yaml 2 directories, 6 files 

Wie Helm hat es die Datei require.yaml mit demselben Layout:


 dependencies: - name: mysql version: 0.13.1 repository: https://kubernetes-charts.storage.googleapis.com/ 

Hier listen Sie einfach Ihre Helm-Abhängigkeiten auf, die Sie in Ihr Diagramm importieren möchten. Die Abhängigkeiten werden in das dependencies verschoben. Verwenden Sie zum Abrufen oder Aktualisieren den Befehl verify:


 $ karavel ensure . 

Danach sieht Ihr dependencies aus:


 $ tree dependencies dependencies └── mysql-0.13.1 └── mysql ├── Chart.yaml ├── README.md ├── templates │  ├── NOTES.txt │  ├── _helpers.tpl │  ├── configurationFiles-configmap.yaml │  ├── deployment.yaml │  ├── initializationFiles-configmap.yaml │  ├── pvc.yaml │  ├── secrets.yaml │  ├── svc.yaml │  └── tests │  ├── test-configmap.yaml │  └── test.yaml └── values.yaml 4 directories, 13 files 

Nachdem wir nun unsere Abhängigkeiten sichergestellt haben, schauen wir uns die Vorlagen an. Zunächst erstellen wir eine einfache Nginx-Bereitstellung:


 from kubernetes import client from karavel.helpers import Values def template(): values = Values().values # Configure Pod template container container = client.V1Container( name='nginx', image='{}:{}'.format(values.nginx.image.repository, values.nginx.image.tag), ports=[client.V1ContainerPort(container_port=80)]) # Create and configurate a spec section template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(labels={'app': 'nginx'}), spec=client.V1PodSpec(containers=[container])) # Create the specification of deployment spec = client.ExtensionsV1beta1DeploymentSpec( replicas=3, template=template) # Instantiate the deployment object deployment = client.ExtensionsV1beta1Deployment( api_version='extensions/v1beta1', kind='Deployment', metadata=client.V1ObjectMeta(name='nginx-deployment'), spec=spec) return deployment # [deployment], (deployment, deployment) are valid 

Damit die Vorlage gültig ist, benötigen Sie die Funktion template() , die entweder ein einzelnes Kubernetes-Objekt oder eine list / ein tuple davon zurückgibt. Die Liste der API-Objekte für den Python-Client finden Sie hier .
Wie Sie sehen können, ist der Code sauber, einfach und lesbar. Sie können sich fragen, woher von values.nginx.image.repository kommt? Es erhält Werte aus den karavel template -f one.yaml --values two.yaml des Diagramms übergeben, genau wie in Helm: karavel template -f one.yaml --values two.yaml . Wir werden sie uns später ansehen.


Okay, was ist mit Helm-Charts?


Jetzt haben wir unsere eigene Bereitstellung erstellt. Aber was ist, wenn wir ein Helmdiagramm oder einen Teil eines Diagramms importieren möchten? templates/service-helm.py wir einen Blick auf templates/service-helm.py :


 from kubernetes import client from karavel.helm import HelmChart from karavel.helpers import Values def template(): values = Values().values # Initialize the chart (== helm template --values) chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm) # Get the desired object from chart service = chart.get(name='svc', obj_class=client.V1Service) # Create custom objects to add custom_ports = [ client.V1ServicePort( name='my-custom-port', protocol=values.mysql.protocol, port=values.mysql.port, target_port=39000, ) ] # Add custom objects to the service service.spec['ports'] = custom_ports # Change Helm-generated label service.metadata['labels']['release'] += '-suffix' # Delete Helm-generated label `heritage: Tiller` del service.metadata['labels']['heritage'] return service # [service], (service, service) are valid 

Einfach, oder? Beachten Sie diese Zeile: service = chart.get(name='svc', obj_class=client.V1Service) - Wir haben ein Objekt der Klasse V1Service aus der Helm- yaml Datei erstellt. Wenn Sie das nicht wollen / müssen, können Sie immer nur mit dict .


Was ist, wenn ich eine benutzerdefinierte Ressource erstellen möchte?


Nun, damit gibt es ein kleines Problem. Die Kubernetes-API fügt der swagger json-Definition unter /openapi/v2 keine CRD-Objekte /openapi/v2 , und Python-Client-Objekte bauen auf dieser Definition auf. Sie können jedoch problemlos mit dict . So:


 from kubernetes import client def template(): resource = { 'apiVersion': 'stable.example.com/v1', 'kind': 'Whale', 'metadata': client.V1ObjectMeta( name='my-object', ), 'spec': { 'image': 'my-whale-image:0.0.1', 'tail': 1, 'fins': 4, } } return resource # [resource], (resource, resource) are valid 

Sieht immer noch gut aus, nicht wahr?


Kann ich Werte für verschiedene Umgebungen haben, z. B. dev / prod?


Ja, das kannst du!
Schauen wir values.yaml zuerst values.yaml :


 nginx: image: repository: nginx tag: 1.15-alpine mysql: port: 3307 protocol: TCP helm: releaseName: my-release namespace: prod imageTag: '5.7.14' service: type: NodePort 

Beachten Sie den chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm) dict: Wir haben ihn verwendet, um Werte für helm chart chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm) . Einige Helm-Diagramme benötigen releaseName für die Anwendungsbenennung und den namespace für RBAC-Richtlinien. Diese beiden Werte werden als Argumente --namespace und NAME in der --namespace an Helm --namespace .


Jetzt können Sie eine zusätzliche Datei für prod env angeben und alle unsere Beispiele vorlegen:


 $ karavel template -f values.yaml -f prod.yaml . --- # Source: templates/custom-resource.py apiVersion: stable.example.com/v1 kind: Whale metadata: name: my-object spec: fins: 4 image: my-whale-image:0.0.1 tail: 1 --- # Source: templates/deployment.py apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - image: nginx:1.14-alpine name: nginx ports: - containerPort: 80 --- # Source: templates/service-helm.py apiVersion: v1 kind: Service metadata: annotations: null labels: app: prod-release-mysql chart: mysql-0.13.1 release: prod-release-suffix name: prod-release-mysql spec: ports: - name: my-custom-port port: 3308 protocol: TCP targetPort: 39000 selector: app: prod-release-mysql type: NodePort 

Danach können Sie kubeclt apply und diese Objekte im Cluster bereitstellen.


Cool! Was ist mit Codierung und base64?


import base64


Was ist mit Vault für Geheimnisse?


import hvac


URLs abrufen?


import importlib


Sichere Hash-Funktionen?


import Crypto


Du hast es verstanden. Mit Python können Sie viele Dinge mit Ihren Kubernetes-Manifesten tun.


Ist es das NIH-Syndrom?


Nein :)
Ich bin glücklich mit Helm in meinen aktuellen Projekten. Es gibt Dinge, die ich vermisse. Ich habe Ksonnet auch in einigen meiner Projekte verwendet.
Ich möchte dieses Tool als Proof-of-Concept betrachten, dass wir Vorlagen-Tools besser als Helm haben können und es nicht sehr schwierig ist, sie mit Python zu entwickeln. Wenn ein Gemeinschaftsinteresse / -bedürfnis an einem solchen Tool besteht, können wir es gemeinsam weiterentwickeln. Oder wir können auf die Veröffentlichung von Helm 3 warten;)


Fazit


Ich habe Ihnen ein Python-basiertes Template-Tool für Kubernetes gezeigt, das Unterstützung für Kubernetes-API-kompatible Objekte und Unterstützung für den Import von Helm-Diagrammen bietet. Alle Kommentare und Diskussionen aus der Community sind willkommen und auch im Repo willkommen.


Vielen Dank für das Lesen und einen schönen Tag!


Referenzen


Source: https://habr.com/ru/post/de437682/


All Articles