В Kubernetes 1.37 KYAML получил статус стабильного. Формулировка «новый формат конфигурации для Kubernetes» в заголовках новостей вводит в заблуждение: никакого нового формата нет, и парсер тоже старый.
KYAML — это строгое подмножество YAML. Всё, что валидно в KYAML, валидно в YAML. Правильнее считать его не языком, а согласованным код-стайлом, который убирает те места, где YAML стреляет в ногу.
Почему YAML вообще нужно чинить
Три класса проблем, из-за которых всё затевалось.
1. Молчаливое приведение типов
Кавычки вокруг строк в YAML необязательны, и парсер угадывает тип по виду значения. Иногда угадывает не то, что вы имели в виду, — и не сообщает об этом.
Самый известный пример — «норвежская проблема». В YAML 1.1 булевыми значениями считаются не только true/false, но и yes, no, on, off:
countries:
- GB # строка "GB"
- FR # строка "FR"
- NO # а это falseKubernetes использует парсер, реализующий YAML 1.1, так что для манифестов это не теория. Соседние сюрпризы того же рода:
version: 1.20 # число 1.2 — хвостовой ноль теряется
time: 12:30 # в YAML 1.1 это 750 (шестидесятеричное)
build: 0755 # восьмеричное 493
answer: y # trueОшибка проявляется не при применении манифеста, а позже — когда приложение получает не то значение.
2. Чувствительность к отступам
Структура задаётся пробелами. Поэтому файл с неправильным отступом остаётся синтаксически валидным, просто описывает другой объект. Ошибётесь уровнем — и поле уедет в соседнюю секцию, а kubectl apply спокойно это примет.
Особенно больно с шаблонизаторами. Helm подставляет текст в YAML, ничего не зная о его структуре, — отсюда весь зоопарк с indent, nindent и toYaml, который приходится вычитывать глазами.
3. Избыточность самого YAML
Kubernetes нужна крошечная часть возможностей формата. Якоря, алиасы, явные теги, пять способов записать многострочный текст — всё это в манифестах либо не нужно, либо опасно.
Как выглядит KYAML
Правила короткие:
- только flow-стиль: карты —
{}, списки —[], блочного стиля с отступами нет; - все строковые значения в двойных кавычках (ключи — без, если не двусмысленны);
- запятая после последнего элемента обязательна;
- числа и булевы значения пишутся как есть, без кавычек;
- отступ в два пробела — как соглашение о читаемости, а не как носитель смысла;
- в начале документа
---, чтобы отличать от JSON.
Обычный манифест:
apiVersion: v1
kind: Service
metadata:
name: hostnames
labels:
app: hostnames
spec:
selector:
app: hostnames
ports:
- port: 80
protocol: TCP
targetPort: 9376Он же в KYAML:
---
{
apiVersion: "v1",
kind: "Service",
metadata: {
labels: {
app: "hostnames",
},
name: "hostnames",
},
spec: {
ports: [{
port: 80,
protocol: "TCP",
targetPort: 9376,
}],
selector: {
app: "hostnames",
},
},
}Похоже на JSON — и это осознанно. Но в отличие от JSON здесь допустимы комментарии, а незакавыченные ключи оставляют читаемость.
Главное следствие: структура задаётся скобками, а не пробелами. Отступ теперь просто оформление. Именно поэтому такой текст безопасно подставлять шаблонами — сдвинулся блок или нет, смысл не меняется.
И приведение типов исчезает как класс: NO в кавычках остаётся строкой "NO".
Как получить
Стабильно с 1.37, флаг вывода у kubectl:
kubectl get service hostnames -o kyamlПуть был обычный: в альфе (1.34) возможность пряталась за переменной окружения KUBECTL_KYAML=true, в бете включилась по умолчанию с возможностью отключить через KUBECTL_KYAML=false, в 1.37 гейт убран — вывод доступен всегда.
Под капотом объект сначала маршалится в JSON, а потом рендерится в KYAML (пакет sigs.k8s.io/yaml/kyaml). Отсюда и согласованность с JSON-тегами, и отсутствие проблем блочного стиля.
Писать манифесты в KYAML необязательно. Приём на вход не изменился: kubectl apply как принимал обычный YAML, так и принимает. Более того, KYAML-файл скормится и старым версиям kubectl — это же валидный YAML.
Где это действительно окупается
Честно говоря, переписывать вручную созданные манифесты смысла мало. Реальная польза в другом:
- Машинно-генерируемый вывод. Всё, что печатают контроллеры, операторы и скрипты, лучше отдавать в форме, где не бывает сюрпризов с типами.
- Шаблоны Helm. Нечувствительность к отступам снимает целый класс ошибок при подстановке.
- Диффы и патчи. Один канонический стиль означает, что диффы показывают смысловые изменения, а не переформатирование. Спор «список через дефис или в скобках» закрывается сам.
- Разбор инцидентов. Когда смотришь на чужой манифест в три часа ночи, явные кавычки и скобки экономят время.
Чего ждать не стоит
Три вещи, о которых в анонсах пишут меньше.
Многострочные строки становятся некрасивыми. Блочные скаляры (| и >) в flow-стиле недоступны, вместо них — экранированные переносы:
example: "\
Line one\n\
Line two\
"Если у вас в ConfigMap лежит скрипт или конфиг nginx на сорок строк, читаемость пострадает заметно. Для таких мест обычный YAML остаётся удобнее.
Комментарии могут теряться. В KEP прямо сказано: используемая библиотека не всегда корректно обрабатывает комментарии, и при сложном входе с якорями, алиасами или явными тегами они могут пропасть. То есть прогонять через конвертацию рабочие файлы с ценными комментариями — плохая идея без проверки результата.
Это вопрос вкуса, и он расколет команды. Часть людей увидит наконец-то однозначный формат, часть — «зачем вы сделали из YAML плохой JSON». Спорить бесполезно; полезно договориться, где формат применяется (генерация, вывод инструментов), а где остаётся привычный YAML (то, что пишут руками).
Что делать сейчас
Ничего срочного. Разумный минимум:
- Попробовать
kubectl get ... -o kyamlна своих ресурсах — привыкнуть к виду. - Посмотреть, где в ваших пайплайнах YAML генерируется, и подумать, не стоит ли там переключиться.
- Помнить про норвежскую проблему независимо от KYAML: закавычивайте строковые значения, особенно коды стран, версии и всё, что похоже на число или на «да/нет». Это работает в любом YAML и стоит одного символа.
Последний пункт, пожалуй, главный. KYAML лишь делает обязательным то, что и так было хорошей практикой.
Ссылки: KEP-5295 · блог Kubernetes про KYAML · релиз 1.37 «Garhwal»