Валидация на Kubernetes YAML за съответствие с добри практики и политики

Прим. прев.: С нарастващия брой YAML конфигурации за K8s средиите, необходимостта от автоматизирана проверка става все по-актуална. Авторът на този преглед не само е събрал съществуващите решения за тази задача, но и е разгледал как те функционират, използвайки пример с Deployment. Станаха много информативни за всички, на които им е интересна тази тема.

Валидация на Kubernetes YAML за съответствие с добри практики и политики

TL;DR: В статията се сравняват шест статични инструмента за проверка и оценка на YAML файлове на Kubernetes за съответствие с добри практики и изисквания.

Работните товари на Kubernetes обикновено се определят под формата на YAML документи. Един от проблемите с YAML е сложността при задаването на ограничения или взаимовръзки между файловете на манифестите.

Какво, ако трябва да се уверим, че всички образи, разгръщани в кластера, идват от доверен регистър?

Как да предотвратим изпращането на Deployment-и в кластера, за които не са зададени PodDisruptionBudgets?

Интеграцията на статично тестване позволява откриването на грешки и нарушения на политиките още в етапа на разработка. По този начин се увеличават гаранциите за точността и сигурността на определенията за ресурси и се увеличава вероятността production натоварванията да следват добри практики.

Екосистемата за статична проверка на YAML файлове на Kubernetes може да се раздели на следните категории:

  • API валидатори. Инструменти в тази категория проверяват YAML манифеста за съответствие на изискванията на API сървъра на Kubernetes.
  • Готови тестери. Инструментите в тази категория идват с готови тестове за сигурност, съответствие с добри практики и т.н.
  • Персонализирани валидатори. Представителите на тази категория позволяват създаването на потребителски тестове на различни езици, например на Rego и Javascript.

В тази статия ще опишем и сравним шест различни инструмента:

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

Добре, да започнем!

Проверка на Deployment-ите

Преди да преминем към сравнение на инструментите, нека създадем някаква основа, на която ще ги тестваме.

По-долу манифест съдържа редица грешки и несъответствия с добрите практики: колко от тях ще можете да намерите?

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)

Ще използваме този YAML за сравнение на различни инструменти.

Горепосоченият манифест base-valid.yaml и други манифести от тази статия можете да намерите в Git-репозиторий.

Манифестът описва уеб приложение, чиято основна задача е да отговаря с "Hello World" на порт 5678. Може да бъде разположен с следната команда:

kubectl apply -f hello-world.yaml

А ето как да проверите работата:

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

Сега отидете на http://localhost:8080 и потвърдете, че приложението работи. Но следва ли то на добрите практики? Нека проверим.

1. Kubeval

В основата kubeval е идеята, че всяко взаимодействие с Kubernetes става чрез неговия REST API. С други думи, можете да използвате схемата на API, за да проверите дали този YAML й отговаря. Нека да разгледаме пример.

Инструкции за инсталиране на kubeval са налични на сайта на проекта.

Към момента на написването на оригиналната статия беше налична версия 0.15.0.

След инсталирането нека "нахраним" манифеста, показан по-горе:

$ kubeval base-valid.yaml
PASS - base-valid.yaml contains a valid Deployment (http-echo)
PASS - base-valid.yaml contains a valid Service (http-echo)

При успех kubeval ще приключи с exit-код 0. Можете да проверите както следва:

$ echo $?
0

Сега да опитаме kubeval с друг манифест:

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)

Можете ли на пръв поглед да определите проблема? Стартираме:

$ kubeval kubeval-invalid.yaml
WARN - kubeval-invalid.yaml contains an invalid Deployment (http-echo) - selector: selector is required
PASS - kubeval-invalid.yaml contains a valid Service (http-echo)

# да проверим кодa на завръщане
$ echo $?
1

Ресурсът не преминава проверката.

Deployment-ите, използващи версия на API apps/v1, трябва да включват селектор, съответстващ на етикета на pod-a. Горният манифест не включва селектор, затова kubeval съобщи за грешка и приключи с ненулев код.

Интересно е какво ще се случи, ако изпълним kubectl apply -f с този манифест?

Какво ж, да опитаме:

$ kubectl apply -f kubeval-invalid.yaml
error: error validating "kubeval-invalid.yaml": error validating data: ValidationError(Deployment.spec):
missing required field "selector" in io.k8s.api.apps.v1.DeploymentSpec; if you choose to ignore these errors,
turn validation off with --validate=false

Това е грешката, за която предупреди kubeval. Можете да я коригирате, като добавите селектор:

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)

Предимството на инструменти като kubeval е, че такива грешки могат да бъдат уловени в ранните етапи на цикъла на разгръщане.

Освен това, за тези проверки не е необходим достъп до клъстера: те могат да се извършват офлайн.

По подразбиране kubeval проверява ресурсите за съответствие с най-новата схема на Kubernetes API. Въпреки това, в повечето случаи може да е необходимо да проверите за съответствие с конкретна версия на Kubernetes. Това може да стане с помощта на флага --kubernetes-version:

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

Обърнете внимание, че версията трябва да бъде указана в формата Major.Minor.Patch.

За да видите списък с версиите, за които се поддържа проверка, посетете JSON схемата на GitHub, която kubeval използва за валидация. Ако искате да стартирате kubeval офлайн, изтеглете схемите и посочете тяхното местоположение локално с помощта на флага --schema-location.

Освен отделни YAML файлове, kubeval също може да работи с директории и stdin.

Освен това, Kubeval лесно се интегрира в CI пайплайн. Тези, които искат да провеждат тестове преди изпращане на манифестите в клъстера, ще се радват да научат, че kubeval поддържа три формата на изход:

  1. Обикновен текст;
  2. JSON;
  3. Test Anything Protocol (TAP).

И може да се използва всеки от тези формати за допълнително парсиране на изхода, за да се формира обобщение на резултатите от желания вид.

Един от недостатъците на kubeval е, че в момента не може да проверява за съответствие с Custom Resource Definitions (CRDs). Въпреки това, kubeval може да бъде настроен да ги игнорира.

Kubeval е отличен инструмент за проверка и оценка на ресурсите; обаче, трябва да се подчертае, че успешното преминаване на теста не гарантира, че ресурсът отговаря на най-добрите практики.

Например, използването на таг latest В контейнера не отговарят на най-добрите практики. Въпреки това kubeval не счита това за грешка и не съобщава за нея. Тоест проверката на такъв YAML ще приключи без предупреждения.

Но какво, ако трябва да оценим YAML и да открием нарушения като етикета latest? Как проверить YAML-файл на соответствие лучшим практикам?

2. Kube-score

Kube-score анализира YAML манифести и ги оценява по вградени тестове. Тези тестове се избират въз основа на препоръки за сигурност и най-добри практики, например:

  • Стартиране на контейнера не под root.
  • Наличие на проверки за здравето на pod-овете.
  • Задаване на заявки и ограничения на ресурсите.

След теста се издават три резултата: OK, WARNING и CRITICAL.

Kube-score може да се опита онлайн или да се инсталира локално.

По време на написването на оригиналната статия най-новата версия на kube-score беше 1.7.0.

Нека да го тестваме на нашия манифест base-valid.yaml:

$ kube-score score base-valid.yaml

apps/v1/Deployment http-echo
[CRITICAL] Container Image Tag
  · http-echo -> Image with latest tag
      Using a fixed tag is recommended to avoid accidental upgrades
[CRITICAL] Pod NetworkPolicy
  · The pod does not have a matching network policy
      Create a NetworkPolicy that targets this pod
[CRITICAL] Pod Probes
  · Container is missing a readinessProbe
      A readinessProbe should be used to indicate when the service is ready to receive traffic.
      Without it, the Pod is risking to receive traffic before it has booted. It is also used during
      rollouts, and can prevent downtime if a new version of the application is failing.
      More information: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[CRITICAL] Container Security Context
  · http-echo -> Container has no configured security context
      Set securityContext to run the container in a more secure context.
[CRITICAL] Container Resources
  · http-echo -> CPU limit is not set
      Resource limits are recommended to avoid resource DDOS. Set resources.limits.cpu
  · http-echo -> Memory limit is not set
      Resource limits are recommended to avoid resource DDOS. Set resources.limits.memory
  · http-echo -> CPU request is not set
      Resource requests are recommended to make sure that the application can start and run without
      crashing. Set resources.requests.cpu
  · http-echo -> Memory request is not set
      Resource requests are recommended to make sure that the application can start and run without crashing.
      Set resources.requests.memory
[CRITICAL] Deployment has PodDisruptionBudget
  · No matching PodDisruptionBudget was found
      It is recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes
      maintenance operations, such as when draining a node.
[WARNING] Deployment has host PodAntiAffinity
  · Deployment does not have a host podAntiAffinity set
      It is recommended to set a podAntiAffinity that stops multiple pods from a deployment from
      being scheduled on the same node. This increases availability in case the node becomes unavailable.

YAML преминава проверки на kubeval, докато kube-score посочва следните недостатъци:

  • Не са настроени проверки за готовност.
  • Липсват заявки и ограничения за ресурси CPU и памет.
  • Не са зададени бюджети за нарушаване на Pod.
  • Липсват правила за антиподелно съществуване (anti-affinity) за максимизиране на наличността.
  • Контейнерът работи с root права.

Всичко това са разумни забележки относно недостатъците, които трябва да бъдат отстранени, за да стане внедряването по-ефективно и надеждно.

Екип kube-score извежда информация в удобен за четене формат, включваща всички нарушения от тип WARNING и CRITICAL, което е много полезно по време на разработката.

Желаещите да използват този инструмент в рамките на CI пайплайна могат да активират по-компактен изход с помощта на флага --output-format ci (в този случай се изписват и тестовете с резултат 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 не е зададен
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Лимит за памет не е зададен
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Заявка за CPU не е зададена
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Заявка за памет не е зададена
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Образ с последния таг
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: Подът няма съответстваща мрежова политика
[CRITICAL] http-echo apps/v1/Deployment: Контейнерът липсва readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Контейнерът няма конфигуриран security context
[CRITICAL] http-echo apps/v1/Deployment: Не е намерен съответстващ PodDisruptionBudget
[WARNING] http-echo apps/v1/Deployment: Внедряването няма зададена podAntiAffinity за хост
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

По аналогия с kubeval, kube-score връща ненулев код на изхода при наличие на тест, завършил с грешка. CRITICAL. Може също така да се активира подобна обработка и за WARNING.

Освен това, има възможност за проверка на ресурсите за съответствие с различни версии на API (както в kubeval). Обаче тази информация е 'hardcoded' в самия kube-score: не може да се избере друга версия на Kubernetes. Такова ограничение може да се окаже голям проблем, ако планирате да обновите клъстера или имате няколко клъстера с различни версии на K8s.

Обърнете внимание, че вече има проблем с предложение да се реализира тази възможност.

Повече информация за kube-score може да се намери на официалния сайт.

Тестовете на kube-score са отличен инструмент за внедряване на добри практики, но какво, ако е необходимо да внесете промени в теста или да добавите свои собствени правила? За съжаление, това не е възможно.

Kube-score не е разширяем: не можете да добавите или настроите политики.

Ако е необходимо да пишете потребителски тестове за проверки на съответствие с политиките, приети в компанията, можете да използвате един от следните четири инструмента: config-lint, copper, conftest или polaris.

3. Config-lint

Config-lint е инструмент за валидация на конфигурационни файлове във формат YAML, JSON, Terraform, CSV и манифести на Kubernetes.

Може да бъде инсталиран чрез инструкциите на сайта на проекта.

Текущото издание към момента на написване на оригиналната статия е 1.5.0.

Config-lint няма вградени тестове за проверка на манифести на Kubernetes.

За провеждане на тестове е необходимо да се създадат съответните правила. Те се записват в YAML файлове, наречени „набори от правила“ (rulesets), и имат следната структура:

version: 1
description: Правила за файлове на Kubernetes спецификация
типа: Kubernetes
files:
  - "*.yaml"
rules:
   # списък с правила

(rule.yaml)

Нека да я разгледаме по-подробно:

  • Поле тип посочва, какъв тип конфигурация ще използва config-lint. За манифестите K8s това е винаги Kubernetes.
  • В полето файлове освен самите файлове, може да се посочи директория.
  • Поле rules предназначено за задаване на потребителски тестове.

Да предположим, че искате да се уверите, че образите в Deployment-а винаги се изтеглят от доверен репозиторий, като my-company.com/myapp:1.0. Правилото за config-lint, което извършва такава проверка, ще изглежда по следния начин:

- id: MY_DEPLOYMENT_IMAGE_TAG
  severity: FAILURE
  message: Deployment трябва да използва валиден етикет на изображение
  resource: Deployment
  assertions:
    - every:
        key: spec.template.spec.containers
        expressions:
          - key: image
            op: starts-with
            value: "my-company.com/"

(rule-trusted-repo.yaml)

За всяко правило трябва да бъдат посочени следните атрибути:

  • id — уникален идентификатор на правилото;
  • сериозност — може да бъде FAILURE, WARNING и NON_COMPLIANT;
  • message — при нарушение на правилото, съдържанието на този ред ще се покаже;
  • ресурс — тип на ресурса, към който се прилага това правило;
  • условия — списък от условия, които ще се оценяват относно дадения ресурс.

В правилото по-горе условие с имената every проверява, че всички контейнери в Deployment-а (key: spec.templates.spec.containers) използват доверени образи (т.е. образи, които започват с my-company.com/).

Пълен набор правилата изглежда по следния начин:

version: 1
description: Правила за файлове на Kubernetes спецификация
type: Kubernetes
files:
  - "*.yaml"
rules:

 - id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
    seriousness: FAILURE
    message: Deployment трябва да използва валиден репозиторий на образа
    resource: Deployment
    assertions:
      - every:
          key: spec.template.spec.containers
          expressions:
            - key: image
              op: starts-with
              value: "my-company.com/"

(ruleset.yaml)

За да тестваме теста, нека го запазим като check_image_repo.yaml. Нека стартираме проверка над файла base-valid.yaml:

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

[
  {
  "AssertionMessage": "Every expression fails: And expression fails: image does not start with 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": "Deployment must use a valid image repository",
  "Status": "FAILURE"
  }
]

Проверката завърши неуспешно. Сега нека проверим следния манифест с правилно хранилище за образи:

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)

Стартираме същия тест с горепосочения манифест. Проблеми не са открити:

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

Config-lint е обещаваща рамка, която ви позволява да създавате собствени тестове за проверка на YAML манифести на Kubernetes с помощта на YAML DSL.

Но какво да правим, ако необходима по-сложна логика и тестове? Не са ли възможностите на YAML твърде ограничени за това? Какво, ако можете да създавате тестове на пълен език за програмиране?

4. Copper

Copper V2 е рамка за валидиране на манифести с помощта на потребителски тестове (аналог на config-lint).

Обаче, за разлика от последното, тя не използва YAML за описание на тестовете. Вместо това тестовете могат да бъдат създавани на JavaScript. Copper предоставя библиотека с няколко основни инструмента, които помагат за извличане на информация за обекти в Kubernetes и докладване за грешки.

Последователността на стъпките за инсталиране на Copper можете да намерите в официалната документация.

2.0.1 — най-новото издание на този инструмент към момента на писане на оригиналната статия.

Както и config-lint, Copper няма вградени тестове. Нека напишем един. Нека той провери дали deployment-ите използват контейнерни образи само от доверени хранилища като my-company.com.

Създайте файл check_image_repo.js със следното съдържание:

$$.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',"Image " + $.metadata.name + " is not from my-company.com repo", 1)
            }
        });
    }
});

Сега, за да проверим нашия манифест base-valid.yaml, използвайте командата copper validate:

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

Проверка no_company_repo не успя с тежест 1, тъй като изображението http-echo не е от хранилището my-company.com

Разбираемо е, че с помощта на copper могат да се провеждат по-сложни тестове – например, да се проверяват домейн имена в манифестите Ingress или да се отхвърлят pod’и, работещи в привилегирован режим.

В Copper са вградени различни служебни функции:

  • DockerImage чете указан файл за вход и създава обект с следните атрибути:
    • name — име на образа,
    • tag — етикет на образа,
    • registry — регистър на образи,
    • registry_url — протокол (https://) и регистър на образи,
    • fqin — пълното местоположение на образа.
  • Функция findByName помага за откриване на ресурс по даден тип (kind) и име (name) от входния файл.
  • Функция findByLabels помага за откриване на ресурс по зададен тип (kind) и етикети (labels).

С всички налични служебни функции можете да се запознаете тук..

По подразбиране той зарежда целия входен YAML файл в променлива $$ и го прави достъпен за скриптове (познат метод за тези, които имат опит с jQuery).

Основното предимство на Copper е очевидно: не е нужно да усвоявате специализиран език и можете да използвате различни възможности на JavaScript за създаване на собствени тестове, като интерполация на низове, функции и т.н.

Трябва също да се отбележи, че текущата версия на Copper работи с версия ES5 на JavaScript движка, а не с ES6.

Подробности са налични на официалния сайт на проекта.

Въпреки това, ако не особено обичате JavaScript и предпочитате език, специално предназначен за написване на запитвания и описване на политики, трябва да обърнете внимание на conftest.

5. Conftest

Conftest е фреймворк за проверка на конфигурационни данни. Подходящ е и за тестване/верификация на манифести Kubernetes. Тестовете се описват с помощта на специализирания език за запитвания Rego.

Инсталирането на conftest може да стане с помощта на инструкциите, посочени на сайта на проекта.

Към момента на написване на оригиналната статия, последната налична версия беше 0.18.2.

Сходно с config-lint и copper, conftest не предлага никакви вградени тестове. Нека да го изпробваме и да напишем собствена политика. Както в предходните примери, ще проверим дали образите на контейнерите идват от надежден източник.

Създайте директория conftest-checks, а в нея – файл с име check_image_registry.rego със следното съдържание:

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])
}

Сега нека тестваме base-valid.yaml чрез conftest:

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

FAIL - base-valid.yaml - image 'hashicorp/http-echo' doesn't come from my-company.com repository
1 tests, 1 passed, 0 warnings, 1 failure

Тестът не успя, тъй като образите идват от ненадежден източник.

В Rego файла задаваме блок deny. Неговата истинност се разглежда като нарушение. Ако блоковете deny са няколко, conftest ги проверява независимо един от друг, и истинността на който и да е от блоковете се тълкува като нарушение.

В допълнение на стандартния изход, conftest поддържа JSON, TAP и табличен формат — изключително полезна възможност, ако искате да вградите отчетите в съществуващия CI пайплайн. Форматът може да се зададе с флага --output.

За улесняване на отстраняването на грешки в политиките в conftest има флаг --trace. Той извежда трасировка на начина, по който conftest парсира зададените файлове с политики.

Политиките на conftest могат да се публикуват и споделят в OCI регистри (Open Container Initiative) под формата на артефакти.

Отбори push и pull позволяват публикуването на артефакт или извличането на съществуващ артефакт от отдалечен регистър. Нека опитаме да публикуваме създадената от нас политика в локалния Docker регистър с помощта на conftest push.

Стартирайте локален Docker регистър:

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

В друг терминал преминете в създадената по-рано директория conftest-checks и изпълнете следната команда:

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

Ако командата е успешна, ще видите съобщение от следен тип:

2020/06/10 14:25:43 pushed bundle with digest: sha256:e9765f201364c1a8a182ca637bc88201db3417bacc091e7ef8211f6c2fd2609c

Сега създайте времена директория и изпълнете команда в нея conftest pull. Тя ще изтегли пакетът, създаден от предходната команда:

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

В временната директория ще се появи поддиректория policy, съдържаща нашия файл с политика:

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

Тестовете могат да се провеждат директно от репозитория:

$ conftest test --update 127.0.0.1:5000/amitsaha/opa-bundle-example:latest base-valid.yaml
..
FAIL - base-valid.yaml - image 'hashicorp/http-echo' doesn't come from my-company.com repository
2 tests, 1 passed, 0 warnings, 1 failure

За съжаление, DockerHub в момента не се поддържа. Затова можете да смятате, че вие имате късмет, ако използвате Azure Container Registry (ACR) или собствен регистър.

Форматът на артефактите е същият като на пакетите Open Policy Agent (OPA), което позволява използването на conftest за изпълнение на тестове от съществуващи пакети OPA.

Повече за споделянето на политики и други характеристики на conftest можете да намерите на официалния сайт на проекта.

6. Polaris

Последният инструмент, който ще разгледаме в тази статия, е Polaris. (Неговото предишно анонсиране вече преведохме — бел. прев.)

Polaris може да бъде инсталиран в клъстер или да се използва в режим на команден ред. Както вече се досещате, той позволява статичен анализ на манифестите на Kubernetes.

При работа в режим на команден ред са налични вградени тестове, обхващащи области като сигурност и най-добри практики (по аналогия с kube-score). Освен това, можете да създавате собствени тестове (както в config-lint, copper и conftest).

С други думи, Polaris съчетава предимствата на двата вида инструменти: с вградени и персонализирани тестове.

За инсталиране на Polaris в режим на команден ред, използвайте инструкции на сайта на проекта.

Към момента на написване на оригиналната статия е налична версия 1.0.3.

След завършване на инсталацията можете да стартирате polaris на манифест base-valid.yaml с помощта на следната команда:

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

Тя ще изведе ред в JSON формат с подробности за извършените тестове и техните резултати. Изходът ще има следната структура:

{
  "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": [
    /* дълъг списък */
  ]
}

Пълният изход е наличен тук..

Както kube-score, Polaris идентифицира проблеми в области, където манифестът не отговаря на най-добрите практики:

  • Липсват проверки за здравето на подовете.
  • Не са зададени тагове за контейнерните образи.
  • Контейнерът работи с root права.
  • Не са зададени заявки и лимити за памет и CPU.

На всеки тест, в зависимост от резултата му, се присвоява степен на критичност: warning или danger. За повече информация относно наличните вградени тестове, обърнете се към документацията.

Ако подробности не са нужни, можете да зададете флага --format score. В този случай Polaris ще изведе число в диапазона от 1 до 100 — score (т.е. оценка):

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

Колкото оценката е по-близо до 100, толкова по-висока е степента на съответствие. Ако проверите exit-кода на командата polaris audit, ще се окаже, че той е равен на 0.

Да накарате polaris audit да завършва с ненулев код може да стане с помощта на два флага:

  • FPIPE --set-exit-code-below-score приема аргументи с прагово значение в диапазона 1-100. В такъв случай, командата ще завърши с exit-код 4, ако оценката е под прага. Това е много полезно, когато имате определено прагово значение (да кажем, 75) и искате да получите предупреждение, ако оценката падне под него.
  • FPIPE --set-exit-code-on-danger ще доведе до завършване на командата с код 3, ако един от тестовете за опасност е неуспешен.

Сега нека да опитаме да създадем потребителски тест, който проверява дали образът идва от надежден репозиторий. Потребителските тестове се задават във формат YAML, а самият тест се описва с помощта на JSON Schema.

Следният фрагмент от YAML-код описва нов тест, наречен checkImageRepo:

checkImageRepo:
  successMessage: Регистърът на изображенията е валиден
  failureMessage: Регистърът на изображенията не е валиден
  category: Изображения
  target: Контейнер
  schema:
    '$schema': http://json-schema.org/draft-07/schema
    type: object
    properties:
      image:
        type: string
        pattern: ^my-company.com/.+$

Нека да го разгледаме по-подробно:

  • successMessage — тази строка ще бъде изведена, ако тестът завърши успешно;
  • failureMessage — това съобщение ще се покаже в случай на неуспех;
  • category — посочва една от категориите: Изображения, Здравословни проверки, Сигурност, Нетворкинг и Ресурси;
  • target— определя върху какъв тип обект (spec) се прилага тестът. Възможни стойности: Контейнер, Под или Контролер;
  • Самият тест се задава в обект schema с помощта на JSON schema. В този тест ключовата дума pattern се използва за сравнението на източника на образа с изискванията.

За да стартирате изброения по-горе тест, е необходимо да създадете следната конфигурация Polaris:

checks:
  checkImageRepo: danger
customChecks:
  checkImageRepo:
    successMessage: Регистърът на изображенията е валиден
    failureMessage: Регистърът на изображенията не е валиден
    category: Изображения
    target: Контейнер
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(polaris-conf.yaml)

Нека разгледаме файла:

  • В полето checks определят тестовете и техните нива на критичност. Понеже е желателно да получим предупреждение, когато образ се взима от ненадежден източник, задаваме тук ниво danger.
  • Самият тест checkImageRepo после се регистрира в обекта customChecks.

Запазете файла като custom_check.yaml. Сега може да стартирате polaris audit с YAML манифест, който изисква проверка.

Тествайте нашия манифест base-valid.yaml:

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

Екип polaris audit изпълни единствено потребителския тест, зададен по-горе, и той не беше успешен.

Ако коригирате образа на my-company.com/http-echo:1.0, Polaris ще завърши успешно. Манифест с измененията вече има в репозитории, така че можете да проверите предишната команда в манифеста image-valid-mycompany.yaml.

Сега възниква въпросът: как да стартирате вградени тестове заедно с потребителските? Лесно! Просто трябва да добавите идентификаторите на вградените тестове в конфигурационния файл. В резултат той ще придобие следния вид:

checks:
  cpuRequestsMissing: warning
  cpuLimitsMissing: warning
  # Други вградени проверки..
  # ..
  # потребителски проверки
  checkImageRepo: danger # !!!
customChecks:
  checkImageRepo:        # !!!
    successMessage: Регистърът на изображение е валиден
    failureMessage: Регистърът на изображение не е валиден
    category: Images
    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)

Примерен пълен конфигурационен файл е наличен тук..

Провери манифеста base-valid.yaml, използвайки вградени и потребителски тестове, може да се направи с командата:

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

Polaris допълва вградените тестове с потребителски, обединявайки най-доброто от двата свята.

От друга страна, невъзможността да се използват по-мощни езици, като Rego или JavaScript, може да бъде ограничаващ фактор, пречещ на създаването на по-изтънчени тестове.

Допълнителна информация за Polaris е налична на сайта на проекта.

Резюме

Въпреки че съществуват много инструменти за проверка и оценка на YAML файлове на Kubernetes, важно е да имате ясна представа за това как тестовете ще бъдат проектирани и изпълнени.

Например, ако вземем манифестите на Kubernetes, преминаващи през пайплайна, kubeval може да бъде първата стъпка в такъв пайплайн. Той би наблюдавал дали определенията на обектите отговарят на схемата на API на Kubernetes.

След завършването на подобна проверка може да се премине към по-изтънчени тестове, като съответствие на стандартни най-добри практики и специфични политики. И тук биха послужили kube-score и Polaris.

На тези, които имат сложни изисквания и необходимост от детайлно настройване на тестовете, биха им подхождали copper, config-lint и conftest..

Conftest и config-lint използват YAML за задаване на потребителски тестове, а copper предоставя достъп до пълен език за програмиране, което го прави доста привлекателен избор.

От друга страна, струва ли си да се възползвате от един от тези инструменти и следователно да създадете всички тестове ръчно, или е по-добре да изберете Polaris и да допишете само това, което е нужно? Няма еднозначен отговор на този въпрос.

По-долу таблицата съдържа кратко описание на всеки инструмент:

Инструмент
Предназначение
Недостатъци
Потребителски тестове

kubeval
Проверява YAML манифести за съответствие с определена версия на API схемата
Не може да работи с CRD
Не

kube-score
Анализира YAML манифести за съответствие с най-добрите практики
Няма възможност да изберете собствена версия на Kubernetes API за проверка на ресурсите
Не

медна
Общ фреймворк за създаване на собствени JavaScript тестове за YAML манифести
Няма вградени тестове. Оскъдна документация
Да

config-lint
Общ фреймворк за създаване на тестове на предметно-ориентиран език, вграден в YAML. Поддържа различни формати на конфигурации (напр. Terraform)
Няма готови тестове. Възможно е вградените assert-и и функции да не са достатъчни
Да

conftest
Фреймворк за създаване на собствени тестове на Rego (специализиран език за запитвания). Позволява споделяне на политики чрез OCI пакети
Няма вградени тестове. Трябва да се запознае с Rego. Docker Hub не се поддържа при публикуване на политики
Да

Polaris
Анализира YAML манифести за съответствие с общи най-добри практики. Позволява създаване на собствени тестове с помощта на JSON Schema
Възможностите за тестове, основани на JSON Schema, може да не са достатъчни
Да

Тъй като тези инструменти не зависят от достъп до Kubernetes клъстера, те са лесни за инсталиране. Позволяват филтриране на входните файлове и предоставят бърза обратна връзка на авторите на pull request-ове в проектите.

P.S. от преводача

Прочетете също в нашия блог:

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster