А нельзя ли эмулировать поведение kube-apiserver простым nginx-ом, просто подкладывая файлики в папочку? Да, собственно, можно. Вы даже можете в kubeconfig клиента использовать http и заигнорировать аутентификацию (точнее сказать, вы можете указать basic-auth с любым содержимым в kubeconfig и просто игнорировать в nginx заголовок Authorization). Просто клонируйте мой репозиторий в любой каталог для server/location nginx’а, натравите на него kubeconfig и можете пользоваться, например, командами kubectl get nodes. Что положите — то и будет работать. В иных случаях он будет грустно сообщать об отсутствии ресурса. Только не забудьте в конфиге локейшена указать 'types {} default_type "application/json; charset=utf-8";' для того, чтобы kubectl не ругался на кодировку (Это нужно потому, что в своей дальновидности я использую в репозитории с примерами расширение .html).
На этом этапе вы можете использовать сию конструкцию, например, для какого-то тестирования, не заморачиваясь с etcd, а также версионируя доступные объекты в kube-apiserver. Однако, очевидно, что кое-чего все же здесь не хватает. Конечно же, любые запросы, кроме GET, дружно сообщат вам об ошибке. Если быть точнее, то они сообщат, что всё OK, однако изменения не доедут. Ведь эти файлики за nginx, а он не умеет ими управлять. Если мы хотим заставить всё это работать, то можем вооружиться встроенным джаваскриптом, lua или чем-то еще, чтобы обеспечить функционирование этих методов. Или можно взять fastCGI и накрутить поверх него определенное поведение. Но зачем усложнять минималистичную конструкцию? В таком случае будет проще поставить apache и использовать там нативно php, а то и CGI скрипты. Если вам это интересно, то можете побаловаться самостоятельно. Я же предпочел просто взять первый попавшийся язык программирования и набросать там простое веб-приложение.
Так что же нам нужно сделать? Очевидно, в первую очередь следует обеспечить работу /api и /apis с соответствующим форматом ответов. Для полноценной эмуляции kube-api стоит также реализовать эндпоинты /openapi и /version. /openapi/v2 можно просто забить пустым файлом. Вы можете написать с нуля свой kube-apiserver, если у вас много свободного времени. А можете смело выкинуть вообще все ресурсы и создать пустой список APIVersions в APIGroupList, сформировать какой-то кастомный ресурс и организовать любое поведение вашего самописного kube api. Оно будет работать. К примеру, можете описать ресурс postgres-databases, работу HTTP методов для создания, просмотра, редактирования и удаления баз данных где-то там в существующей постгре, и спокойно после этого управлять ими через kubectl.
Однако, советую в /api реализовать хотя бы ресурс namespaces, он вам явно может пригодится для группировки ваших ресурсов. Но... зачем эти шаманства? Ну, к примеру, вы можете организовать gitops, например, через argocd, вообще не имея куба в инфраструктуре. Вам всего лишь нужно написать свои контроллеры для всего, что вы хотите покрыть этой модной методологией. (Правда в argocd есть нюанс. При регистрации кластера через cli он пытается достучаться до токена, с которым впоследствии будет обращаться к kube-api. Так что вам придется реализовать roles и secrets. Ну, создать какие-то фиксированные, если вы такой же любитель кривых костылей, как я). Допустим, что мы настолько отчаянные, что даже написали все наши контроллеры, и теперь у нас свой собственный kube-apiserver, с которым работают kubectl, helm, argocd, jsonnet и все, что вообще умеет взаимодействовать с кубом. Также мы можем воспроизвести еще две полезные фичи kubernetes. Это веб хуки валидации и мутации. Реализуется это через
Admission Controllers.В целом это работает следующим образом: где-то вертится контроллер, по сути еще одно api, в которое перенаправляется любой запрос к kube-apiserver перед тем, как зафиксировать это в etcd. Есть два типа обработки: Validating и Mutating. С помощью валидации мы можем, к примеру, запретить добавлять в кластер ресурсы с определенными атрибутами в спеке. А мутация позволяет автоматически изменять применяемые манифесты. Классический случай — применение сайдкаров при наличии какой-либо аннотации во всяких там service mesh решениях. В кубе это делается через регистрацию такого контроллера через ресурс Admission Configuration. Подробно описывать этот процесс я не буду, можете ознакомиться с этим
здесь.Но кажется, мы все же еще кое о чем забыли. Это конечно же аутентификация и авторизация, а также генерация токенов. Тут тоже вдаваться в подробности не буду. Достаточно сказать что в kube api есть ресурсы role, определяющие некоего «юзера». Не совсем так, правда, потому что в кубе есть и ресурс user. Если мне не изменяет память, он используется для oauth аутентифицированных клиентов в kube-apiserver. Также есть ресурс Group, для группировки. Во время аутентификации по сертификатам, сопоставление роли будет использоваться CommonName (cn) аттрибут в сертификате. Сопоставление с группой идет по атрибуту Organizatin (o). Наглядно это можно посмотреть в сертификатах kubelet в опусе Келси Хайтауера
kubernetes the hard way. Нюансы относительно этих ресурсов, биндингов и прочего, с вашего позволения, я опущу. Так как это является одним из базовых знаний, которые вы уже должны знать, если интересуетесь углубленной работой kube. С необходимостью реализовать эти компоненты мы уже столкнулись, пытаясь натравить argocd на наш новоявленный куб-кластер. Подождите, кластер? Похоже что для кластера нам тоже далеко, ведь нам нужно как-то синхронизировать состояние между репликами. А если мы хорошо вкатились в kube, то знаем что у нас должны быть еще валидация, ивенты, веб хуки валидации и мутации, а также много чего еще... Кажется, что писать свои костыли было все же не самой хорошей идеей. Но не пропадать же нашим знаниям даром? Давайте все же раскатаем kube, но прицепим к нему наше новоявленное api как расширение. Делается это с помощью механизма API Aggregation.
Полностью это описано
здесь, но краткая суть сводится примерно к следующему. Мы определяем во флагах kube-apiserver сертификат и необходимые заголовки для подключения к нашему api, после чего создаем ресурс APIService, в котором описываем нашу группу ресурсов, версию, параметры подключения и все такое. После чего kube api становится нашим api-гейтвеем или прокси, если угодно, обслуживая запросы на указанную в ресурсе группу api, перенаправляя запросы в наш новорожденный контроллер.
На этом моменте я все же сдамся и расскажу страшный секрет. Здесь тоже можно упростить себе жизнь, если вы гошник. В
репозитории вы можете обнаружить описание как можно собрать расширение для kubeapi. Если же вы вдруг решили, что махинации с nginx-ом или иной проксей можно использовать, чтобы каким-то образом управлять запросами перед kube api, например, чтобы организовать что-то вроде наколеночного мультитенанта, то такое уже тоже есть. Например, как я понял, так
работает это приложение. Если же вас взбудоражила возможность отказаться от etcd как бэкенда, то и пример такой реализации уже
есть.Так что и тут покостылять не удастся. Но все же никто не может вам этого запретить!
Однако, в большинстве случаев у нас не возникает в этом необходимости. Потому что мейнстримом сейчас считается «паттерн» операторов, которые работают на основе CustomResourceDefinitions (CRD). Это такие кубовые ресурсы, с помощью которых можно создать группу api и версию, а также openapi-схему для спеки вашего ресурса, после чего вы можете через kubectl или любой клиент создавать экземпляры этих ресурсов в кластере. Обычно, далее их подхватывает оператор и на базе них уже генерирует стандартные ресурсы. Самым элементарным примером для такого будет уже не раз упомянутый argocd, который полностью конфигурируется через его CRD-хи. Разница между расширением api заключается в том, что работа с crd получается асинхронной, тогда как в расширении api вы можете управлять запросом как вам угодно. Однако, очевидно, это более трудоемко.