Validierung von Kubernetes YAML gemäß bewährten Praktiken und Richtlinien

Anmerkung des Übersetzers.: Mit der wachsenden Anzahl an YAML-Konfigurationen für K8s-Umgebungen wird die Notwendigkeit einer automatisierten Überprüfung immer relevanter. Der Autor dieses Berichts hat nicht nur bestehende Lösungen für diese Aufgabe ausgewählt, sondern auch anhand eines Beispiels von Deployment untersucht, wie sie funktionieren. Es ist sehr informativ für diejenigen, die an diesem Thema interessiert sind.

Validierung von Kubernetes YAML gemäß bewährten Praktiken und Richtlinien

TL;DR: In dem Artikel werden sechs statische Prüftools verglichen, die YAML-Dateien von Kubernetes auf ihre Konformität mit bewährten Praktiken und Anforderungen hin überprüfen.

Kubernetes-Lasten werden in der Regel in Form von YAML-Dokumenten definiert. Ein Problem bei YAML ist die Schwierigkeit, Einschränkungen oder Beziehungen zwischen Manifestdateien festzulegen.

Was, wenn wir sicherstellen müssen, dass alle Images, die im Cluster bereitgestellt werden, aus einem vertrauenswürdigen Register stammen?

Wie verhindern wir, dass Deployments ohne festgelegte PodDisruptionBudgets in das Cluster gesendet werden?

Die Integration von statischen Tests ermöglicht es, Fehler und Verstöße gegen Richtlinien bereits in der Entwicklungsphase zu erkennen. Dadurch wird die Zuverlässigkeit und Sicherheit der Ressourcenbestimmungen erhöht, und die Wahrscheinlichkeit steigt, dass Produktionslasten bewährte Praktiken folgen.

Das Ökosystem der statischen Überprüfung von YAML-Dateien in Kubernetes lässt sich in folgende Kategorien unterteilen:

  • API-Validatoren. Werkzeuge in dieser Kategorie überprüfen das YAML-Manifest auf die Anforderungen des Kubernetes-API-Servers.
  • Fertige Tester. Werkzeuge aus dieser Kategorie kommen mit vorgefertigten Tests zur Sicherheit, zur Einhaltung bewährter Praktiken usw.
  • Benutzerdefinierte Validatoren. Vertreter dieser Kategorie ermöglichen die Erstellung benutzerdefinierter Tests in verschiedenen Sprachen, beispielsweise in Rego und Javascript.

In diesem Artikel werden wir sechs verschiedene Werkzeuge beschreiben und vergleichen:

  1. kubeval;
  2. kube-score;
  3. config-lint;
  4. copper;
  5. conftest;
  6. Polaris.

Nun, lasst uns beginnen!

Überprüfung von Deployments

Bevor wir mit dem Vergleich der Werkzeuge beginnen, lassen Sie uns eine Grundlage schaffen, auf der wir sie testen können.

Das untenstehende Manifest enthält eine Reihe von Fehlern und Abweichungen von bewährten Praktiken: Wie viele können Sie finden?

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: http-echo
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: http-echo
spec:
  ports:
  - port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app: http-echo

(base-valid.yaml)

Wir werden dieses YAML zur Vergleichung verschiedener Werkzeuge verwenden.

Das oben angegebene Manifest base-valid.yaml und andere Manifeste aus diesem Artikel finden Sie in Git-Repository.

Das Manifest beschreibt eine Webanwendung, deren Hauptaufgabe darin besteht, mit der Nachricht "Hello World" auf Port 5678 zu antworten. Es kann mit folgendem Befehl bereitgestellt werden:

kubectl apply -f hello-world.yaml

Und so überprüfen Sie, ob es funktioniert:

kubectl port-forward svc/http-echo 8080:5678

Gehen Sie jetzt zu http://localhost:8080 und bestätigen Sie, dass die Anwendung funktioniert. Aber entspricht sie den besten Praktiken? Lassen Sie uns das überprüfen.

1. Kubeval

Die Grundlage von kubeval ist die Idee, dass jede Interaktion mit Kubernetes über seine REST-API erfolgt. Mit anderen Worten, Sie können das API-Schema verwenden, um zu überprüfen, ob das gegebene YAML dem entspricht. Lassen Sie uns ein Beispiel betrachten.

Anweisungen zur Installation von kubeval sind auf der Projektwebsite verfügbar.

Zum Zeitpunkt des Schreibens des Originalartikels war Version 0.15.0 verfügbar.

Nach der Installation lassen Sie uns das oben genannte Manifest "füttern":

$ kubeval base-valid.yaml
PASS - base-valid.yaml enthält ein gültiges Deployment (http-echo)
PASS - base-valid.yaml enthält einen gültigen Service (http-echo)

Im Erfolgsfall wird kubeval mit Exit-Code 0 beendet. Sie können das folgendermaßen überprüfen:

$ echo $?
0

Lassen Sie uns nun kubeval mit einem anderen Manifest ausprobieren:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: http-echo
spec:
  ports:
  - port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app: http-echo

(kubeval-invalid.yaml)

Können Sie das Problem auf den ersten Blick erkennen? Wir führen aus:

$ kubeval kubeval-invalid.yaml
WARN - kubeval-invalid.yaml enthält ein ungültiges Deployment (http-echo) - selector: selector ist erforderlich
PASS - kubeval-invalid.yaml enthält einen gültigen Service (http-echo)

# überprüfen wir den Rückgabecode
$ echo $?
1

Die Ressource besteht die Überprüfung nicht.

Deployments, die die API-Version apps/v1, müssen einen Selector enthalten, der dem Label des Pods entspricht. Das obige Manifest enthält keinen Selector, daher hat kubeval einen Fehler ausgegeben und mit einem nicht-null Rückgabecode beendet.

Es ist interessant, was passiert, wenn Sie ausführen kubectl apply -f mit diesem Manifest?

Nun, lass uns versuchen:

$ kubectl apply -f kubeval-invalid.yaml
Fehler: Fehler bei der Validierung "kubeval-invalid.yaml": Fehler bei der Validierung der Daten: ValidationError(Deployment.spec):
fehlendes erforderliches Feld "selector" in io.k8s.api.apps.v1.DeploymentSpec; wenn Sie diese Fehler ignorieren möchten,
schalten Sie die Validierung mit --validate=false aus

Genau der Fehler, vor dem kubeval gewarnt hat. Sie können ihn beheben, indem Sie einen Selektor hinzufügen:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  selector:          # !!!
    matchLabels:     # !!!
      app: http-echo # !!!
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: http-echo
spec:
  ports:
  - port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app: http-echo

(base-valid.yaml)

Der Vorteil von Werkzeugen wie kubeval ist, dass solche Fehler früh im Bereitstellungszyklus erfasst werden können.

Darüber hinaus ist für diese Überprüfungen kein Zugang zum Cluster erforderlich: Sie können offline durchgeführt werden.

Standardmäßig überprüft kubeval Ressourcen auf Übereinstimmung mit dem neuesten Schema der Kubernetes API. In den meisten Fällen müssen Sie jedoch eine Überprüfung auf Übereinstimmung mit einer bestimmten Kubernetes-Version durchführen. Dies ist möglich mit dem Flag --kubernetes-version:

$ kubeval --kubernetes-version 1.16.1 base-valid.yaml

Bitte beachten Sie, dass die Version im Format angegeben werden muss Major.Minor.Patch.

Um die Liste der Versionen zu sehen, für die die Überprüfung unterstützt wird, besuchen Sie die JSON-Schema auf GitHub, das kubeval für die Validierung verwendet. Wenn kubeval offline ausgeführt werden muss, laden Sie die Schemata herunter und geben Sie ihren lokalen Speicherort mit dem Flag an --schema-location.

Neben einzelnen YAML-Dateien kann kubeval auch mit Verzeichnissen und stdin arbeiten.

Außerdem lässt sich kubeval leicht in CI-Pipelines integrieren. Für diejenigen, die Tests vor dem Einreichen von Manifesten im Cluster durchführen möchten, wird es erfreulich sein zu erfahren, dass kubeval drei Ausgabeformate unterstützt:

  1. Einfacher Text;
  2. JSON;
  3. Test Anything Protocol (TAP).

Und jedes dieser Formate kann zur weiteren Verarbeitung der Ausgabe verwendet werden, um eine Zusammenfassung der Ergebnisse in der gewünschten Form zu erstellen.

Ein Nachteil von kubeval ist, dass es derzeit nicht in der Lage ist, die Übereinstimmung von Custom Resource Definitions (CRDs) zu überprüfen. Sie können jedoch kubeval so konfigurieren, dass es diese ignoriert..

Kubeval ist ein hervorragendes Werkzeug zur Überprüfung und Bewertung von Ressourcen; es sollte jedoch hervorgehoben werden, dass das Bestehen eines Tests nicht garantiert, dass die Ressource den besten Praktiken entspricht.

Zum Beispiel die Verwendung des Tags latest Im Container entspricht es nicht den besten Praktiken. Allerdings betrachtet kubeval dies nicht als Fehler und meldet ihn nicht. Das heißt, die Überprüfung eines solchen YAML endet ohne Warnungen.

Aber was, wenn man YAML bewerten und Verstöße wie das Tag erkennen muss? latest? Как проверить YAML-файл на соответствие лучшим практикам?

2. Kube-score

Kube-score analysiert YAML-Manifestdateien und bewertet sie anhand von integrierten Tests. Diese Tests werden basierend auf Sicherheitsempfehlungen und Best Practices ausgewählt, zum Beispiel:

  • Container dürfen nicht als root betrieben werden.
  • Es müssen Gesundheitsprüfungen für Pods vorhanden sein.
  • Anforderungen und Limits für Ressourcen müssen festgelegt werden.

Nach dem Test werden drei Ergebnisse ausgegeben: OK, WARNUNG und KRITISCH.

Kube-score kann online ausprobiert oder lokal installiert werden.

Zum Zeitpunkt des Schreibens des ursprünglichen Artikels war die neueste Version von kube-score 1.7.0.

Lassen Sie uns testen, ob es auf unserem Manifest funktioniert: base-valid.yaml:

$ kube-score score base-valid.yaml

apps/v1/Deployment http-echo
[KRITISCH] Container Image Tag
  · http-echo -> Image mit neuestens Tag
      Es wird empfohlen, einen festen Tag zu verwenden, um unabsichtliche Upgrades zu vermeiden
[KRITISCH] Pod NetworkPolicy
  · Der Pod hat keine entsprechende Netzwerk-Richtlinie
      Erstellen Sie eine NetworkPolicy, die auf diesen Pod abzielt
[KRITISCH] Pod Probes
  · Container hat einen readinessProbe nicht
      Ein readinessProbe sollte verwendet werden, um anzuzeigen, wann der Service bereit ist, Verkehr zu empfangen.
      Ohne dies riskiert der Pod, Verkehr zu empfangen, bevor er gestartet ist. Es wird auch während
      Rollouts verwendet und kann Ausfallzeiten verhindern, wenn eine neue Version der Anwendung fehlschlägt.
      Weitere Informationen: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[KRITISCH] Container Sicherheits-Kontext
  · http-echo -> Container hat keinen konfigurierten Sicherheitskontext
      Setzen Sie securityContext, um den Container in einem sichereren Kontext auszuführen.
[KRITISCH] Container Ressourcen
  · http-echo -> CPU-Limit ist nicht festgelegt
      Ressourcengrenzen werden empfohlen, um RessourcendDoS zu vermeiden. Setzen Sie resources.limits.cpu
  · http-echo -> Speicherlimit ist nicht festgelegt
      Ressourcengrenzen werden empfohlen, um RessourcendDoS zu vermeiden. Setzen Sie resources.limits.memory
  · http-echo -> CPU-Anforderung ist nicht festgelegt
      Ressourcenanforderungen werden empfohlen, um sicherzustellen, dass die Anwendung gestartet werden kann und ohne
      Absturz läuft. Setzen Sie resources.requests.cpu
  · http-echo -> Speicheranforderung ist nicht festgelegt
      Ressourcenanforderungen werden empfohlen, um sicherzustellen, dass die Anwendung gestartet werden kann und ohne Absturz läuft.
      Setzen Sie resources.requests.memory
[KRITISCH] Deployment hat PodDisruptionBudget
  · Kein passendes PodDisruptionBudget gefunden
      Es wird empfohlen, ein PodDisruptionBudget zu definieren, um unerwartete Ausfallzeiten während der Kubernetes
      Wartungsarbeiten, z. B. beim Leeren eines Knotens, zu vermeiden.
[WARNUNG] Deployment hat host PodAntiAffinity
  · Deployment hat keine host podAntiAffinity gesetzt
      Es wird empfohlen, ein podAntiAffinity festzulegen, das verhindert, dass mehrere Pods eines Deployments
      auf demselben Knoten geplant werden. Dies erhöht die Verfügbarkeit, falls der Knoten nicht mehr verfügbar wird.

YAML besteht die Überprüfungen von kubeval, während kube-score auf folgende Mängel hinweist:

  • Es sind keine readiness-Probes konfiguriert.
  • Es fehlen Anforderungen und Limits für CPU- und Speicherräume.
  • Es sind keine Pod disruption budgets definiert.
  • Es fehlen Anti-Affinitätsregeln (anti-affinity) zur Maximierung der Verfügbarkeit.
  • Der Container wird unter Root ausgeführt.

All dies sind berechtigte Anmerkungen zu den Mängeln, die behoben werden müssen, damit das Deployment effektiver und zuverlässiger wird.

Team kube-score gibt Informationen in einem lesbaren Format mit allen Verstößen vom Typ aus WARNUNG und KRITISCH, was während der Entwicklung sehr hilfreich ist.

Benutzer, die dieses Tool im Rahmen ihrer CI-Pipeline verwenden möchten, können die kompaktere Ausgabe mit dem Flag aktivieren --output-format ci (in diesem Fall werden auch Tests mit dem Ergebnis ausgegeben OK):

$ kube-score score base-valid.yaml --output-format ci

[OK] http-echo apps/v1/Deployment
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU-Grenze ist nicht festgelegt
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Speichergrenze ist nicht festgelegt
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU-Anforderung ist nicht festgelegt
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Speicheranforderung ist nicht festgelegt
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Bild mit neuester Kennung
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: Der Pod hat keine passende Netzwerkrichtlinie
[CRITICAL] http-echo apps/v1/Deployment: Container fehlt eine readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Container hat keinen konfigurierten Sicherheitskontext
[CRITICAL] http-echo apps/v1/Deployment: Keine passende PodDisruptionBudget gefunden
[WARNING] http-echo apps/v1/Deployment: Deployment hat keine host podAntiAffinity eingestellt
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

Ähnlich wie kubeval gibt kube-score einen ungleich null Rückgabewert aus, wenn ein Test mit einem Fehler endet KRITISCH. Ähnliche Verarbeitung kann auch für WARNUNG.

Darüber hinaus gibt es die Möglichkeit, Ressourcen auf die Übereinstimmung mit verschiedenen API-Versionen zu überprüfen (wie auch in kubeval). Allerdings ist diese Information im kube-score 'hardcodiert': Man kann keine andere Kubernetes-Version auswählen. Diese Einschränkung kann ein großes Problem darstellen, wenn Sie das Cluster aktualisieren möchten oder mehrere Cluster mit unterschiedlichen K8s-Versionen haben.

Beachten Sie, dass Es gibt bereits ein Issue mit dem Vorschlag, diese Möglichkeit zu implementieren.

Näheres zu kube-score erfahren Sie auf der offiziellen Website.

Die Tests von kube-score sind ein hervorragendes Werkzeug zur Einführung von Best Practices, aber was ist, wenn es notwendig ist, einen Test zu ändern oder eigene Regeln hinzuzufügen? Leider ist das nicht möglich.

Kube-score ist nicht erweiterbar: Man kann darin keine Richtlinien hinzufügen oder anpassen.

Wenn benutzerdefinierte Tests für Prüfungen zur Einhaltung von Richtlinien, die im Unternehmen angenommen wurden, benötigt werden, kann eines der folgenden vier Werkzeuge verwendet werden: config-lint, copper, conftest oder polaris.

3. Config-lint

Config-lint ist ein Tool zur Validierung von Konfigurationsdateien im YAML-, JSON-, Terraform-, CSV-Format und für Kubernetes-Manifeste.

Es kann installiert werden mit den Anleitungen auf der Projektwebsite.

Die aktuelle Version zum Zeitpunkt der Erstellung des Originalartikels ist 1.5.0.

Config-lint enthält keine eingebauten Tests zur Überprüfung von Kubernetes-Manifeste.

Um Tests durchzuführen, müssen entsprechende Regeln erstellt werden. Diese werden in YAML-Dateien, den sogenannten „Regelsätzen“ (rulesets), erfasst und haben die folgende Struktur:

version: 1
description: Regeln für Kubernetes-Spezifikationsdateien
type: Kubernetes
files:
  - "*.yaml"
rules:
   # Liste von Regeln

(rule.yaml)

Lassen Sie uns näher darauf eingehen:

  • Feld type gibt an, welcher Konfigurationstyp von config-lint verwendet wird. Für K8s-Manifeste ist dies immer Kubernetes.
  • Im Feld files außer den eigentlichen Dateien kann auch ein Verzeichnis angegeben werden.
  • Feld rules soll für benutzerdefinierte Tests verwendet werden.

Angenommen, Sie möchten sicherstellen, dass die Images im Deployment immer aus einem vertrauenswürdigen Repository wie my-company.com/myapp:1.0geladen werden. Eine Regel für config-lint, die eine solche Überprüfung durchführt, würde wie folgt aussehen:

- id: MY_DEPLOYMENT_IMAGE_TAG
  severity: FAILURE
  message: Deployment muss ein gültiges Image-Tag verwenden
  resource: Deployment
  assertions:
    - every:
        key: spec.template.spec.containers
        expressions:
          - key: image
            op: starts-with
            value: "my-company.com/"

(rule-trusted-repo.yaml)

Für jede Regel müssen folgende Attribute angegeben werden:

  • id — eindeutige Kennung der Regel;
  • severity — kann sein FAILURE, WARNUNG und NON_COMPLIANT;
  • message — Der Inhalt dieser Zeile wird angezeigt, wenn die Regel verletzt wird;
  • resource — Typ der Ressource, auf die diese Regel angewendet wird;
  • assertions — Liste der Bedingungen, die für diese Ressource bewertet werden.

In der obigen Regel assertion mit dem Namen every überprüft, dass alle Container im Deployment (key: spec.templates.spec.containers) vertrauenswürdige Images verwenden (d.h. solche, die mit my-company.com/).

beginnen). Der vollständige Ruleset sieht folgendermaßen aus:

version: 1
description: Regeln für Kubernetes-Spezifikationsdateien
type: Kubernetes
files:
  - "*.yaml"
rules:

 - id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
    severity: FAILURE
    message: Deployment muss ein gültiges Image-Repository verwenden
    resource: Deployment
    assertions:
      - every:
          key: spec.template.spec.containers
          expressions:
            - key: image
              op: starts-with
              value: "my-company.com/"

(ruleset.yaml)

Um den Test auszuführen, lassen Sie uns ihn als check_image_repo.yaml. Speichern Sie die Überprüfung in der Datei. base-valid.yaml:

$ config-lint -rules check_image_repo.yaml base-valid.yaml

[
  {
  "AssertionMessage": "Jeder Ausdruck schlägt fehl: Und-Ausdruck schlägt fehl: Bild beginnt nicht mit my-company.com/",
  "Category": "",
  "CreatedAt": "2020-06-04T01:29:25Z",
  "Filename": "test-data/base-valid.yaml",
  "LineNumber": 0,
  "ResourceID": "http-echo",
  "ResourceType": "Deployment",
  "RuleID": "DEPLOYMENT_IMAGE_REPOSITORY",
  "RuleMessage": "Bereitstellung muss ein gültiges Bild-Repository verwenden",
  "Status": "FEHLER"
  }
]

Die Überprüfung war nicht erfolgreich. Lassen Sie uns nun das nächste Manifest mit einem gültigen Bild-Repository überprüfen:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: http-echo
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
         image: my-company.com/http-echo:1.0 # !!!
         args: ["-text", "hello-world"]
         ports:
         - containerPort: 5678

(image-valid-mycompany.yaml)

Wir führen denselben Test mit dem obigen Manifest durch. Es wurden keine Probleme festgestellt:

$ config-lint -rules check_image_repo.yaml image-valid-mycompany.yaml
[]

Config-lint ist ein vielversprechendes Framework zum Erstellen eigener Tests zur Überprüfung von YAML-Manifests in Kubernetes mit Hilfe von YAML DSL.

Aber was ist, wenn komplexere Logik und Tests erforderlich sind? Ist YAML nicht zu begrenzt dafür? Was, wenn wir Tests in einer vollwertigen Programmiersprache erstellen könnten?

4. Copper

Copper V2 ist ein Framework zur Validierung von Manifests mithilfe benutzerdefinierter Tests (analog zu config-lint).

Es unterscheidet sich jedoch dadurch, dass es kein YAML zur Beschreibung von Tests verwendet. Stattdessen können Tests in JavaScript erstellt werden. Copper bietet eine Bibliothek mit mehreren grundlegenden Werkzeugen, die helfen, Informationen über Kubernetes-Objekte auszulesen und Fehler zu melden.

Die Schritte zur Installation von Copper finden Sie in offiziellen Dokumentation.

2.0.1 — die neueste Version dieses Tools zum Zeitpunkt des Schreibens des ursprünglichen Artikels.

Wie bei config-lint hat Copper keine integrierten Tests. Lassen Sie uns einen schreiben. Er soll überprüfen, dass Deployments ausschließlich Containerbilder aus vertrauenswürdigen Repositories wie my-company.com.

eine Datei erstellen check_image_repo.js mit folgendem Inhalt:

$$.forEach(function($){
    if ($.kind === 'Deployment') {
        $.spec.template.spec.containers.forEach(function(container) {
            var image = new DockerImage(container.image);
            if (image.registry.lastIndexOf('my-company.com/') != 0) {
                errors.add_error('no_company_repo',"Bild " + $.metadata.name + " stammt nicht aus dem my-company.com Repository", 1)
            }
        });
    }
});

Nun, um unser Manifest zu überprüfen, base-valid.yaml, verwenden Sie den Befehl copper validate:

$ copper validate --in=base-valid.yaml --validator=check_image_tag.js

Die Überprüfung no_company_repo ist mit Schweregrad 1 fehlgeschlagen, da das Bild http-echo nicht aus dem my-company.com Repository stammt.
Die Validierung ist fehlgeschlagen.

Es ist klar, dass man mit Copper komplexere Tests durchführen kann – zum Beispiel um die Domainnamen in den Ingress-Manifests zu überprüfen oder Pods abzulehnen, die im privilegierten Modus laufen.

Copper enthält verschiedene Dienstprogramme:

  • DockerImage liest die angegebene Eingabedatei und erstellt ein Objekt mit den folgenden Attributen:
    • name – Name des Images,
    • tag – Tag des Images,
    • registry – Image-Registry,
    • registry_url – Protokoll (https://) und Image-Registry,
    • fqin – vollständiger Speicherort des Images.
  • Funktion findByName hilft, eine Ressource nach dem angegebenen Typ (kind) und Namen (name) aus der Eingabedatei zu finden.
  • Funktion findByLabels hilft, eine Ressource nach dem angegebenen Typ (kind) und Labels (labels).

Alle verfügbaren Dienstprogramme können angesehen werden hier.

Standardmäßig lädt es die gesamte Eingabedatei im YAML-Format in eine Variable $$ und macht sie für Skripte verfügbar (eine vertraute Methode für diejenigen, die Erfahrung mit jQuery haben).

Der Hauptvorteil von Copper ist offensichtlich: Sie müssen keine spezielle Sprache lernen und können die verschiedenen Möglichkeiten von JavaScript nutzen, um Ihre eigenen Tests zu erstellen, wie z.B. String-Interpolation, Funktionen usw.

Es sollte auch beachtet werden, dass die aktuelle Version von Copper mit der ES5-Version der JavaScript-Engine arbeitet, nicht mit ES6.

Details sind auf verfügbar offiziellen Webseite des Projekts..

Wenn Sie jedoch JavaScript nicht besonders mögen und eine Sprache bevorzugen, die speziell zum Erstellen von Abfragen und zum Beschreiben von Richtlinien gedacht ist, sollten Sie sich Conftest ansehen.

5. Conftest

Conftest ist ein Framework zur Überprüfung von Konfigurationsdaten. Es eignet sich auch zum Testen/Validieren von Kubernetes-Manifests. Die Tests werden mit einer speziellen Abfragesprache Rego.

installiert, kann über den Anleitungen, die auf der Projektseite aufgeführt sind.

Zum Zeitpunkt der Erstellung des Originalartikels war die zuletzt verfügbare Version 0.18.2.

Ähnlich wie bei config-lint und Copper kommt Conftest ohne integrierte Tests. Lassen Sie uns es ausprobieren und unsere eigene Richtlinie schreiben. Wie in den vorherigen Beispielen werden wir überprüfen, ob Container-Images aus einer vertrauenswürdigen Quelle stammen.

Erstellen Sie ein Verzeichnis conftest-checks, in dem eine Datei mit dem Namen check_image_registry.rego mit folgendem Inhalt:

package main

deny[msg] {

  input.kind == "Deployment"
  image := input.spec.template.spec.containers[_].image
  not startswith(image, "my-company.com/")
  msg := sprintf("Bild '%v' stammt nicht aus dem my-company.com Repository", [image])
}

Lassen Sie uns jetzt base-valid.yaml über conftest testen:

$ conftest test --policy .\/conftest-checks base-valid.yaml

FEHLGESCHLAGEN - base-valid.yaml - Das Image 'hashicorp\/http-echo' stammt nicht aus dem Repository my-company.com
1 Tests, 1 bestanden, 0 Warnungen, 1 Fehler

Der Test ist erwartungsgemäß fehlgeschlagen, da die Images aus einer nicht vertrauenswürdigen Quelle stammen.

In der Rego-Datei definieren wir den Block deny. Seine Wahrheit wird als Verstoß angesehen. Wenn es mehrere Blöcke gibt, deny prüft conftest sie unabhängig voneinander, und die Wahrheit eines jeden Blocks wird als Verstoß betrachtet.

Neben der Standardausgabe unterstützt conftest JSON, TAP und tabellarisches Format – eine äußerst nützliche Funktion, wenn Berichte in eine bestehende CI-Pipeline integriert werden müssen. Der gewünschte Format kann mit dem Flag --output.

Um das Debugging von Richtlinien zu erleichtern, bietet conftest ein Flag --trace. Es gibt eine Verfolgungsinformation darüber aus, wie conftest die angegebenen Richtliniendateien parst.

Die Richtlinien von conftest können als Artefakte in OCI-Registry (Open Container Initiative) veröffentlicht und geteilt werden.

Befehle push und pull erlauben es, ein Artefakt zu veröffentlichen oder ein bestehendes Artefakt aus einem entfernten Register abzurufen. Lassen Sie uns versuchen, die von uns erstellte Richtlinie mit conftest push.

Starten Sie ein lokales Docker-Register:

$ docker run -it --rm -p 5000:5000 registry

Wechseln Sie in ein anderes Terminal und gehen Sie in das zuvor erstellte Verzeichnis conftest-checks und führen Sie den folgenden Befehl aus:

$ conftest push 127.0.0.1:5000\/amitsaha\/opa-bundle-example:latest

Wenn der Befehl erfolgreich war, sehen Sie eine Nachricht des folgenden Typs:

2020\/06\/10 14:25:43 Bündel mit Digest gepusht: sha256:e9765f201364c1a8a182ca637bc88201db3417bacc091e7ef8211f6c2fd2609c

Erstellen Sie jetzt ein temporäres Verzeichnis und führen Sie darin den Befehl conftest pullaus. Dieser lädt das vom vorherigen Befehl erstellte Paket herunter:

$ cd $(mktemp -d)
$ conftest pull 127.0.0.1:5000\/amitsaha\/opa-bundle-example:latest

Im temporären Verzeichnis wird ein Unterverzeichnis policy, das unsere Richtliniendatei enthält:

$ tree
.
└── policy
  └── check_image_registry.rego

Tests können direkt aus dem Repository durchgeführt werden:

$ conftest test --update 127.0.0.1:5000\/amitsaha\/opa-bundle-example:latest base-valid.yaml
..
FEHLGESCHLAGEN - base-valid.yaml - Das Image 'hashicorp\/http-echo' stammt nicht aus dem Repository my-company.com
2 Tests, 1 bestanden, 0 Warnungen, 1 Fehler

Leider wird DockerHub derzeit nicht unterstützt. Daher zählen Sie sich glücklich, wenn Sie Azure Container Registry (ACR) oder Ihr eigenes Register verwenden.

Das Format der Artefakte ist dasselbe wie das von Paketen des Open Policy Agent (OPA), wodurch conftest verwendet werden kann, um Tests aus bestehenden OPA-Paketen auszuführen.

Mehr über das Teilen von Richtlinien und andere Funktionen von conftest erfahren Sie auf offiziellen Webseite des Projekts..

6. Polaris

Das letzte Werkzeug, über das in diesem Artikel gesprochen wird, ist Polaris. (Seine Ankündigung im letzten Jahr haben wir bereits übersetzt.Anm. d. Ü.)

Polaris kann in einem Cluster installiert oder im Befehlszeilenmodus verwendet werden. Wie Sie bereits erraten haben, ermöglicht es die statische Analyse von Kubernetes-Manifests.

Im Befehlszeilenmodus stehen integrierte Tests zur Verfügung, die Bereiche wie Sicherheit und bewährte Praktiken abdecken (ähnlich wie kube-score). Darüber hinaus können Sie eigene Tests erstellen (wie in config-lint, copper und conftest).

Anders ausgedrückt, verbindet Polaris die Vorteile beider Kategorien von Werkzeugen: mit integrierten und benutzerdefinierten Tests.

Um Polaris im Befehlszeilenmodus zu installieren, verwenden Sie die Anleitungen auf der Projektwebsite..

Zum Zeitpunkt der Erstellung des ursprünglichen Artikels ist die Version 1.0.3 verfügbar.

Nach der Installation können Sie Polaris auf dem Manifest ausführen base-valid.yaml mit dem folgenden Befehl:

$ polaris audit --audit-path base-valid.yaml

Es wird eine Zeile im JSON-Format mit einer detaillierten Beschreibung der durchgeführten Tests und deren Ergebnissen ausgegeben. Die Ausgabe hat die folgende Struktur:

{
  "PolarisOutputVersion": "1.0",
  "AuditTime": "0001-01-01T00:00:00Z",
  "SourceType": "Path",
  "SourceName": "test-data/base-valid.yaml",
  "DisplayName": "test-data/base-valid.yaml",
  "ClusterInfo": {
    "Version": "unbekannt",
    "Nodes": 0,
    "Pods": 2,
    "Namespaces": 0,
    "Controllers": 2
  },
  "Results": [
    
    /* lange Liste */
  ]
}

Die vollständige Ausgabe ist verfügbar. hier.

Wie kube-score identifiziert Polaris Probleme in den Bereichen, in denen das Manifest nicht den besten Praktiken entspricht:

  • Fehlende Gesundheitsprüfungen für Pods.
  • Keine Tags für Container-Images angegeben.
  • Der Container wird unter Root ausgeführt.
  • Keine Requests und Limits für Speicher und CPU angegeben.

Jeden Test wird je nach seinen Ergebnissen eine Kritikalität zugewiesen: Warnung oder Gefahr. Um mehr über die vorhandenen integrierten Tests zu erfahren, wenden Sie sich an die Dokumentation.

Wenn keine Details benötigt werden, können Sie das Flag --format scoreangeben. In diesem Fall gibt Polaris eine Zahl im Bereich von 1 bis 100 aus — Wertung (d.h. Bewertung):

$ polaris audit --audit-path test-data/base-valid.yaml --format score
68

Je näher die Bewertung bei 100 liegt, desto höher ist der Grad der Einhaltung. Wenn Sie den Exit-Code des Befehls polaris auditprüfen, werden Sie feststellen, dass er 0 beträgt.

Zwänge polaris audit das Programm dazu, mit einem nicht-null Exit-Code zu beenden, können Sie zwei Flags verwenden:

  • Flag --set-exit-code-below-score nimmt einen Schwellenwert im Bereich von 1-100 als Argument. In diesem Fall wird der Befehl mit dem Exit-Code 4 beendet, wenn die Bewertung unter dem Schwellenwert liegt. Das ist sehr praktisch, wenn Sie einen bestimmten Schwellenwert haben (sagen wir 75) und eine Warnung benötigen, wenn die Bewertung darunter fällt.
  • Flag --set-exit-code-on-danger führt dazu, dass der Befehl mit Code 3 beendet wird, wenn einer der Gefahren-Tests fehlschlägt.

Lassen Sie uns nun versuchen, einen benutzerdefinierten Test zu erstellen, der überprüft, ob das Image aus einem vertrauenswürdigen Repository stammt. Benutzerdefinierte Tests werden im YAML-Format angegeben, und der Test selbst wird mit JSON-Schema beschrieben.

Der folgende YAML-Code beschreibt einen neuen Test mit dem Namen checkImageRepo:

checkImageRepo:
  successMessage: Image-Registry ist gültig
  failureMessage: Image-Registry ist nicht gültig
  category: Bilder
  target: Container
  schema:
    '$schema': http://json-schema.org/draft-07/schema
    type: object
    properties:
      image:
        type: string
        pattern: ^my-company.com/.+$

Lassen Sie uns einen genaueren Blick darauf werfen:

  • successMessage — diese Zeile wird angezeigt, wenn der Test erfolgreich abgeschlossen wurde;
  • failureMessage — diese Nachricht wird im Fehlerfall angezeigt;
  • category — verweist auf eine der Kategorien: Bilder, Gesundheitsprüfungen, Sicherheit, Netzwerk und Ressourcen;
  • target— definiert, auf welchen Objekttyp (spec) der Test angewendet wird. Mögliche Werte: Container, Pod oder Controller;
  • Der Test selbst wird im Objekt schema mit Hilfe des JSON-Schemas definiert. In diesem Test wird das Schlüsselwort pattern verwendet, um die Quelle des Images mit dem erforderlichen zu vergleichen.

Um den obigen Test auszuführen, ist es notwendig, die folgende Polaris-Konfiguration zu erstellen:

checks:
  checkImageRepo: danger
customChecks:
  checkImageRepo:
    successMessage: Image-Registry ist gültig
    failureMessage: Image-Registry ist nicht gültig
    category: Bilder
    target: Container
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(polaris-conf.yaml)

Lassen Sie uns die Datei analysieren:

  • Im Feld checks werden die Tests und deren Kritikalitätsgrad beschrieben. Da es wünschenswert ist, eine Warnung zu erhalten, wenn das Image aus einer unsicheren Quelle stammt, setzen wir hier das Niveau Gefahr.
  • Der Test checkImageRepo wird dann im Objekt customChecks.

Speichern Sie die Datei als custom_check.yaml. Jetzt kann das getestet werden polaris audit mit dem YAML-Manifest, das überprüft werden muss.

Testen wir unser Manifest base-valid.yaml:

$ polaris audit --config custom_check.yaml --audit-path base-valid.yaml

Team polaris audit führte nur den oben angegebenen benutzerdefinierten Test aus, und dieser war nicht erfolgreich.

Wenn das Image auf my-company.com/http-echo:1.0, wird Polaris erfolgreich beendet. Das Manifest mit den Änderungen ist bereits vorhanden in das Repository, sodass Sie den vorherigen Befehl im Manifest überprüfen können image-valid-mycompany.yaml.

Nun stellt sich die Frage: Wie lassen sich integrierte Tests zusammen mit benutzerdefinierten ausführen? Ganz einfach! Man muss lediglich die IDs der integrierten Tests in die Konfigurationsdatei hinzufügen. Dadurch erhält sie folgendes Format:

checks:
  cpuRequestsMissing: warning
  cpuLimitsMissing: warning
  # Weitere integrierte Checks..
  # ..
  # benutzerdefinierte Checks
  checkImageRepo: danger # !!!
customChecks:
  checkImageRepo:        # !!!
    successMessage: Das Abbild-Repository ist gültig
    failureMessage: Das Abbild-Repository ist nicht gültig
    category: Bilder
    target: Container
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(config_with_custom_check.yaml)

Ein Beispiel für eine vollständige Konfigurationsdatei ist verfügbar hier.

Manifest prüfen base-valid.yaml, indem Sie integrierte und benutzerdefinierte Tests verwenden, können Sie dies mit folgendem Befehl tun:

$ polaris audit --config config_with_custom_check.yaml --audit-path base-valid.yaml

Polaris ergänzt integrierte Tests mit benutzerdefinierten und vereint so das Beste aus beiden Welten.

Andererseits kann die Unfähigkeit, leistungsstärkere Sprachen wie Rego oder JavaScript zu verwenden, ein einschränkender Faktor sein, der die Erstellung komplexerer Tests verhindert.

Weitere Informationen über Polaris sind verfügbar unter der Projektwebsite.

Zusammenfassung

Obwohl es viele Tools zur Überprüfung und Bewertung von Kubernetes-YAML-Dateien gibt, ist es wichtig, eine klare Vorstellung davon zu haben, wie die Tests entworfen und ausgeführt werden..

Zum Beispiel, wenn man Kubernetes-Manifeste betrachtet, die durch die Pipeline laufen, könnte kubeval der erste Schritt in einer solchen Pipeline sein.. Es würde sicherstellen, dass die Objektdefinitionen dem Kubernetes-API-Schema entsprechen.

Nach der Durchführung eines solchen Tests könnte man zu komplexeren Tests übergehen, wie der Einhaltung von Best Practices und spezifischen Richtlinien. Hier würden kube-score und Polaris nützlich sein.

Für diejenigen mit komplexen Anforderungen und dem Bedarf, Tests im Detail anzupassen, wären copper, config-lint und conftest geeignet..

Conftest und config-lint verwenden YAML, um benutzerdefinierte Tests festzulegen, während copper Zugriff auf eine vollständige Programmiersprache bietet, was es zu einer recht attraktiven Wahl macht.

Andererseits, sollte man eines dieser Tools nutzen und folglich alle Tests manuell erstellen oder sich für Polaris entscheiden und nur das hinzufügen, was notwendig ist? Auf diese Frage gibt es keine eindeutige Antwort..

Die folgende Tabelle enthält eine kurze Zusammenfassung jedes Tools:

Tool
Zweckbestimmung
Nachteile
Benutzerdefinierte Tests

kubeval
Überprüft YAML-Manifeste auf die Übereinstimmung mit einer bestimmten Version des API-Schemas
Kann nicht mit CRD arbeiten
Nein

kube-score
Analysiert YAML-Manifeste auf die Übereinstimmung mit bewährten Praktiken
Es ist nicht möglich, Ihre eigene Kubernetes-API-Version zur Überprüfung der Ressourcen auszuwählen
Nein

copper
Gemeinsames Framework zur Erstellung eigener JavaScript-Tests für YAML-Manifeste
Keine integrierten Tests. Magere Dokumentation
Ja

config-lint
Gemeinsames Framework zur Erstellung von Tests in einer domänenspezifischen Sprache, die in YAML integriert ist. Unterstützt verschiedene Konfigurationsformate (z. B. Terraform)
Es gibt keine vorgefertigten Tests. Integrierte Assertions und Funktionen könnten unzureichend sein
Ja

conftest testen
Framework zur Erstellung eigener Tests in Rego (einer spezialisierten Abfragesprache). Erlaubt das Teilen von Richtlinien über OCI-Bundles
Es gibt keine integrierten Tests. Man muss Rego lernen. Docker Hub wird bei der Veröffentlichung von Richtlinien nicht unterstützt
Ja

Polaris
Analysiert YAML-Manifeste auf die Übereinstimmung mit standardisierten bewährten Praktiken. Ermöglicht die Erstellung eigener Tests mit JSON Schema
Die Testmöglichkeiten, die auf JSON Schema basieren, könnten unzureichend sein
Ja

Da diese Tools nicht vom Zugriff auf das Kubernetes-Cluster abhängig sind, lassen sie sich leicht installieren. Sie ermöglichen das Filtern von Quell-Dateien und bieten den Autoren von Pull-Requests in Projekten schnelles Feedback.

P.S. vom Übersetzer

Lesen Sie auch in unserem Blog:

Quelle: habr.com

60GB SSD 8Gb DDR4