Kubernetes без интернета: как мы устанавливаем Deckhouse в закрытом контуре (обзор и видео доклада)

Всем привет! На связи Максим Набоких, архитектор и технический руководитель Deckhouse Kubernetes Platform. Deckhouse работает в компаниях из разных отраслей: нефтегазовые предприятия, финтех, государственные организации, банки, облачные провайдеры и так далее. И больше чем в половине этих организаций во внутренней инфраструктуре нет интернета — он просто запрещён. Поэтому нам надо было придумать процесс установки своей платформы в закрытый контур.

bb1d9fc0f7048940ea759cef1cfb2cc5.png

О том, как устанавливать Kubernetes (Deckhouse использует ванильный K8s), где «не ступал» ни один пакет из публичной сети, я рассказал на HighLoad++ 2023. Эта статья — текстовая версия моего доклада. Мы разберём целевую схему закрытого контура, нюансы работы инструментов для создания безопасной среды, посмотрим, как готовить дистрибутив Kubernetes-платформы к установке и осуществлять доставку приложений в закрытых окружениях.

Что такое и как устроена Kubernetes-платформа

Прежде чем разобраться в том, как работать с закрытым контуром, нужно сначала понять, что собой представляет Kubernetes-платформа. 

Разберёмся, из чего состоит сам Kubernetes-кластер:

  • Контейнеры для запуска управляющего слоя кластера. Контейнеры, которые чаще всего включены в Kubernetes-платформу:

    • kube-apiserver;

    • kube-controller-manager;

    • kube-scheduler;

    • etcd;

    • addons.

  • Пакеты операционной системы. Например, нам нужен «запускатор» наших контейнеров, допустим containerd, который ставится пакетом. Ещё нужен пакет kubelet, который нужно поставить на операционную систему. Также могут быть зависимости, которые тоже нужны в виде пакетов операционной системы. 

  • Инструмент для установки, например kubeadm, kubespray или dhctl. С помощью последнего можно разворачивать кластеры Deckhouse. 

d29e9717657d753f8a4bd9a3ca524b7d.png

Получается, что контейнеры мы храним в container registry, пакеты — в репозитории пакетов, специфичном для операционной системы, а бинарники для установки — в storage vstream, blob«ах или где-то на GitHub«е:

c37c0aa5cc9d2d190c619727c813fa2c.png

Здесь мы и встречаемся с первым препятствием: Kubernetes состоит из разнородного софта, который нужно загрузить заказчику в разные места, а это требует согласования с несколькими службами. В том числе возникает проблема, как его скачать, так как из-за отсутствия интернета нет доступа к файлам платформы. А значит, нам необходимо избежать разнородности и упаковывать софт в одинаковые пакеты.

Как убрать разнородность в K8s

Чтобы избавиться от разнородности в Kubernetes, мы реализовали следующую идею. То, что было контейнерами, так и осталось в виде контейнеров. А для доставки остального софта, в том числе установщиков Kubernetes и пакетов операционной системы, мы решили использовать registry:

8f9230e692ea277cb691ab2aac118790.png

Дело в том, что контейнеры — это blob«ы, которые лежат в registry. А в registry можно загружать что угодно. Поэтому туда можно загрузить и containerd, и инструмент для установки. Благодаря этому нам понадобится только container registry в контуре заказчика.

Преимущества такого подхода:

  • Простота. Для установки нужен доступ только к registry.

  • Удобство. В registry можно хранить всё необходимое.

  • Экономия. Registry эффективно хранит данные, не дублирует их и экономно расходует место.

Как выглядит схема закрытого контура

Теперь разберёмся, что такое закрытый контур и как он выглядит. Вот его простейшая схема — назовём её proxy registry:

0282a3131dd3f6479fa762f5274d3a16.png

У нас есть контур заказчика и Kubernetes-кластеры внутри. На схеме их три, но может быть и бесконечное количество. Они ходят в container registry и берут оттуда образы. Сам container registry может ходить в интернет, например, через proxy или настроенную сеть. Здесь либо сам registry может проксировать запросы, например если это Nexus или Harbor, либо просто есть доступ извне в этот registry.

У нас есть свой официальный registry для платформы, который называется registry.deckhouse.ru. Если между ними есть связь — можно скопировать контейнер из одного в другое.

Есть вторая, более сложная схема. Назовём её black out:

5137fdc5ca565f2fb657def443a06c80.png

В этой схеме у registry нет доступа в интернет. Соответственно, мы не можем скопировать контейнеры напрямую. Получается, данные можно загружать только физически — через флешку. Только теперь мы туда загружаем все контейнеры с помощью команды dhctl mirror:

85a4746e6b175c9627531c455fd087bb.png

Далее специально обученный человек берёт флешку, едет с ней в офис заказчика, на проходной проходит все этапы проверки и лично передаёт флешку в руки главному инженеру. Конечно, безопасники настороженно относятся ко всем флешкам. И то, что мы им приносим, они проверяют самостоятельно — сканируют образы, изучают, как они выглядят.

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

Потом с этой флешки с помощью той же команды загружаются данные из файлов в container registry:

93b4d11b6aef332a25f9a72bd5557616.png

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

Как скачать образы на флешку

Существует команда docker save, с помощью которой можно получить tar-архив с одним конкретным образом. В таком случае придётся качать по одному контейнеру — выполнять эту команду много раз и на выходе получить 15 ГБ образов, так как в registry слоистый docker-образ, все эти слои лежат по отдельности и их нельзя переиспользовать. Это неэффективно и неэкономно — как если бы несколько Docker-образов использовали один слой.

Чтобы избежать этой проблемы, группа крупных вендоров Open Container Initiative, которая разрабатывает открытые стандарты для запуска контейнеров, придумала специальный формат — Image Format. Вот пример схемы, как это работает:  

44c68f5ead848c569bd633bcd7f6d899.png

Image index — главная сущность в этой схеме. В нём описаны ссылки на манифесты — в нашем случае это container image. Этих манифестов может быть много. В каждом из них есть две ссылки:

  • на метаданные образа: что это за лейбл, как он был создан, какими командами;

  • на слои самого образа, на слои файловой системы, которые у нас есть в контейнере. И этих слоёв может быть бесконечное количество.

В файловой системе эта схема выглядит примерно так:  

c5d54ffdd29df839f3eb082338555628.png

Здесь у нас папка images, в которой есть файл index.json и папка blobs. В index.json описаны все манифесты — просто JSON-массив, внутри которого есть digest. А в папке blobs у нас лежат файлы, и их имена — хэшированное с помощью SHA-256 содержимое этих файлов. Эта папка как раз и будет нам в будущем делать дедупликацию.

Ссылка в index.json — digest — это один из blob«ов в этой папке. Если мы откроем этот файл, то увидим, что это манифест — тоже JSON-документ, в котором есть ссылки на config и на слои:

39ca3a9d5fd344bad3077bca4abc9c82.png

 Если таким образом скачать 150 или 200 образов, получается большая экономия. То есть из registry мы теперь качаем не tar-архивы, а файл в специальном формате, что позволяет получить релиз не на 15 ГБ, а на 5 ГБ:

c75730bda85dc6d0ed39e30e7570bf0a.png

Преимущества использования такого формата:

  • Количество. На одну флешку влезает 5–6 релизов, и тогда не надо гонять человека много раз в один и тот же офис.

  • Скорость. Загрузка релизов занимает меньше времени. Чем меньше у нас данных, тем быстрее они загружаются в registry и тем быстрее мы скачиваем из него.

Как доставить приложения

У заказчика могут быть свои приложения. Он может разрабатывать их сам, может брать их у подрядчиков, а может покупать софт у каких-то вендоров. И ему этот софт тоже нужно доставить в контур, где опять же нет интернета. Получается, нам нужно найти возможность вендору установить своё приложение также в закрытый контур.

То есть у нас есть контейнеры с файловой системой, на которой лежит само приложение и все зависимости для этого приложения. Также там есть манифесты, которые нужны, чтобы «сказать» Kubernetes, чтобы он выложил это приложение в кластер. Для манифестов используют разные вещи: Terraform, kubectl, Helm.

Контейнеры мы загружаем в container registry, а манифесты — в Chart Museum или Git:

4d57b386a7f50f10fb10b4b94b6091d8.png

При этом между контейнерами и манифестами есть черта, так как мы думаем о них, как о разных вещах. Непонятно, как это всё упаковать, потому что на практике образы контейнеров и манифесты связаны — они зависят друг от друга. Часто бывает так, что поправили приложение — правим манифесты. Соответственно, о них нужно думать, как об одной штуке. 

Во-первых, применяем подход, который мы обсуждали в самом начале доклада: контейнеры и Helm-чарты складываем в container registry:

ee7b053be9040f51abbeab939b28d2d6.png

Теперь мы не думаем об образах контейнеров и манифестах, как о чём-то раздельном, — они единое целое. Мы называем это bundle — подход, при котором чарт и связанные с ним образы распространяются как единое целое. Этот подход можно сделать с помощью Docker и Helm самостоятельно или можно взять утилиту werf — опенсорсный инструмент, который изначально разработали во «Фланте», а позже передали в Cloud Native Computing Foundation, и в нём эта функциональность есть «из коробки».

Как в werf описывается bundle: в папке .helm есть шаблоны — обычный Helm-чарт, который лежит там вместе с нашим приложением. Также у нас лежит werf.yaml, который описывает, как собираются контейнеры:

74e6cf075ad15d43ffef6b9b6f303d06.png

Такую связку образов и шаблонов werf делает автоматически с помощью helper«а:

a4039f4d8d2b78887f3d7b0a84afc3cd.png

То есть werf сначала собирает контейнеры, а мы потом с помощью helper«а можем использовать сигнатуру собранных контейнеров. Далее это загружается в container registry и используется:

79c729cb0a086c3c759a8298dbd30139.png

В итоге мы собрали приложение, запаковали этот bundle в контейнеры, теперь нам нужно доставить его до закрытого контура. У нас есть два container registry: один — наш, второй — не наш. Чтобы скопировать bundle из одного registry в другой, мы используем команду werf bundle copy. Если у нас black-out-схема и нет доступа к registry, снова используем флешку — загружаем всё на неё, относим и загружаем в container registry:

253a67bd841f9902714021986d0df474.png

Для доставки bundle в кластеры Kubernetes в каждом из них есть ArgoCD, который проверяет, не появилась ли в registry новая версия bundle. Если появилась, ArgoCD либо применяет сразу новую версию, либо запрашивает подтверждение. Всё зависит от настроек.

Преимущества использования bundle«ов:

  • Вендор может быть уверен, что его приложение развернётся. То есть как оно было запаковано, так мы его и доставим вместе со всеми зависимостями.

  • Автоматизация развёртывания: меньше ручных действий и ошибок. 

  • Разделение прав на чтение и на запись. Разберём это преимущество подробнее.

Загрузка bundle происходит на доверительном узле, то есть права на запись в registry у нас есть только на нём. Остальные кластеры имеют доступ только на чтение из этого registry — у нас нет доступа ко всем установкам Kubernetes в компании. Тут идёт схема разделения прав, которая нравится безопасникам, так как их данные лучше защищены. При этом сложно представить, что каждый вендор будет носить свою флешку в компанию. Это может навредить безопасности.  А их много, и они разные: вендоры антивирусов, security scanner«ов, разработки софта для инфраструктуры, SRM-системы. 

Но мы знаем, как упростить процесс передачи флешки. В первом квартале 2024 года запустится Deckhouse Marketplace.

7f6d16f320fcef472fae51b18e2b5f7e.png

Он будет работать примерно по тем же принципам. То есть можно будет загрузить свои модули в наш Marketplace, мы этот Marketplace доставим заказчику со своей флешкой, а заказчик уже сможет установить вендорский софт по одному клику. Это удобно, и каждому вендору не нужно будет ходить пешком к заказчику.

Чтобы заказчик мог убедиться, что через маркетплейс ему оставили, например, антивирус от производителя, для этого в инструменте будет реализован механизм формирования подписи контейнеров. То есть будет утилита, которая хранит подписи в registry и проверяет, есть ли подпись для нужного контейнера.

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

Итоги

Чтобы успешно пройти всю процедуру запуска Kubernetes-платформы в закрытом контуре, понадобится старая добрая флешка. При этом нужно соблюдать следующие принципы:

  • Упаковку ПО нужно делать универсальной. Это упрощает все согласования и работу с данными. А ещё это удобно, экономит время и инженеров, и заказчика.

  • Необходимо всё хранить в контейнерах, так как это самый эффективный способ.

  • Вместо собственных подходов лучше брать готовую реализацию, например Deckhouse и werf, чтобы ускорить процесс.

  • Для разработчиков стороннего ПО, которое расширяет возможности платформы или позволяет быстро разворачивать в кластере необходимые сторонние приложения, есть удобные способы доставки приложений в закрытый контур. У нас это будет Deckhouse Marketplace, который станет публично доступен в 2024 году.

Видео и слайды

Видео с выступления (~41 минута):

Презентация доклада

P. S.

Читайте также в нашем блоге:

Habrahabr.ru прочитано 3171 раз