Przyp. tłum.: W miarę rosnącej liczby konfiguracji YAML dla środowisk K8s, coraz ważniejsza staje się potrzeba ich automatycznego sprawdzania. Autor tego przeglądu nie tylko wybrał istniejące rozwiązania do tego zadania, ale również na przykładzie Deploymentu przyjrzał się ich działaniu. Okazało się to bardzo pouczające dla tych, których temat interesuje.

TL;DR: W artykule porównano sześć statycznych narzędzi do sprawdzania i oceny plików YAML Kubernetes pod kątem zgodności z najlepszymi praktykami i wymaganiami.
Obciążenia robocze Kubernetes zazwyczaj definiowane są w postaci dokumentów YAML. Jednym z problemów z YAML-em jest złożoność określania ograniczeń lub relacji między plikami manifestów.
Co zrobić, jeśli musimy upewnić się, że wszystkie obrazy wdrażane w klastrze pochodzą z zaufanego rejestru?
Jak zapobiec wysyłaniu do klastra Deploymentów, dla których nie zostały określone PodDisruptionBudgets?
Integracja testowania statycznego pozwala wykrywać błędy i naruszenia polityki już na etapie rozwoju. W ten sposób zwiększa się pewność poprawności i bezpieczeństwa definicji zasobów, a także rośnie prawdopodobieństwo, że obciążenia produkcyjne będą przestrzegały najlepszych praktyk.
Ekosystem statycznej weryfikacji plików YAML Kubernetes można podzielić na następujące kategorie:
- Weryfikatory API. Narzędzia w tej kategorii sprawdzają manifest YAML pod kątem zgodności z wymaganiami serwera API Kubernetes.
- Gotowe testery. Narzędzia z tej kategorii mają gotowe testy dotyczące bezpieczeństwa, zgodności z najlepszymi praktykami itd.
- Niestandardowe weryfikatory. Przedstawiciele tej kategorii pozwalają na tworzenie niestandardowych testów w różnych językach, np. w Rego i Javascript.
W tym artykule opiszemy i porównamy sześć różnych narzędzi:
- kubeval;
- kube-score;
- config-lint;
- copper;
- conftest;
- Polaris.
Cóż, zaczynajmy!
Sprawdzanie Deploymentów
Zanim przejdziemy do porównania narzędzi, stwórzmy pewną podstawę, na której będziemy je testować.
Podany poniżej manifest zawiera szereg błędów i niezgodności z najlepszymi praktykami: ile z nich możesz znaleźć?
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)
Będziemy używać tego YAML do porównania różnych narzędzi.
Powyższy manifest
base-valid.yamli inne manifesty z tego artykułu można znaleźć w .
Manifest opisuje aplikację internetową, której głównym zadaniem jest odpowiedzieć komunikatem „Hello World” na porcie 5678. Można go wdrożyć następującą komendą:
kubectl apply -f hello-world.yamlA oto jak sprawdzić działanie:
kubectl port-forward svc/http-echo 8080:5678Teraz przejdź do i potwierdź, że aplikacja działa. Ale czy stosuje najlepsze praktyki? Sprawdźmy to.
1. Kubeval
Na podstawie leży idea, że każde interakcje z Kubernetes odbywają się przez jego REST API. Innymi słowy, można użyć schematu API do weryfikacji, czy dany YAML jest zgodny. Przyjrzyjmy się przykładowi.
kubeval są dostępne na stronie projektu.
W momencie pisania oryginalnego artykułu dostępna była wersja 0.15.0.
Po instalacji „podajmy” mu manifest podany powyżej:
$ kubeval base-valid.yaml
PASS - base-valid.yaml zawiera ważny Deployment (http-echo)
PASS - base-valid.yaml zawiera ważny Service (http-echo)W przypadku sukcesu kubeval zakończy pracę z kodem wyjścia 0. Można to sprawdzić w następujący sposób:
$ echo $?
0Teraz spróbujmy kubeval z innym manifestem:
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)
Czy możesz dostrzec problem gołym okiem? Uruchamiamy:
$ kubeval kubeval-invalid.yaml
WARN - kubeval-invalid.yaml zawiera nieprawidłowy Deployment (http-echo) - selektor: selektor jest wymagany
PASS - kubeval-invalid.yaml zawiera ważny Service (http-echo)
# sprawdźmy kod zwrotu
$ echo $?
1Zasób nie przechodzi weryfikacji.
Deploymenty korzystające z wersji API apps/v1, muszą zawierać selektor odpowiadający etykiecie podu. Powyższy manifest nie zawiera selektora, dlatego kubeval zgłosił błąd i zakończył działanie z kodem różnym od zera.
Ciekawe, co się stanie, jeśli wykonasz kubectl apply -f z tym manifestem?
Cóż, spróbujmy:
$ kubectl apply -f kubeval-invalid.yaml
błąd: błąd walidacji "kubeval-invalid.yaml": błąd walidacji danych: ValidationError(Deployment.spec):
brak wymaganego pola "selector" w io.k8s.api.apps.v1.DeploymentSpec; jeśli chcesz zignorować te błędy,
wyłącz walidację za pomocą --validate=falseTo właśnie ten błąd, o którym ostrzega kubeval. Można go naprawić, dodając selektor:
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)
Zaletą narzędzi takich jak kubeval jest to, że takie błędy można wychwytywać na wczesnych etapach cyklu wdrożenia.
Ponadto, do tych sprawdzeń nie jest wymagany dostęp do klastra: można je przeprowadzać offline.
Domyślnie kubeval sprawdza zasoby pod kątem zgodności z najnowszym schematem API Kubernetes. Jednak w wielu przypadkach może być konieczne sprawdzenie zgodności z określoną wersją Kubernetes. Można to zrobić za pomocą flagi --kubernetes-version:
$ kubeval --kubernetes-version 1.16.1 base-valid.yamlZauważ, że wersja musi być podana w formacie Major.Minor.Patch.
Aby zobaczyć listę wersji, dla których dostępne jest sprawdzenie zgodności, odwiedź , który kubeval używa do walidacji. Jeśli potrzebujesz uruchomić kubeval offline, pobierz schemy i podaj ich lokalizację lokalną za pomocą flagi --schema-location.
Oprócz pojedynczych plików YAML, kubeval może również pracować z katalogami i stdin.
Ponadto, Kubeval łatwo integruje się z pipeline'ami CI. Ciągle testujących przed przesłaniem manifestów do klastra ucieszy informacja, że kubeval obsługuje trzy formaty wyjściowe:
- Zwykły tekst;
- JSON;
- Test Anything Protocol (TAP).
I dowolny z formatów można wykorzystać do dalszego parsowania wyjścia, aby utworzyć podsumowanie wyników w pożądanym formacie.
Jedną z wad kubeval jest to, że obecnie nie potrafi on sprawdzać zgodności z Custom Resource Definitions (CRDs). Można jednak skonfigurować kubeval, .
Kubeval to świetne narzędzie do sprawdzania i oceny zasobów; jednak należy podkreślić, że pomyślne przejście testu nie gwarantuje, że zasób spełnia najlepsze praktyki.
Na przykład użycie tagu latest W kontenerze nie spełnia najlepszych praktyk. Jednak kubeval nie uznaje tego za błąd i nie zgłasza. Oznacza to, że sprawdzenie takiego YAML zakończy się bez ostrzeżeń.
Ale co, jeśli trzeba ocenić YAML i wykryć naruszenia, takie jak tag latest? Как проверить YAML-файл на соответствие лучшим практикам?
2. Kube-score
analizuje manifesty YAML i ocenia je na podstawie wbudowanych testów. Te testy są wybierane na podstawie zaleceń dotyczących bezpieczeństwa i najlepszych praktyk, na przykład:
- Uruchomienie kontenera nie jako root.
- Obecność kontroli zdrowia dla podów.
- Określenie requestów i limitów zasobów.
Po zakończeniu testu wydawane są trzy wyniki: OK, WARNING i CRITICAL.
Kube-score można wypróbować online lub zainstalować lokalnie.
W momencie pisania oryginalnego artykułu najnowsza wersja kube-score wynosiła 1.7.0.
Wypróbujmy go na naszym manifeście base-valid.yaml:
$ kube-score score base-valid.yaml
apps/v1/Deployment http-echo
[CRITICAL] Tag obrazu kontenera
· http-echo -> Obraz z tagiem latest
Zaleca się używanie stałego tagu, aby uniknąć niezamierzonych aktualizacji
[CRITICAL] Pod NetworkPolicy
· Pod nie ma dopasowanej polityki sieciowej
Utwórz NetworkPolicy, która będzie celować w ten pod
[CRITICAL] Pod Probes
· Kontener nie ma readinessProbe
readinessProbe powinien być używany, aby wskazać, kiedy usługa jest gotowa na przyjmowanie ruchu.
Bez niego Pod ryzykuje otrzymanie ruchu zanim się załaduje. Jest to także używane podczas
wdrożeń, a może zapobiec przestojowi, jeśli nowa wersja aplikacji zawiedzie.
Więcej informacji: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[CRITICAL] Kontekst zabezpieczeń kontenera
· http-echo -> Kontener nie ma skonfigurowanego kontekstu zabezpieczeń
Ustaw securityContext, aby uruchomić kontener w bardziej zabezpieczonym kontekście.
[CRITICAL] Zasoby kontenera
· http-echo -> Limit CPU nie jest ustawiony
Zaleca się ustawienie limitów zasobów, aby uniknąć DDOS zasobów. Ustaw resources.limits.cpu
· http-echo -> Limit pamięci nie jest ustawiony
Zaleca się ustawienie limitów zasobów, aby uniknąć DDOS zasobów. Ustaw resources.limits.memory
· http-echo -> Żądanie CPU nie jest ustawione
Zaleca się ustawienie żądań zasobów, aby upewnić się, że aplikacja może wystartować i działać bez
awarii. Ustaw resources.requests.cpu
· http-echo -> Żądanie pamięci nie jest ustawione
Zaleca się ustawienie żądań zasobów, aby upewnić się, że aplikacja może wystartować i działać bez awarii.
Ustaw resources.requests.memory
[CRITICAL] Wdrożenie ma PodDisruptionBudget
· Nie znaleziono dopasowanego PodDisruptionBudget
Zaleca się zdefiniowanie PodDisruptionBudget, aby uniknąć niespodziewanych przestojów podczas Kubernetes
operacji konserwacyjnych, takich jak opróżnianie węzła.
[WARNING] Wdrożenie ma host PodAntiAffinity
· Wdrożenie nie ma ustawionej polityki podAntiAffinity
Zaleca się ustawienie podAntiAffinity, aby zapobiec planowaniu wielu podów z wdrożenia na
tym samym węźle. To zwiększa dostępność w przypadku, gdy węzeł stanie się niedostępny.YAML przechodzi kontrole kubeval, podczas gdy kube-score wskazuje na następujące niedociągnięcia:
- Brak są skonfigurowane kontrole gotowości.
- Brakują requesty i limitów dla zasobów CPU i pamięci.
- Nie określono budżetów zakłóceń podów.
- Brak zasad podziału (anti-affinity) aby maksymalizować dostępność.
- Kontener działa z uprawnieniami roota.
Wszystkie te uwagi to uzasadnione spostrzeżenia dotyczące wad, które należy wyeliminować, aby wdrożenie było bardziej efektywne i niezawodne.
Zespół kube-score wyświetla informacje w czytelnej formie, uwzględniając wszelkie naruszenia typu WARNING i CRITICAL, co bardzo pomocne podczas rozwoju.
Osoby chcące używać tego narzędzia w ramach CI mogą włączyć bardziej zwięzły format wyjścia za pomocą flagi --output-format ci (w tym przypadku wyświetlane są również testy z wynikami 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) limit CPU nie jest ustawiony
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) limit pamięci nie jest ustawiony
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) żądanie CPU nie jest ustawione
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) żądanie pamięci nie jest ustawione
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) obraz z najnowszym tagiem
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: Pod nie ma pasującej polityki sieciowej
[CRITICAL] http-echo apps/v1/Deployment: Kontener nie zawiera readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Kontener nie ma skonfigurowanego kontekstu bezpieczeństwa
[CRITICAL] http-echo apps/v1/Deployment: Nie znaleziono pasującego PodDisruptionBudget
[WARNING] http-echo apps/v1/Deployment: Wdrożenie nie ma ustawionej polityki podAntiAffinity
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/ServicePodobnie jak kubeval, kube-score zwraca niezerowy kod wyjścia w przypadku, gdy któryś z testów zakończył się błędem CRITICAL. Można również włączyć podobne przetwarzanie dla WARNING.
Ponadto istnieje możliwość sprawdzenia zasobów pod kątem zgodności z różnymi wersjami API (tak jak w kubeval). Jednak te informacje są 'hardcodowane' w samym kube-score: nie można wybrać innej wersji Kubernetes. Tego rodzaju ograniczenie może być dużym problemem, jeśli planujesz zaktualizować klaster lub masz kilka klastrów z różnymi wersjami K8s.
Zauważ, że z propozycją wdrożenia tej możliwości.
Więcej informacji o kube-score można znaleźć na .
Testy kube-score to doskonałe narzędzie do wprowadzania najlepszych praktyk, ale co jeśli potrzebujesz wprowadzić zmiany w teście lub dodać własne zasady? Niestety, nie jest to możliwe.
Kube-score nie jest rozszerzalny: nie można dodać ani dostosować polityk.
Jeśli potrzebujesz pisać niestandardowe testy do sprawdzenia zgodności z politykami przyjętymi w firmie, możesz skorzystać z jednego z czterech narzędzi: config-lint, copper, conftest lub polaris.
3. Config-lint
Config-lint to narzędzie do walidacji plików konfiguracyjnych w formacie YAML, JSON, Terraform, CSV oraz manifestów Kubernetes.
Można je zainstalować za pomocą na stronie projektu.
Aktualna wersja w momencie pisania oryginalnego artykułu to 1.5.0.
Config-lint nie zawiera wbudowanych testów do sprawdzania manifestów Kubernetes.
Aby przeprowadzić jakiekolwiek testy, należy stworzyć odpowiednie reguły. Zapisuje się je w plikach YAML, zwanych "zestawami reguł" (rulesets), i mają one następującą strukturę:
version: 1
description: Reguły dla plików specyfikacji Kubernetes
type: Kubernetes
files:
- "*.yaml"
rules:
# lista reguł(rule.yaml)
Przyjrzyjmy się jej bliżej:
- Pole
typeokreśla, jaki typ konfiguracji będzie używać config-lint. Dla manifestów K8s jest to zawszeKubernetes. - W polu
filesoprócz samych plików można określić katalog. - Pole
rulessłuży do definiowania testów użytkownika.
Załóżmy, że chcesz upewnić się, że obrazy w Deployment zawsze są pobierane z zaufanego repozytorium, takiego jak my-company.com/myapp:1.0. Reguła dla config-lint, która wykonuje podobną kontrolę, będzie wyglądać następująco:
- id: MY_DEPLOYMENT_IMAGE_TAG
severity: FAILURE
message: Deployment musi używać prawidłowego tagu obrazu
resource: Deployment
assertions:
- every:
key: spec.template.spec.containers
expressions:
- key: image
op: starts-with
value: "my-company.com/"(rule-trusted-repo.yaml)
Dla każdej reguły należy podać następujące atrybuty:
id— unikalny identyfikator reguły;severity— może być FAILURE, WARNING i NON_COMPLIANT;message— w przypadku naruszenia reguły wyświetla się treść tej linii;resource— typ zasobu, do którego stosuje się tę regułę;assertions— lista warunków, które będą oceniane w odniesieniu do danego zasobu.
W powyższej regule assertion o nazwie sprawdza, że wszystkie kontenery w Deployment (key: spec.templates.spec.containers) używają zaufanych obrazów (czyli zaczynających się od my-company.com/).
Pełny zestaw reguł wygląda następująco:
version: 1
description: Reguły dla plików specyfikacji Kubernetes
type: Kubernetes
files:
- "*.yaml"
rules:
- id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
severity: FAILURE
message: Deployment musi używać prawidłowego repozytorium obrazów
resource: Deployment
assertions:
- every:
key: spec.template.spec.containers
expressions:
- key: image
op: starts-with
value: "my-company.com/"(ruleset.yaml)
Aby przetestować regułę, zapiszmy ją jako check_image_repo.yaml. Uruchomimy sprawdzenie na pliku base-valid.yaml:
$ config-lint -rules check_image_repo.yaml base-valid.yaml
[
{
"AssertionMessage": "Każdy wyrażenie zawiodło: Wyrażenie 'And' zawiodło: obraz nie zaczyna się od 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": "Wdrożenie musi używać prawidłowego repozytorium obrazów",
"Status": "FAILURE"
}
]Sprawdzenie zakończyło się niepowodzeniem. Teraz sprawdźmy następny manifest z poprawnym repozytorium obrazów:
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)
Uruchamiamy ten sam test z powyższym manifestem. Nie wykryto problemów:
$ config-lint -rules check_image_repo.yaml image-valid-mycompany.yaml
[]Config-lint to obiecujący framework, który pozwala tworzyć własne testy do weryfikacji manifestów YAML Kubernetes za pomocą YAML DSL.
Ale co zrobić, jeśli potrzebna jest bardziej złożona logika i testy? Czy możliwości YAML nie są na to zbyt ograniczone? Co jeśli można by było tworzyć testy w pełnoprawnym języku programowania?
4. Copper
to framework do walidacji manifestów z użyciem niestandardowych testów (analogicznie do config-lint).
Jednak różni się od niego tym, że nie używa YAML do opisu testów. Zamiast tego testy można tworzyć w JavaScript. Copper zapewnia bibliotekę z wieloma podstawowymi narzędziami, które pomagają odczytywać informacje o obiektach Kubernetes i zgłaszać błędy.
Kolejność kroków do zainstalowania Copper można znaleźć w .
2.0.1 — najnowsza wersja tego narzędzia w chwili pisania oryginalnego artykułu.
Jak w przypadku config-lint, Copper nie ma wbudowanych testów. Napiszmy jeden. Niech sprawdza, czy wdrożenia używają obrazów kontenerowych wyłącznie z zaufanych repozytoriów takich jak my-company.com.
Utwórz plik check_image_repo.js z następującą zawartością:
$$.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',"Obraz " + $.metadata.name + " nie pochodzi z repozytorium my-company.com", 1)
}
});
}
});Teraz, aby sprawdzić nasz manifest base-valid.yaml, użyj polecenia copper validate:
$ copper validate --in=base-valid.yaml --validator=check_image_tag.js
Weryfikacja no_company_repo nie powiodła się z powodu Obraz http-echo nie pochodzi z repozytorium my-company.com
Weryfikacja nie powiodła sięJasne, że za pomocą copper można przeprowadzać bardziej złożone testy — na przykład sprawdzać nazwy domen w manifestach Ingress lub odrzucać pod’y działające w trybie uprzywilejowanym.
W Copper wbudowane są różne funkcje pomocnicze:
DockerImageodczytuje podany plik wejściowy i tworzy obiekt z następującymi atrybutami:name— nazwa obrazu,tag— tag obrazu,registry— rejestr obrazów,registry_url— protokół (https://) i rejestr obrazów,fqin— pełna lokalizacja obrazu.
- veth_xdp_flush_bq()
findByNamepomaga znaleźć zasób według podanego typu (kind) i nazwy (name) z pliku wejściowego. - veth_xdp_flush_bq()
findByLabelspomaga znaleźć zasób według określonego typu (kind) i etykiet (labels).
Ze wszystkimi dostępnymi funkcjami pomocniczymi można zapoznać się .
Domyślnie ładuje cały plik YAML do zmiennej $$ i udostępnia ją dla skryptów (znany sposób dla tych, którzy mają doświadczenie w pracy z jQuery).
Główną zaletą Copper jest to, że nie trzeba uczyć się specjalistycznego języka i można korzystać z różnych możliwości JavaScript do tworzenia własnych testów, takich jak interpolacja łańcuchów, funkcje itp.
Należy również zauważyć, że obecna wersja Copper działa z wersją ES5 silnika JavaScript, a nie z ES6.
Szczegóły są dostępne na .
Jednak jeśli nie lubisz zbytnio JavaScript i wolisz język specjalnie przeznaczony do tworzenia zapytań i opisywania polityk, powinieneś zwrócić uwagę na conftest.
5. Conftest
Conftest to framework do weryfikacji danych konfiguracyjnych. Nadaje się również do testowania/weryfikacji manifestów Kubernetes. Testy opisywane są za pomocą specjalistycznego języka zapytań .
Instalację conftest można przeprowadzić przy użyciu , wskazówek na stronie projektu.
W momencie pisania oryginalnego artykułu najnowszą dostępną wersją była 0.18.2.
Na wzór config-lint i copper, conftest nie zawiera żadnych wbudowanych testów. Spróbujmy go i napiszmy własną politykę. Jak w poprzednich przykładach, będziemy sprawdzać, czy obrazy kontenerów pochodzą z zaufanego źródła.
Utwórz katalog conftest-checks, a w nim plik o nazwie check_image_registry.rego z następującą zawartością:
package main
deny[msg] {
input.kind == "Deployment"
image := input.spec.template.spec.containers[_].image
not startswith(image, "my-company.com/")
msg := sprintf("image '%v' doesn't come from my-company.com repository", [image])
}Teraz przetestujmy base-valid.yaml przez conftest:
$ conftest test --policy ./conftest-checks base-valid.yaml
FAIL - base-valid.yaml - obraz 'hashicorp/http-echo' nie pochodzi z repozytorium my-company.com
1 test, 1 zaliczony, 0 ostrzeżeń, 1 błądTest nieoczekiwanie nie przeszedł, ponieważ obrazy pochodzą z niezweryfikowanego źródła.
W pliku Rego definiujemy blok deny. Jego prawdziwość traktowana jest jako naruszenie. Jeśli bloków deny jest więcej, conftest sprawdza je niezależnie od siebie, a prawdziwość dowolnego z bloków interpretowana jest jako naruszenie.
Oprócz domyślnego wyjścia, conftest wspiera formaty JSON, TAP i tabelaryczny — bardzo przydatna funkcjonalność, jeśli trzeba wkomponować raporty w istniejący pipeline CI. Odpowiedni format można ustawić za pomocą flagi --output.
Aby ułatwić debugowanie polityk, w conftest znajdziesz flagę --trace. Wyświetli ślad, jak conftest analizuje wskazane pliki polityk.
Polityki conftest można publikować i dzielić się nimi w rejestrach OCI (Open Container Initiative) w postaci artefaktów.
Polecenia push i pull pozwalają publikować artefakt lub pobrać istniejący artefakt z dalekiego rejestru. Spróbujmy opublikować stworzoną przez nas politykę w lokalnym rejestrze Docker przy pomocy conftest push.
Uruchom lokalny rejestr Docker:
$ docker run -it --rm -p 5000:5000 registryW innym terminalu przejdź do wcześniej utworzonego katalogu conftest-checks i wykonaj następującą komendę:
$ conftest push 127.0.0.1:5000/amitsaha/opa-bundle-example:latestJeśli komenda przebiegła pomyślnie, zobaczysz wiadomość podobną do:
2020/06/10 14:25:43 przesłano zestaw z hashem: sha256:e9765f201364c1a8a182ca637bc88201db3417bacc091e7ef8211f6c2fd2609cTeraz stwórz tymczasowy katalog i wykonaj w nim polecenie conftest pull. Pobrał on do niego pakiet stworzony poprzednią komendą:
$ cd $(mktemp -d)
$ conftest pull 127.0.0.1:5000/amitsaha/opa-bundle-example:latestW tymczasowym katalogu pojawi się podkatalog policy, zawierający nasz plik z polityką:
$ tree
.
└── policy
└── check_image_registry.regoTesty można przeprowadzać bezpośrednio z repozytorium:
$ conftest test --update 127.0.0.1:5000/amitsaha/opa-bundle-example:latest base-valid.yaml
..
FAIL - base-valid.yaml - obraz 'hashicorp/http-echo' nie pochodzi z repozytorium my-company.comNiestety, DockerHub na razie nie jest wspierany. Więc uznaj, że masz szczęście, jeśli używasz (ACR) lub własnego rejestru.
Format artefaktów jest taki sam, jak pakietów (OPA), co umożliwia korzystanie z conftest do uruchamiania testów z istniejących pakietów OPA.
Więcej o współdzieleniu polityk i innych cechach conftest można znaleźć na .
6. Polaris
Ostatnim narzędziem, o którym mowa w tym artykule, jest . (Jego zeszłoroczna zapowiedź została — przyp. tłum.)
Polaris można zainstalować w klastrze lub używać w trybie wiersza poleceń. Jak już się domyśliliście, pozwala on na statyczną analizę manifestów Kubernetes.
Pracując w trybie wiersza poleceń, dostępne są wbudowane testy obejmujące takie obszary, jak bezpieczeństwo i najlepsze praktyki (podobnie jak w kube-score). Dodatkowo można tworzyć własne testy (jak w config-lint, copper i conftest).
Innymi słowy, Polaris łączy w sobie zalety obu kategorii narzędzi: z wbudowanymi i użytkownikami testami.
Aby zainstalować Polaris w trybie wiersza poleceń, skorzystaj z .
W momencie pisania oryginalnego artykułu dostępna była wersja 1.0.3.
Po zakończeniu instalacji można uruchomić polaris na manifeście base-valid.yaml za pomocą następującego polecenia:
$ polaris audit --audit-path base-valid.yamlZostanie wyświetlona linia w formacie JSON z szczegółowym opisem wykonanych testów i ich wynikami. Wyjście będzie miało następującą 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": "unknown",
"Nodes": 0,
"Pods": 2,
"Namespaces": 0,
"Controllers": 2
},
"Results": [
/* długa lista */
]
}Pełne wyjście jest dostępne .
Podobnie jak kube-score, Polaris wykrywa problemy w tych obszarach, w których manifest nie odpowiada najlepszym praktykom:
- Brak kontroli zdrowia podów.
- Nie określono tagów dla obrazów kontenerów.
- Kontener działa z uprawnieniami roota.
- Nie określono requestów i limitów dla pamięci i CPU.
Każdemu testowi w zależności od jego wyników przypisywana jest stopień krytyczności: warning lub danger. Aby dowiedzieć się więcej o dostępnych wbudowanych testach, zapoznaj się z .
Jeśli szczegóły nie są potrzebne, można użyć flagi --format score. W takim przypadku Polaris wyświetli liczbę w zakresie od 1 do 100 — score (czyli ocenę):
$ polaris audit --audit-path test-data/base-valid.yaml --format score
68Im ocena jest bliższa 100, tym wyższy poziom zgodności. Jeśli sprawdzisz kod wyjścia polecenia polaris audit, okaże się, że wynosi 0.
Wymusić polaris audit zakończenie pracy z kodem różnym od zera można za pomocą dwóch flag:
- Flaga
--set-exit-code-below-scoreprzyjmuje jako argument próg w zakresie 1-100. W takim przypadku polecenie zakończy się kodem wyjścia 4, jeśli ocena będzie poniżej progu. To bardzo wygodne, gdy masz pewną wartość progową (powiedzmy 75) i potrzebujesz uzyskać alert, jeśli ocena spadnie poniżej. - Flaga
--set-exit-code-on-dangerspowoduje, że polecenie zakończy się kodem 3, jeśli jeden z testów typu danger się nie powiedzie.
Teraz spróbujmy stworzyć test niestandardowy, który sprawdza, czy obraz pochodzi z zaufanego repozytorium. Testy niestandardowe definiuje się w formacie YAML, a sam test opisuje się za pomocą schemy JSON.
Następujący fragment kodu YAML opisuje nowy test, nazywany checkImageRepo:
checkImageRepo:
successMessage: Rejestr obrazów jest ważny
failureMessage: Rejestr obrazów jest nieważny
category: Obrazy
target: Kontener
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
properties:
image:
type: string
pattern: ^my-company.com/.+$Przyjrzyjmy się mu bliżej:
successMessage— ten komunikat zostanie wyświetlony, jeśli test zakończy się sukcesem;failureMessage— ten komunikat zostanie wyświetlony w przypadku niepowodzenia;category— wskazuje na jedną z kategorii:Obrazy,Kontrole stanu,Bezpieczeństwo,NetworkingiZasoby;target— określa, do jakiego rodzaju obiektu (spec) stosuje się test. Możliwe wartości:Kontener,PodlubKontroler;- Sam test jest definiowany w obiekcie
schemaza pomocą schemy JSON. W tym teście słowo kluczowewzorzecjest używane do porównania źródła obrazu z wymaganym.
Aby uruchomić powyższy test, należy stworzyć następującą konfigurację Polaris:
checks:
checkImageRepo: danger
customChecks:
checkImageRepo:
successMessage: Rejestr obrazów jest ważny
failureMessage: Rejestr obrazów jest nieważny
category: Obrazy
target: Kontener
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
properties:
image:
type: string
pattern: ^my-company.com/.+$(polaris-conf.yaml)
Rozbijmy plik:
- W polu
checksokreślają testy i poziom ich krytyczności. Ponieważ pożądane jest otrzymywanie ostrzeżenia, gdy obraz pochodzi z niepewnego źródła, ustawiamy tutaj poziomdanger. - Test
checkImageReponastępnie definiuje się w obiekciecustomChecks.
Zapisz plik jako custom_check.yaml. Teraz można uruchomić polaris audit z manifestem YAML, który wymaga weryfikacji.
Przetestujemy nasz manifest base-valid.yaml:
$ polaris audit --config custom_check.yaml --audit-path base-valid.yamlZespół polaris audit wykonał tylko test niestandardowy, który został określony powyżej, i nie zakończył się sukcesem.
Jeśli poprawisz obraz na my-company.com/http-echo:1.0, Polaris zakończy się pomyślnie. Manifest z wprowadzonymi zmianami już istnieje w , więc możesz sprawdzić poprzednie polecenie w manifeście image-valid-mycompany.yaml.
Teraz pojawia się pytanie: jak uruchamiać wbudowane testy razem z użytkownikami? To proste! Wystarczy dodać identyfikatory wbudowanych testów do pliku konfiguracyjnego. W rezultacie przybierze on następującą formę:
checks:
cpuRequestsMissing: warning
cpuLimitsMissing: warning
# Inne wbudowane kontrole..
# ..
# kontrole użytkownika
checkImageRepo: danger # !!!
customChecks:
checkImageRepo: # !!!
successMessage: Rejestr obrazu jest ważny
failureMessage: Rejestr obrazu nie jest ważny
category: Obrazy
target: Kontener
schema:
'$schema': http://json-schema.org/draft-07/schema
type: object
properties:
image:
type: string
pattern: ^my-company.com/.+$(config_with_custom_check.yaml)
Przykład pełnego pliku konfiguracyjnego jest dostępny .
Sprawdź manifest base-valid.yaml, korzystając z wbudowanych i użytkowników testów, można za pomocą polecenia:
$ polaris audit --config config_with_custom_check.yaml --audit-path base-valid.yamlPolaris uzupełnia wbudowane testy o testy użytkowników, łącząc to, co najlepsze z obu światów.
Z drugiej strony, niemożność korzystania z bardziej zaawansowanych języków, takich jak Rego czy JavaScript, może być ograniczeniem dla tworzenia bardziej wyszukanych testów.
Dodatkowe informacje o Polaris są dostępne na .
Podsumowanie
Chociaż istnieje wiele narzędzi do sprawdzania i oceniania plików YAML Kubernetes, ważne jest, aby mieć jasny obraz tego, jak testy będą projektowane i wykonywane.
Na przykład, jeśli zacząć od manifestów Kubernetes przechodzących przez pipeline, kubeval mógłby być pierwszym krokiem w takim pipeline. Będzie on monitorował, czy definicje obiektów są zgodne ze schemą API Kubernetes.
Po zakończeniu takiej kontroli można by przejść do bardziej zaawansowanych testów, takich jak zgodność z najlepszymi praktykami oraz specjalnymi politykami. I tutaj przydałyby się kube-score i Polaris.
Osobom z złożonymi wymaganiami i potrzebą szczegółowego konfigurowania testów odpowiadałyby copper, config-lint oraz conftest.
Conftest i config-lint wykorzystują YAML do definiowania testów użytkowników, a copper daje dostęp do pełnoprawnego języka programowania, co sprawia, że jest dość atrakcyjnym wyborem.
Z drugiej strony, czy warto skorzystać z jednego z tych narzędzi i w ten sposób tworzyć wszystkie testy ręcznie, czy raczej wybrać Polaris i dodać tylko to, co potrzebne? Na to pytanie nie ma jednoznacznej odpowiedzi.
Tabela poniżej zawiera krótkie podsumowanie każdego narzędzia:
Narzędzie
Przeznaczenie
Wady
Testy użytkowników
kubeval
Sprawdza manifesty YAML pod kątem zgodności z określoną wersją schematu API
Nie obsługuje CRD
Nie
kube-score
Analizuje manifesty YAML pod kątem zgodności z najlepszymi praktykami
Nie można wybrać własnej wersji API Kubernetes do weryfikacji zasobów
Nie
miedź
Ogólny framework do tworzenia własnych testów JavaScript dla manifestów YAML
Brak wbudowanych testów. Uboga dokumentacja
Tak
config-lint
Ogólny framework do tworzenia testów w obiektowym języku wbudowanym w YAML. Obsługuje różne formaty konfiguracji (np. Terraform)
Brak gotowych testów. Wbudowane asercje i funkcje mogą być niewystarczające
Tak
conftest
Framework do tworzenia własnych testów w Rego (specjalizowanym języku zapytań). Umożliwia dzielenie się politykami za pośrednictwem pakietów OCI
Brak wbudowanych testów. Konieczność nauki Rego. Docker Hub nie jest obsługiwany podczas publikacji polityk
Tak
Polaris
Analizuje manifesty YAML pod kątem zgodności ze standardowymi najlepszymi praktykami. Umożliwia tworzenie własnych testów za pomocą JSON Schema
Możliwości testów opartych na JSON Schema mogą być niewystarczające
Tak
Ponieważ te narzędzia nie są zależne od dostępu do klastra Kubernetes, łatwo je zainstalować. Umożliwiają filtrowanie plików źródłowych i zapewniają szybką informację zwrotną dla autorów pull requestów w projektach.
P.S. od tłumacza
Przeczytaj także na naszym blogu:
- «»;
- «»;
- «».
Źródło: habr.com
