В Kubernetes 1.37 KYAML получил статус стабильного. Формулировка «новый формат конфигурации для Kubernetes» в заголовках новостей вводит в заблуждение: никакого нового формата нет, и парсер тоже старый.

KYAML — это строгое подмножество YAML. Всё, что валидно в KYAML, валидно в YAML. Правильнее считать его не языком, а согласованным код-стайлом, который убирает те места, где YAML стреляет в ногу.

Почему YAML вообще нужно чинить

Три класса проблем, из-за которых всё затевалось.

1. Молчаливое приведение типов

Кавычки вокруг строк в YAML необязательны, и парсер угадывает тип по виду значения. Иногда угадывает не то, что вы имели в виду, — и не сообщает об этом.

Самый известный пример — «норвежская проблема». В YAML 1.1 булевыми значениями считаются не только true/false, но и yes, no, on, off:

YAML
countries:
  - GB      # строка "GB"
  - FR      # строка "FR"
  - NO      # а это false
Нажмите, чтобы развернуть и увидеть больше

Kubernetes использует парсер, реализующий YAML 1.1, так что для манифестов это не теория. Соседние сюрпризы того же рода:

YAML
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

Правила короткие:

Обычный манифест:

YAML
apiVersion: v1
kind: Service
metadata:
  name: hostnames
  labels:
    app: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
Нажмите, чтобы развернуть и увидеть больше

Он же в KYAML:

PLAINTEXT
---
{
  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:

BASH
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.

Где это действительно окупается

Честно говоря, переписывать вручную созданные манифесты смысла мало. Реальная польза в другом:

Чего ждать не стоит

Три вещи, о которых в анонсах пишут меньше.

Многострочные строки становятся некрасивыми. Блочные скаляры (| и >) в flow-стиле недоступны, вместо них — экранированные переносы:

PLAINTEXT
example: "\
     Line one\n\
     Line two\
    "
Нажмите, чтобы развернуть и увидеть больше

Если у вас в ConfigMap лежит скрипт или конфиг nginx на сорок строк, читаемость пострадает заметно. Для таких мест обычный YAML остаётся удобнее.

Комментарии могут теряться. В KEP прямо сказано: используемая библиотека не всегда корректно обрабатывает комментарии, и при сложном входе с якорями, алиасами или явными тегами они могут пропасть. То есть прогонять через конвертацию рабочие файлы с ценными комментариями — плохая идея без проверки результата.

Это вопрос вкуса, и он расколет команды. Часть людей увидит наконец-то однозначный формат, часть — «зачем вы сделали из YAML плохой JSON». Спорить бесполезно; полезно договориться, где формат применяется (генерация, вывод инструментов), а где остаётся привычный YAML (то, что пишут руками).

Что делать сейчас

Ничего срочного. Разумный минимум:

  1. Попробовать kubectl get ... -o kyaml на своих ресурсах — привыкнуть к виду.
  2. Посмотреть, где в ваших пайплайнах YAML генерируется, и подумать, не стоит ли там переключиться.
  3. Помнить про норвежскую проблему независимо от KYAML: закавычивайте строковые значения, особенно коды стран, версии и всё, что похоже на число или на «да/нет». Это работает в любом YAML и стоит одного символа.

Последний пункт, пожалуй, главный. KYAML лишь делает обязательным то, что и так было хорошей практикой.

Ссылки: KEP-5295 · блог Kubernetes про KYAML · релиз 1.37 «Garhwal»

Авторские права

Автор: Vasiliy Koshkin

Ссылка: https://notes.melancholic.tech/posts/kyaml-kubernetes/

Лицензия: CC BY-NC-SA 4.0

Использование материалов блога разрешается при условии: указания авторства/источника, некоммерческого использования и сохранения лицензии.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша