Прим. перев.: С ростом числа YAML-конфигураций для K8s-окружений всё более актуальной становится потребность в их автоматизированной проверке. Автор этого обзора не просто отобрал существующие решения для этой задачи, но и на примере Deployment'а посмотрел, как они работают. Получилось весьма информативно для тех, кому эта тема интересна. TL;DR: В статье сравниваются шесть статических инструментов проверки и оценки YAML-файлов Kubernetes на соответствие лучшим практикам и требованиям. Рабочие нагрузки Kubernetes, как правило, определяются в форме YAML-документов. Одна из проблем с YAML'ом — сложность задания ограничений или взаимоотношений между файлами манифестов. Что, если нам надо удостовериться, что все образы, развертываемые в кластере, берутся из доверенного реестра? Как предотвратить отправку в кластер Deployment'ов, для которых не заданы PodDisruptionBudgets? Интеграция статического тестирования позволяет выявлять ошибки и нарушения политик еще на стадии разработки. Тем самым повышаются гарантии правильности и безопасности определений ресурсов и повышается вероятность того, что production-нагрузки будут следовать лучшим практикам. Экосистему статической проверки YAML-файлов Kubernetes можно разделить на следующие категории: В этой статье мы опишем и сравним шесть различных инструментов: Что ж, давайте приступим! Прежде чем приступить к сравнению инструментов, давайте создадим некоторую основу, на которой будем их тестировать. Приведенный ниже манифест содержит ряд ошибок и несоответствий лучшим практикам: сколько из них вы сможете найти? (base-valid.yaml) Мы будем использовать этот YAML для сравнения различных инструментов. Манифест описывает веб-приложение, основная задача которого — отвечать сообщением «Hello World» на порт 5678. Его можно развернуть следующей командой: А так — проверить работу: Теперь перейдите на http://localhost:8080 и подтвердите, что приложение работает. Но следует ли оно лучшим практикам? Давайте проверим. В основе kubeval лежит идея о том, что любое взаимодействие с Kubernetes происходит через его REST API. Другими словами, можно использовать схему API для проверки того, соответствует ли ей данный YAML. Давайте рассмотрим пример. Инструкции по установке kubeval доступны на сайте проекта. После установки давайте «скормим» ему манифест, приведенный выше: В случае успеха kubeval завершит работу с exit-кодом 0. Проверить его можно следующим образом: Давайте теперь попробуем kubeval с другим манифестом: (kubeval-invalid.yaml) Можете на глаз определить проблему? Запускаем: Ресурс не проходит проверку. Deployment'ы, использующие версию API apps/v1, должны включать селектор, соответствующий метке pod'а. Манифест выше не включает селектор, поэтому kubeval сообщил об ошибке и вышел с ненулевым кодом. Интересно, что произойдет, если выполнить kubectl apply -f с этим манифестом? Что ж, давайте попробуем: Именно та ошибка, о которой предупреждал kubeval. Исправить ее можно, добавив селектор: (base-valid.yaml) Преимущество инструментов вроде kubeval в том, что подобные ошибки можно отлавливать на ранних стадиях цикла развертывания. Кроме того, для этих проверок не нужен доступ в кластер: их можно проводить оффлайн. По умолчанию kubeval проверяет ресурсы на соответствие самой последней схеме Kubernetes API. Однако в большинстве случаев вам может потребоваться провести проверку на соответствие конкретному релизу Kubernetes. Сделать это можно с помощью флага --kubernetes-version: Обратите внимание, что версия должна указываться в формате Major.Minor.Patch. Чтобы просмотреть список версий, для которых поддерживается проверка, обратитесь к JSON-схеме на GitHub, которую kubeval использует для валидации. Если нужно запустить kubeval оффлайн, скачайте схемы и укажите их локальное местоположение с помощью флага --schema-location. Кроме отдельных YAML-файлов, kubeval также может работать с директориями и stdin. Кроме того, Kubeval легко интегрируется в пайплайн CI. Желающие проводить тесты перед отправкой манифестов в кластер будут рады узнать, что kubeval поддерживает три формата вывода: И любой из форматов можно использовать для дальнейшего парсинга вывода, чтобы формировать сводку результатов желаемого вида. Один из недостатков kubeval — в настоящее время он не умеет проверять на соответствие Custom Resource Definitions (CRDs). Однако можно настроить kubeval игнорировать их. Kubeval — отличный инструмент для проверки и оценки ресурсов; правда, следует подчеркнуть, что успешное прохождение теста не гарантирует, что ресурс соответствует лучшим практикам.  Например, использование тега latest в контейнере не соответствует лучшим практикам. Однако kubeval не считает это ошибкой и не сообщает о ней. То есть проверка такого YAML завершится без предупреждений. Но что, если нужно оценить YAML и выявить нарушения вроде тега latest? Как проверить YAML-файл на соответствие лучшим практикам? Kube-score анализирует манифесты YAML и оценивает их по встроенным тестам. Эти тесты выбираются на основе рекомендаций по безопасности и лучших практик, например: По итогам теста выдается три результата: OK, WARNING и CRITICAL. Kube-score можно попробовать онлайн или установить его локально. Давайте испытаем его на нашем манифесте base-valid.yaml: YAML проходит проверки kubeval, в то время как kube-score указывает на следующие недостатки: Всё это — резонные замечания о недостатках, которые следует устранить, чтобы Deployment стал более эффективным и надежным. Команда kube-score выводит информацию в удобочитаемой форме с включением всех нарушений типа WARNING и CRITICAL, что очень помогает во время разработки. Желающие использовать этот инструмент в рамках пайплайна CI могут включить более сжатый вывод с помощью флага --output-format ci (в этом случае выводятся также тесты с результатом OK): По аналогии с kubeval, kube-score возвращает ненулевой код выхода при наличии теста, завершившегося ошибкой CRITICAL. Также можно включить подобную обработку и для WARNING. Кроме того, имеется возможность проверки ресурсов на соответствие различным версиям API (как и в kubeval). Однако эта информация за'hardcode'на в самом kube-score: выбрать другую версию Kubernetes нельзя. Подобное ограничение может стать большой проблемой, если вы намерены обновить кластер или у вас есть несколько кластеров с различными версиями K8s. Подробнее о kube-score можно узнать на официальном сайте. Тесты kube-score — отличный инструмент для внедрения лучших практик, но что, если необходимо внести изменения в тест или добавить собственные правила? Увы, этого не получится сделать. Kube-score не расширяем: в него нельзя добавить политики или подстроить их. Если требуется писать пользовательские тесты для проверок на соответствие политикам, принятым в компании, можно использовать один из следующих четырех инструментов: config-lint, copper, conftest или polaris. Config-lint — это инструмент для валидации конфигурационных файлов формата YAML, JSON, Terraform, CSV и манифестов Kubernetes. Установить его можно с помощью инструкций на сайте проекта. Config-lint не содержит встроенных тестов для проверки манифестов Kubernetes. Для проведения любых тестов необходимо создавать соответствующие правила. Они записываются в YAML-файлы, называемые «наборами правил» (rulesets), и имеют следующую структуру: (rule.yaml) Давайте изучим ее повнимательнее: Допустим, вы хотите убедиться, что образы в Deployment'е всегда скачиваются из доверенного репозитория вроде my-company.com/myapp:1.0. Правило для config-lint, осуществляющее подобную проверку, будет выглядеть следующим образом: (rule-trusted-repo.yaml) Для каждого правила должны быть указаны следующие атрибуты: В правиле выше assertion под названием every проверяет, что все контейнеры в Deployment'е (key: spec.templates.spec.containers) используют доверенные образы (т.е., начинающиеся с my-company.com/). Полный ruleset выглядит следующим образом: (ruleset.yaml) Чтобы испытать тест, давайте сохраним его как check_image_repo.yaml. Запустим проверку над файлом base-valid.yaml: Проверка завершилась неудачно. Теперь давайте проверим следующий манифест с правильным репозиторием образов: (image-valid-mycompany.yaml) Запускаем тот же самый тест с приведенным выше манифестом. Проблем не обнаружено: Config-lint — перспективный фреймворк, позволяющий создавать собственные тесты для проверки YAML-манифестов Kubernetes с помощью YAML DSL. Но что делать, если требуется более сложная логика и тесты? Разве возможности YAML не слишком малы для этого? Что, если можно было бы создавать тесты на полноценном языке программирования? Copper V2 — это фреймворк для валидации манифестов с помощью пользовательских тестов (аналог config-lint). Однако от последнего он отличается тем, что не использует YAML для описания тестов. Вместо этого тесты можно создавать на JavaScript. Copper предоставляет библиотеку с несколькими базовыми инструментами, помогающими считывать информацию об объектах Kubernetes и сообщать об ошибках. Последовательность шагов для установки Copper можно найти в официальной документации. Как и config-lint, Copper не имеет встроенных тестов. Давайте напишем один. Пусть он проверяет, что deployment'ы используют контейнерные образы исключительно из доверенных репозиториев вроде my-company.com. Создайте файл check_image_repo.js со следующим содержимым: Теперь, чтобы проверить наш манифест base-valid.yaml, используйте команду copper validate: Понятно, что с помощью copper можно проводить более сложные тесты — например, проверять доменные имена в манифестах Ingress или отвергать pod'ы, работающие в привилегированном режиме. В Copper встроены различные служебные функции: Со всеми доступными служебными функциями можно ознакомиться здесь. По умолчанию он загружает весь входной YAML-файл в переменную $$ и делает ее доступной для скриптов (знакомый метод для тех, кто имеет опыт работы с jQuery). Главный плюс Copper очевиден: вам не нужно осваивать специализированный язык и можно пользоваться различными возможностями JavaScript для создания собственных тестов, такими как интерполяция строк, функции и т.д. Подробности доступны на официальном сайте проекта. Впрочем, если вы не очень любите JavaScript и предпочитаете язык, специально предназначенный для создания запросов и описания политик, вам следует обратить внимание на conftest. Conftest — это фреймворк для проверки конфигурационных данных. Подходит и для тестирования/верификации манифестов Kubernetes. Тесты описываются с помощью специализированного языка запросов Rego. Установить conftest можно с помощью инструкций, приведенных на сайте проекта. По аналогии с config-lint и copper, conftest идет без каких-либо встроенных тестов. Давайте попробуем его и напишем собственную политику. Как и в предыдущих примерах, будем проверять, берутся ли образы контейнеров из надежного источника. Создайте директорию conftest-checks, а в ней — файл с именем check_image_registry.rego со следующим содержимым: Теперь давайте протестируем base-valid.yaml через conftest: Тест ожидаемо провалился, поскольку образы поступают из недоверенного источника. В файле Rego мы задаем блок deny. Его истинность рассматривается как нарушение. Если блоков deny несколько, conftest проверяет их независимо друг от друга, и истинность любого из блоков трактуется как нарушение. Помимо вывода по умолчанию conftest поддерживает JSON, TAP и табличный формат — крайне полезная возможность, если нужно встроить отчеты в существующий пайплайн CI. Задать нужный формат можно с помощью флага --output. Для облегчения отладки политик в conftest имеется флаг --trace. Он выводит трассировку того, как conftest парсит указанные файлы политик. Политики conftest можно публиковать и делиться ими в OCI-реестрах (Open Container Initiative) в виде артефактов. Команды push и pull позволяют опубликовать артефакт или извлечь существующий артефакт из удаленного реестра. Давайте попробуем опубликовать созданную нами политику в локальный реестр Docker с помощью conftest push. Запустите локальный реестр Docker: В другом терминале перейдите в созданную ранее директорию conftest-checks и выполните следующую команду: Если команда прошла успешно, вы увидите сообщение следующего типа: Теперь создайте временную директорию и выполните в ней команду conftest pull. Она скачает в нее пакет, созданный предыдущей командой: Во временной директории появится подкаталог policy, содержащий наш файл с политикой: Тесты можно проводить напрямую из репозитория: К сожалению, DockerHub пока не поддерживается. Поэтому считайте, что вам повезло, если вы используете Azure Container Registry (ACR) или свой собственный реестр. Формат артефактов — такой же, как у пакетов Open Policy Agent (OPA), что позволяет использовать conftest для запуска тестов из существующих пакетов OPA. Больше о совместном использовании политик и других особенностях conftest можно узнать на официальном сайте проекта. Последний инструмент, о котором пойдет речь в этой статье, — это Polaris. (Его прошлогодний анонс мы уже переводили — прим. перев.) Polaris можно установить в кластер или использовать в режиме командной строки. Как вы уже догадались, он позволяет статически анализировать манифесты Kubernetes. При работе в режиме командной строки доступны встроенные тесты, охватывающие такие области, как безопасность и лучшие практики (по аналогии с kube-score). Кроме того, можно создавать собственные тесты (как в config-lint, copper и conftest). Другими словами, Polaris сочетает в себе плюсы обеих категорий инструментов: со встроенными и пользовательскими тестами. Для установки Polaris в режиме командной строки воспользуйтесь инструкциями на сайте проекта. После завершения установки можно запустить polaris на манифесте base-valid.yaml с помощью следующей команды: Она выведет строку в формате JSON с подробным описанием выполненных тестов и их результатами. Вывод будет иметь следующую структуру: Полный вывод доступен здесь. Как и kube-score, Polaris выявляет проблемы в тех областях, в которых манифест не соответствует лучшим практикам: Каждому тесту в зависимости от его результатов присваивается степень критичности: warning или danger. Чтобы узнать больше об имеющихся встроенных тестах, обратитесь к документации. Если подробности не нужны, можно указать флаг --format score. В этом случае Polaris выведет число в диапазоне от 1 до 100 — score (т.е. оценку): Чем оценка ближе к 100, тем выше степень соответствия. Если проверить exit-код команды polaris audit, окажется, что он равен 0. Заставить polaris audit завершать работу с ненулевым кодом можно с помощью двух флагов: Теперь давайте попробуем создать пользовательский тест, проверяющий, берется ли образ из доверенного репозитория. Пользовательские тесты задаются в формате YAML, а сам тест описывается с помощью JSON Schema. Следующий фрагмент YAML-кода описывает новый тест, называемый checkImageRepo: Давайте посмотрим на него поближе: Для запуска вышеприведенного теста необходимо создать следующую конфигурацию Polaris: (polaris-conf.yaml) Разберем файл: Сохраните файл как custom_check.yaml. Теперь можно запустить polaris audit с YAML-манифестом, требующим проверки. Протестируем наш манифест base-valid.yaml: Команда polaris audit выполнила только пользовательский тест, заданный выше, и он не увенчался успехом. Если исправить образ на my-company.com/http-echo:1.0, Polaris завершится успешно. Манифест с изменениями уже есть в репозитории, так что вы можете проверить предыдущую команду на манифесте image-valid-mycompany.yaml. Теперь возникает вопрос: как запускать встроенные тесты совместно с пользовательскими? Легко! Просто надо добавить идентификаторы встроенных тестов в файл конфигурации. В результате он приобретет следующий вид: (config_with_custom_check.yaml) Пример полного файла конфигурации доступен здесь. Проверить манифест 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, и дописать в него только то, что нужно? Однозначного ответа на этот вопрос нет. Таблица ниже содержит краткое описание каждого инструмента: Поскольку эти инструменты не зависят от доступа в кластер Kubernetes, их легко устанавливать. Они позволяют фильтровать исходные файлы и обеспечивают быструю обратную связь авторам pull request'ов в проектах. Читайте также в нашем блоге: