Валідація Kubernetes YAML на відповідність кращим практикам та політикам

Прим. перев.: Зі зростанням числа YAML-конфігурацій для K8s-оточень все більш актуальною стає потреба в їхній автоматизованій перевірці. Автор цього огляду не просто відібрав існуючі рішення для цього завдання, а й на прикладі Deployment'а подивився, як вони працюють. Вийшло дуже інформативно для тих, кому ця тема цікава.

Валідація Kubernetes YAML на відповідність кращим практикам та політикам

TL, д-р: У статті порівнюються шість статичних інструментів перевірки та оцінки 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. мідь;
  5. conftest;
  6. Поляріс.

Що ж, давайте приступимо!

Перевірка 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)

# проверим код возврата
$ echo $?
1

Ресурс не проходить перевірку.

Deployment'и, які використовують версію API apps/v1, повинні включати селектор, що відповідає мітці pod'а. Маніфест вище не включає селектор, тому 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

Саме та помилка, про яку попереджав кубевал. Виправити її можна, додавши селектор:

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 оффлайн, скачайте схеми та вкажіть їхнє локальне розташування за допомогою прапора --schema-location.

Крім окремих YAML-файлів, кубевал також може працювати з директоріями та 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'ів.
  • Завдання request'ів та limit'ов ресурсів.

За підсумками тесту видається три результати: OK, УВАГА и КРИТИЧНІ.

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 вказує на такі недоліки:

  • Не налаштовано перевірку готовності.
  • Відсутні request'и та limit'и на ресурси CPU та пам'ять.
  • Не задано Pod disruption budgets.
  • Відсутні правила роздільного існування (anti-affinity) для максимізації доступності.
  • Контейнер виконується під root'ом.

Все це резонні зауваження про недоліки, які слід усунути, щоб Deployment став більш ефективним і надійним.

Команда kube-score виводить інформацію у легкочитаній формі з включенням усіх порушень типу УВАГА и КРИТИЧНІщо дуже допомагає під час розробки.

Бажаючі використовувати цей інструмент у рамках пайплайну 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 limit is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Memory limit is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU request is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Memory request is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Image with latest tag
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: The pod does not have a matching network policy
[CRITICAL] http-echo apps/v1/Deployment: Container is missing a readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Container has no configured security context
[CRITICAL] http-echo apps/v1/Deployment: No matching PodDisruptionBudget was found
[WARNING] http-echo apps/v1/Deployment: Deployment does not have a host podAntiAffinity set
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

За аналогією з kubeval, kube-score повертає ненульовий код виходу за наявності тесту, що завершився помилкою КРИТИЧНІ. Також можна включити подібну обробку і для УВАГА.

Крім того, є можливість перевірки ресурсів на відповідність різним версіям API (як і kubeval). Однак ця інформація за 'hardcode'на в самому kube-score: вибрати іншу версію Kubernetes не можна. Подібне обмеження може стати великою проблемою, якщо ви збираєтеся оновити кластер або у вас є кілька кластерів з різними версіями K8s.

Зверніть увагу, що вже є issue із пропозицією реалізувати цю можливість.

Докладніше про 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: Rules for Kubernetes spec files
type: Kubernetes
files:
  - "*.yaml"
rules:
   # список правил

(rule.yaml)

Давайте вивчимо її уважніше:

  • Поле type вказує, який тип конфігурації використовуватиме config-lint. Для маніфестів K8s це завжди Kubernetes.
  • В полі files Крім самих файлів можна вказати директорію.
  • Поле rules призначено для завдання тестів користувача.

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

- id: MY_DEPLOYMENT_IMAGE_TAG
  severity: FAILURE
  message: Deployment must use a valid image tag
  resource: Deployment
  assertions:
    - every:
        key: spec.template.spec.containers
        expressions:
          - key: image
            op: starts-with
            value: "my-company.com/"

(rule-trusted-repo.yaml)

Для кожного правила мають бути зазначені такі атрибути:

  • id - Унікальний ідентифікатор правила;
  • severity - може бути ПОМИЛКА, УВАГА и NON_COMPLIANT;
  • message - при порушенні правила відображається вміст цього рядка;
  • resource - Тип ресурсу, до якого застосовується це правило;
  • assertions — список умов, які оцінюватимуться щодо цього ресурсу.

У правилі вище assertion під назвою every перевіряє, що всі контейнери в Deployment'і (key: spec.templates.spec.containers) використовують довірені образи (тобто, що починаються з my-company.com/).

Повний ruleset виглядає так:

version: 1
description: Rules for Kubernetes spec files
type: Kubernetes
files:
  - "*.yaml"
rules:

 - id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
    severity: FAILURE
    message: Deployment must use a valid image repository
    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 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

Check no_company_repo failed with severity 1 due to Image http-echo is not from my-company.com repo
Validation failed

Зрозуміло, що за допомогою copper можна проводити складніші тести — наприклад, перевіряти доменні імена в маніфестах Ingress або відкидати під'ї, які працюють у привілейованому режимі.

У 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. Тести описуються за допомогою спеціалізованої мови запитів Рего.

Встановити 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 (ACR) або власний реєстр.

Формат артефактів — такий самий, як у пакетів Open Policy Agent (OPA), що дозволяє використовувати conftest для запуску тестів із існуючих пакетів OPA.

Більше про спільне використання політик та інші особливості conftest можна дізнатися на офіційному сайті проекту.

6. полярна зірка

Останній інструмент, про який йтиметься у цій статті, — це Полярна зірка. (Його минулорічний анонс ми вже перекладали - прим. перев.)

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 виявляє проблеми у тих областях, у яких маніфест не відповідає найкращим практикам:

  • Відсутні перевірки здоров'я pod'ів.
  • Не вказано теги для контейнерних образів.
  • Контейнер виконується під root'ом.
  • Не вказані request'и та limit'и для пам'яті та CPU.

Кожному тесту в залежності від його результатів надається ступінь критичності: попередження або небезпека. Щоб дізнатися більше про наявні вбудовані тести, зверніться до документації.

Якщо подробиці не потрібні, можна вказати прапор --format score. У цьому випадку Polaris виведе число в діапазоні від 1 до 100 рахунок (тобто оцінку):

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

Чим оцінка ближче до 100, тим вищий рівень відповідності. Якщо перевірити exit-код команди polaris audit, Виявиться, що він дорівнює 0.

змусити polaris audit завершувати роботу з ненульовим кодом можна за допомогою двох прапорів:

  • прапор --set-exit-code-below-score приймає як аргумент граничне значення в діапазоні 1-100. У цьому випадку команда завершиться з exit-кодом 4, якщо оцінка виявиться нижчою від порогової. Це дуже зручно, коли у вас є якесь граничне значення (скажімо, 75), і вам необхідно отримати alert, якщо оцінка опуститься нижче.
  • прапор --set-exit-code-on-danger призведе до того, що команда завершиться з кодом 3, якщо один із danger-тестів завершиться невдало.

Тепер давайте спробуємо створити тест користувача, що перевіряє, чи береться образ з довіреного репозиторію. Тести користувача задаються у форматі YAML, а сам тест описується за допомогою JSON Schema.

Наступний фрагмент YAML-коду описує новий тест, що називається checkImageRepo:

checkImageRepo:
  successMessage: Image registry is valid
  failureMessage: Image registry is not valid
  category: Images
  target: Container
  schema:
    '$schema': http://json-schema.org/draft-07/schema
    type: object
    properties:
      image:
        type: string
        pattern: ^my-company.com/.+$

Давайте подивимося на нього ближче:

  • successMessage - цей рядок буде виведено, якщо тест завершиться успішно;
  • failureMessage — це повідомлення буде показано у разі невдачі;
  • category - Вказує на одну з категорій: Images, Health Checks, Security, Networking и Resources;
  • target- визначає, до якого типу об'єкта (spec) застосовується тест. Можливі значення: Container, Pod або Controller;
  • Сам тест задається в об'єкті schema за допомогою JSON schema. У цьому тесті ключове слово pattern використовується порівняння джерела образу з необхідним.

Для запуску наведеного вище тесту необхідно створити наступну конфігурацію Polaris:

checks:
  checkImageRepo: danger
customChecks:
  checkImageRepo:
    successMessage: Image registry is valid
    failureMessage: Image registry is not valid
    category: Images
    target: Container
    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
  # Other inbuilt checks..
  # ..
  # custom checks
  checkImageRepo: danger # !!!
customChecks:
  checkImageRepo:        # !!!
    successMessage: Image registry is valid
    failureMessage: Image registry is not valid
    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 на відповідність найкращим практикам
Не можна вибрати свою версію API Kubernetes для перевірки ресурсів
Ні

мідь
Загальний фреймфорк для створення власних JavaScript-тестів для YAML-маніфестів
Нема вбудованих тестів. Убога документація
Так

config-lint
Загальний фреймворк для створення тестів предметно-орієнтованою мовою, вбудованою в YAML. Підтримує різні формати конфігурацій (наприклад, Terraform)
Нема готових тестів. Вбудованих асерцій і функцій може виявитися недостатньо
Так

conftest
Фреймворк для створення власних тестів Rego (спеціалізованою мовою запитів). Дозволяє ділитися політиками через OCI bundles
Нема вбудованих тестів. Доводиться вивчати Rego. Docker Hub не підтримується під час публікації політик
Так

Полярна зірка
Аналізує YAML-маніфести на відповідність стандартним найкращим практикам. Дозволяє створювати власні тести за допомогою JSON Schema
Можливостей тестів, що базуються на JSON Schema, може не вистачити
Так

Оскільки ці інструменти не залежать від доступу до кластеру Kubernetes, їх легко встановлювати. Вони дозволяють фільтрувати вихідні файли та забезпечують швидкий зворотний зв'язок авторам pull request'ів у проектах.

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук