Хабрахабр

Безопасность Helm

Эмоджи классные, с их помощью можно даже отразить всю суть рассказа о самом популярном пакетном менеджере для Kubernetes:

  • коробка — это Helm (это самое подходящее, что есть в последнем релизе Emoji);
  • замок — безопасность;
  • человечек — решение проблемы.

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

Поговорим про Helm.

  • Кратко, что такое Helm, если вы не знали или забыли. Какие проблемы он решает и где находится в экосистеме.
  • Рассмотрим архитектуру Helm. Ни один разговор о безопасности и о том, как сделать инструмент или решение более безопасным, не может обойтись без понимания архитектуры компонента.
  • Обсудим компоненты Helm.
  • Самый животрепещущий вопрос — будущее — новая версия Helm 3. 

Все в этой статье относится к Helm 2. Эта версия сейчас находится в продакшене и, скорее всего, именно его вы сейчас используете, и именно в нем есть угрозы безопасности.

О спикере: Александр Хаёров (allexx) занимается разработкой 10 лет, помогает улучшать контент Moscow Python Conf++ и присоединился к комитету Helm Summit. Сейчас работает в Chainstack на позиции development lead — это гибрид между руководителем разработки и человеком, который отвечает за delivery конечных релизов. То есть находится на месте боевых действий, где происходит все от создания продукта до его эксплуатации.

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

Helm

Это менеджер пакетов (чартов) для Kubernetes. Самый понятный и универсальный способ приносить приложения в Kubernetes-кластер.

Речь, конечно же, про более структурный и промышленный подход, нежели создание своих YAML-манифестов и написание маленьких утилит.

Helm — это лучшее, что сейчас есть из доступного и популярного.

Почему Helm? В первую очередь потому, что он поддерживается CNCF. Cloud Native — большая организация, является материнской компанией для проектов Kubernetes, etcd, Fluentd и других.

Когда в январе 2019 я только задумал рассказать о том, как сделать Helm безопасным, у проекта была тысяча звездочек на GitHub. Другой важный факт, Helm — очень популярный проект. К маю их стало 12 тысяч.

Безопасность — это важно. Многие интересуются Helm, поэтому, даже если вы до сих пор его не используете, вам пригодятся знания относительно его безопасности.

Выход Helm 3 Alpha 2 в середине июля свидетельствует о том, что над проектом работает достаточно много людей, и у них есть желание и силы развивать и улучшать Helm. Основную команду Helm поддерживает Microsoft Azure, и поэтому это довольно стабильный проект в отличие от многих других.

Helm решает несколько корневых проблем управления приложениями в Kubernetes.

  • Пакетирование приложения. Даже приложение типа «Hello, World» на WordPress уже представляет из себя несколько сервисов, и их хочется упаковать вместе.
  • Управление сложностью, которая возникает с менеджментом этих приложений.
  • Жизненный цикл, который не заканчивается после установки или деплоя приложения. Оно продолжает жить, его нужно обновлять, и помогает в этом Helm и старается принести для этого правильные меры и политики.

Пакетирование устроено понятным образом: есть метаданные в полном соответствии с работой обычного пакетного менеджера для Linux, Windows или MacOS. То есть репозиторий, зависимости от различных пакетов, метаинформация для приложений, настройки, особенности конфигурирования, индексирование информации и т.п Все это Helm позволяет получить и использовать для приложений.

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

Это то, почему в свое время я пришел к Helm. Менеджмент жизненного цикла приложения — на мой взгляд, это самый интересный и нерешенный вопрос. Нам нужно было следить за жизненным циклом приложения, мы хотели перенести свой CI/CD и циклы приложений в эту парадигму.

Helm позволяет:

  • управлять деплоями, вводит понятие кофигурации и ревизии;
  • успешно проводить rollback;
  • использовать хуки на разные события;
  • добавлять дополнительные проверки приложений и реагировать на их результаты.

Кроме того у Helm есть «батарейки» — огромное количество вкусных вещей, которые можно включить в виде плагинов, упростив свою жизнь. Плагины можно писать самостоятельно, они достаточно изолированные и не требуют стройной архитектуры. Если вы хотите что-то реализовать, я рекомендую сделать это в виде плагина, а потом возможно включить в upstream.

Helm зиждется на трех основных концепциях:

  • Chart Repo — описание и массив параметризации, возможной для вашего манифеста. 
  • Config —то есть значения, которые будут применены (текст, численные значения и т.п.).
  • Release собирает в себя два верхних компонента, и вместе они превращаются в Release. Релизы можно версионировать, тем самым добиваясь организации жизненного цикла: маленького на момент установки и крупного на момент upgrade, downgrade или rollback.

Архитектура Helm

На схеме концептуально отражена высокоуровневая архитектура Helm.

Поэтому нам не обойтись без Kubernetes-кластера (прямоугольник). Напомню, что Helm — это нечто, что связано с Kubernetes. Без Helm у нас есть Kubeconfig. Компонент kube-apiserver находится на мастере. Helm приносит одну небольшую бинарную, если можно так назвать, утилиту Helm CLI, которая устанавливается на компьютер, лэптоп, мейнфрейм — на все что угодно.

У Helm есть серверный компонент Tiller. Но этого недостаточно. Он представляет интересы Helm внутри кластера, это такое же приложение внутри кластера Kubernetes, как и любой другое.

Есть официальный репозиторий, и может быть приватный репозиторий компании или проекта. Следующий компонент Chart Repo — репозиторий с чартами.

Взаимодействие

Рассмотрим, как взаимодействуют компоненты архитектуры, когда мы хотим установить приложение с помощью Helm.

  • Мы говорим Helm install, обращаемся к репозиторию (Chart Repo) и получаем Helm-чарт.

  • Helm-утилита (Helm CLI) взаимодействует с Kubeconfig, для того чтобы выяснить к какому кластеру обратиться. 
  • Получив эту информацию, утилита обращается к Tiller, который находится в нашем кластере, уже как к приложению. 
  • Tiller обращается к Kube-apiserver, чтобы произвести действия в Kubernetes, создать какие-то объекты (сервисы, поды, реплики, секреты и т.д.).

Далее мы усложним схему, чтобы увидеть вектор атак, которым может подвергнуться вся архитектура Helm в целом. А потом попробуем ее защитить.

Вектор атак

Первое потенциально слабое место — привилегированный API-пользователь. В рамках схемы это хакер, получивший админский доступ к Helm CLI.

У такого пользователя будет другой контекст, например, он может быть зафиксирован в одном namespace кластера в настройках Kubeconfig. Непривилегированный API-пользователь тоже может представлять собой опасность, если находится где-то рядом.

Это может быть веб-сервер или микросервис, который видит сетевое окружение кластера. Наиболее интересным вектором атаки может являться процесс, который находится внутри кластера где-то рядом с Tiller и может к нему обращаться.

Чарт, созданный недобросовестным автором, может содержать небезопасные ресурс, и вы выполните его, приняв на веру. Экзотический, но набирающий популярность, вариант атаки связан с Chart Repo. Либо он может подменить чарт, который вы скачиваете с официального репозитория, и, например, создать ресурс в виде политик и эскалировать себе доcтуп.

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

Укрупним схему, добавим больше элементов, но сохраним все базовые составляющие.

Helm CLI общается с Chart Repo, взаимодействует с Kubeconfig, работа передается в кластер в компонент Tiller.

Tiller представлен двумя объектами:

  • Tiller-deploy svc, который выставляет некий сервис;
  • Tiller-deploy pod (на схеме в единственном экземпляре в одной реплике), на котором работает вся нагрузка, который обращается к кластеру.

Для взаимодействия используются разные протоколы и схемы. С точки зрения безопасности нам наиболее интересны:

  • Механизм, с помощью которого Helm CLI обращается к chart repo: какой протокол, есть ли аутентификация и что с этим можно сделать.
  • Протокол по которому Helm CLI, используя kubectl, общается с Tiller. Это RPC-сервер, установленный внутри кластера.
  • Сам Tiller доступен для микросервисов, которые находятся в кластере, и взаимодействует с Kube-apiserver.

Обсудим все эти направления по порядку.

RBAC

Бесполезно говорить о какой-либо безопасности Helm или другого сервиса внутри кластера, если не включен RBAC.

Кажется, это не сама свежая рекомендация, но уверен, до сих пор многие не включили RBAC даже в продакшене, потому что это большая возня и нужно много всего настроить. Тем не менее, призываю это сделать.

Там собрано огромное количество интересных материалов, которые помогут настроить RBAC, покажут, почему он хорош и как с ним в принципе жить в продакшене. https://rbac.dev/ — сайт-адвокат для RBAC.

Tiller работает внутри кластера под неким сервисным аккаунтом. Попробую объяснить, как работает Tiller и RBAC. В базовой конфигурации Tiller будет админом. Как правило, если не настроен RBAC, это будет суперпользователь. В действительности это так, поэтому можно использовать отдельный специализированный сервисный аккаунт вместо Default Service Account на схеме выше. Именно поэтому часто говорят, что Tiller — это SSH-туннель к вашему кластеру.

Это позволит использовать пользователя с минимально необходимым набором прав. Когда вы инициализируете Helm, впервые устанавливаете его на сервер, то можете задать сервисный аккаунт с помощью --service-account. Правда, придется создать такую «гирлянду»: Role и RoleBinding.

Вам или вашему администратору Kubernetes-кластера нужно заранее подготовить набор Role, RoleBinding для service-account, чтобы передать Helm. К сожалению, Helm не сделает это за вас.

Разница в том, что ClusterRole действует для всех namespaces, в отличие от обычных Role и RoleBinding, которые работают только для специализированного namespace. Возникает вопрос — в чем различие между Role и ClusterRole? Можно настроить политики как для всего кластера и всех namespaces, так и персонализировано для каждого namespace в отдельности.

Многие жалуются, что Helm, к сожалению, не является multitenancy (не поддерживает мультиарендность). Стоит упомянуть, что RBAC позволяет решить еще одну большую проблему. Это правда так — как сам бинарный файл, как процесс, Helm Tiller не имеет понятия о multitenancy. Если несколько команд потребляют кластер и используют Helm, невозможно в принципе настроить политики и разграничить их доступ в рамках этого кластера, потому что есть некий сервисный аккаунт, из-под которого работает Helm, и он создает из-под него все ресурсы в кластере, что порой очень неудобно.

С этим нет никаких проблем, Tiller можно запустить в каждом namespace. Однако есть прекрасный способ, который позволяет запустить Tiller в кластере несколько раз. Тем самым вы можете воспользоваться RBAC, Kubeconfig как контекстом, и ограничить доступ к специальному Helm.

Это будет выглядеть следующим образом.

У кластера админа свой широкий Tiller, который находится в пространстве Kube-system namespace, соответственно продвинутый service-account. Например, есть два Kubeconfig с контекстом для разных команд (два namespace): X Team для команды разработчиков и кластер админа. И отдельный namespace для команды разработчиков, они смогут деплоить свои сервисы в специальный namespace.

Это одно из быстрых решений. Это рабочий подход, Tiller не настолько прожорлив, чтобы это могло сильно повлиять на ваш бюджет.

Не стесняйтесь настраивать отдельно Tiller и предоставлять Kubeconfig с контекстом для команды, для конкретного разработчика или для окружения: Dev, Staging, Production (сомнительно, что все будет на одном кластере, однако, сделать так можно).

Продолжая нашу историю, переключимся от RBAC и поговорим про ConfigMaps.

ConfigMaps

Helm использует ConfigMaps в качестве хранилища данных. Когда мы говорили об архитектуре, там нигде не было базы данных, в которой хранилась бы информация о релизах, конфигурациях, rollbacks и пр. Для этого используется ConfigMaps.

Речь идет про все, что не должно попасть дальше сервиса, например, пароли. Основная проблема с ConfigMaps известна — они небезопасны в принципе, в них невозможно хранить sensitive-данные. Самым нативным способом для Helm сейчас является переход от использования ConfigMaps к секретам.

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

Однако, стоит понимать, что этим занимаются сами разработчики Kubernetes. Вы можете возразить, что сами секреты — странная концепция, и это не очень безопасно. 10, т.е. Начиная с версии 1. Сейчас команда работает над тем, чтобы еще лучше раздавать доступ к секретам, отдельным подам или другим сущностям. довольно давно, есть возможность, как минимум, в публичных облаках подключать правильный storage для хранения секретов.

Storage Helm лучше перевести на секреты, а их в свою очередь обезопасить централизованно.

Конечно, останется лимит для хранения данных в 1 Мбайт. Helm здесь использует etcd как распределенное хранилище для ConfigMaps. А там посчитали, что это подходящий чанк данных для репликаций и т.п. По этому поводу есть интересное обсуждение на Reddit, рекомендую найти это забавное чтиво на выходные или прочитать выжимку тут.

Chart Repos

Чарты наиболее социально уязвимы и могут стать источником «Man in the middle», особенно если использовать стоковое решение. В первую очередь речь идет о репозиториях, которые выставлены по HTTP.

Определенно, нужно выставлять Helm Repo по HTTPS — это лучший вариант и стоит недорого.

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

Вы можете использовать серверные и клиентские ключи для того, чтобы общаться. Кроме того, Helm-клиент поддерживает TLS (не в смысле HTTP со стороны сервера, а взаимный TLS). В принципе, chartmuseum — основной инструмент выставления Helm Repo для Helm 2 — поддерживает еще и basic auth. Скажу честно, я не использую такой механизм из-за нелюбви к взаимным сертификатам. Можно использовать basic auth, если это удобнее и спокойнее.

Это довольно удобно, прекрасно работает и достаточно безопасно, потому что утилизируются все описанные механизмы. Еще есть плагин helm-gcs, который позволяет размещать Chart Repos в Google Cloud Storage.

Если включить HTTPS или TLS, использовать mTLS, подключить basic auth, чтобы еще снизить риски, получится безопасный канал общения Helm CLI и Chart Repo.

gRPC API

Следующий шаг очень ответственный — обезопасить Tiller, который находится в кластере и является, с одной стороны, сервером, с другой стороны — сам обращается к другим компонентам и пытается представляться кем-то.

По умолчанию, естественно, TLS выключен. Как я уже сказал, Tiller — сервис, который выставляет gRPC, Helm-клиент приходит к нему по gRPC. Зачем это сделано — вопрос дискуссионный, мне кажется, чтобы упростить настройку на старте.

Для production и даже для staging рекомендую включить TLS на gRPC.

На мой взгляд, в отличие от mTLS для чартов, тут это уместно и делается очень просто — генерируете PQI инфраструктуру, создаете сертификат, запускаете Tiller, передаете сертификат во время инициализации. После этого можно выполнять все Helm-команды, представляясь сгенерированным сертификатом и приватным ключом.

Тем самым вы обезопасите себя от всех запросов к Tiller извне кластера.

Итак, мы обезопасили канал подключения к Tiller, уже обсудили RBAC и отрегулировали права Kubernetes apiserver, уменьшили домен, с которым он может взаимодействовать.

Защищенный Helm

Посмотрим на финальную схему. Это та же самая архитектура с теми же самыми стрелочками.

Все соединения теперь смело можно рисовать зеленым:

  • для Chart Repo используем TLS или mTLS и basic auth;
  • mTLS для Tiller, и он выставлен как gRPC-сервис с TLS, используем сертификаты;
  • в кластере используется специальный сервис-аккаунт с Role и RoleBinding. 

Мы заметно обезопасили кластер, но кто-то умный сказал:

«Абсолютно безопасное решение может быть только одно — выключенный компьютер, который находится в бетонной коробке и его охраняют солдаты».

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

Бонус

Эта часть не относится напрямую к безопасности, но тоже будет полезна. Покажу некоторые интересные вещи, о которых немногие знают. Например, как искать чарты — официальные и неофициальные.

Тот, кто контрибьютит, прекрасно знает, как тяжело попасть из incubator в stable, и как легко из stable вылететь. В репозитории github.com/helm/charts сейчас порядка 300 чартов и два стрима: stable и incubator. Однако, это не лучший инструмент, чтобы искать чарты для Prometheus и всего, что вам нравится, по одной простой причине — это не портал, где удобно искать пакеты.

Самое главное, там намного больше внешних репозиториев и доступно почти 800 чаротов. Но есть сервис hub.helm.sh, с помощью которого находить чарты гораздо удобнее. Плюс, вы можете подключить свой репозиторий, если по каким-то причинам не хотите отправлять свои чарты в stable.

Этот сервис под Helm-проектом, и вы можете контрибьютить даже в его UI, если вы фронтендер и хотите просто улучшить внешний вид. Попробуйте hub.helm.sh и давайте вместе его развивать.

Звучит громоздко и непонятно, но решает задачи, с которым все сталкиваются. Еще хочу обратить ваше внимание на Open Service Broker API integration. Поясню на простом примере.

Как правило, для полной функциональности нужна база данных. Есть Kubernetes-кластер, в котором мы хотим запустить классическое приложение — WordPress. Это не очень удобно, но многие так поступают. Есть много разных решений,, например, можно запустить свой statefull-сервис.

Поэтому у нас БД находятся где-то в облаке. Другие, например, мы в Chainstack, используют managed базы данных, например, MySQL или PostgreSQL, для серверов.

Все это обычно делается вручную системным администратором или разработчиком. Но возникает проблема: нужно связать наш сервис с базой данных, создать flavor базы данных, передать credential и как-то им управлять. Когда их много, нужен комбайн. И нет никакой проблемы, когда приложений мало. Он позволяет использовать специальный плагин к кластеру публичного облака и заказывать ресурсы у провайдера через Broker, как будто это API. Такой комбайн есть — это Service Broker. Для этого можно использовать нативные средства Kubernetes.

Можно запросить, например, Managed MySQL в Azure с базовым tier (это можно настроить). Это очень просто. Вам не понадобиться в это вмешиваться, за это отвечает плагин. С использованием API Azure база будет создана и подготовлена к использованию. Вы сможете использовать WordPress с облачным MySQL, вообще не заниматься managed базами данных и не париться о statefull-сервисах внутри. Например, OSBA (плагин Azure) вернет credential в сервис, передаст это Helm.

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

Можно написать свой плагин и использовать всю эту историю on-premise. Тогда у вас просто будет свой плагин к корпоративному Cloud-провайдеру. Я советую попробовать такой подход, особенно если у вас большой масштаб и вы хотите быстро развернуть dev, staging или всю инфраструктуру под фичу. Это упростит жизнь вашим operations или DevOps.

Еще одна находка, которую я уже упоминал — это плагин helm-gcs, который позволяет использовать Google-buckets (объектное хранилище), чтобы хранить Helm-чарты.

Нужно всего четыре команды, чтобы начать его использовать:

  1. установить плагин;
  2. инициировать его;
  3. задать путь к bucket, который находится в gcp;
  4. опубликовать чарты стандартным способом.

Прелесть в том, что будет использоваться нативный способ gcp для авторизации. Вы можете использовать сервисный аккаунт, аккаунт разработчика — все, что угодно. Это очень удобно и ничего не стоит в эксплуатации. Если вы, как и я, пропагандируете opsless-философию, то это будет очень удобно, особенно для небольших команд.

Альтернативы

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

Вы можете использовать свои классические средства управления инфраструктурой (Ansible, Terraform, Chef и пр.) для тех же целей, о которых я говорил. Это могут быть как специализированные решения, например, Ksonnet или Metaparticle.

Наконец, есть решение Operator Framework, популярность которого растет.

Operator Framework — главная альтернатива Helm, на которую следует обратить внимание.

Оно более нативно для CNCF и Kubernetes, но порог вхождения намного выше, нужно больше программировать и меньше описывать манифесты.

Они сильно облегчают жизнь, например, разработчикам упрощают цикл отправки и запуска Helm для деплоя тестового окружения. Есть различные addons, такие, как Draft, Scaffold. Я бы назвал их расширителями возможностей.

Вот наглядный график о том, где что находится.

Helm версии 2 находится где-то посерединке. По оси абсцисс уровень вашего личного контроля над происходящим, по оси ординат — уровень нативности Kubernetes. Решения уровня Ksonnet все-таки уступают даже Helm 2. В версии 3 не колоссально, но улучшены и контроль и уровень нативности. Конечно, ваш менеджер конфигураций будет у вас под контролем, но абсолютно не нативен для Kubernetes. Однако на них стоит посмотреть, чтобы знать, что еще есть в этом мире.

Скорее, это подойдет для специализированного приложения и создания для него менеджмента, нежели массового комбайна по упаковке огромного количества приложений с помощью Helm. Operator Framework абсолютно нативен для Kubernetes и позволяет управлять им намного элегантнее и скрупулёзнее (но помним про уровень вхождения).

Расширители просто чуть улучшают контроль, дополняют workflow или срезают углы CI/CD pipelines.

Будущее Helm

Хорошая новость в том, что появляется Helm 3. Уже вышел релиз альфа-версии Helm 3.0.0-alpha.2, можно попробовать. Он достаточно стабилен, но функциональность пока ограничена.

В первую очередь это история про исчезновение Tiller, как компонента. Зачем нужен Helm 3? Это, как вы уже понимаете, огромный шаг вперед, потому что с точки зрения безопасности архитектуры все упрощается.

8 или даже раньше, многие концепции были незрелые. Когда создавался Helm 2, а это было во времена Kubernetes 1. Станет возможным использовать только клиент и не держать серверную часть. Например, концепция CRD сейчас активно внедряется, и Helm будет использовать CRD, чтобы хранить структуры. Это огромный шаг вперед. Соответственно, использовать нативные команды Kubernetes для работы со структурами и ресурсами.

Это огромная инициатива, и Helm она интересна в первую очередь для того, чтобы размещать свои чарты. Появится поддержка нативных репозиториев OCI (Open Container Initiative). Я не загадываю, но возможно, классические провайдеры Docker-репозиториев начнут давать возможность размещать вам свои Helm-чарты. Доходит до того, что, например, Docker Hub поддерживает многие стандарты OCI.

Я не большой фанат Lua, но это будет полностью опциональной возможность. Спорная для меня история — это поддержка Lua, как templating engine для написания скриптов. Поэтому тот, кто хочет сможет использовать Lua, тот, кому нравится Go — присоединяйтесь к нашему огромному лагерю и используйте go-tmpl для этого. Я 3 раза это проверил — использование Lua будет не обязательно.

Больше не будет проблем с int или string, не нужно будет оборачивать ноль в двойные кавычки. Наконец то, чего мне точно не хватало — это появление схемы и валидация типов данных. Появится JSONS-схема, которая позволит явно это описать для values.

Она уже концептуально описана. Будет очень сильно переработана event-driven model. Посмотрите в ветку Helm 3, и увидите, как много было добавлено событий и хуков и прочего, что сильно упростит и, с другой стороны, добавит контроля над процессами деплоя и реакций по ним.

Соответственно, Helm может использовать наработки Kubernetes и создавать на нем отличные менеджеры для Kubernetes. Helm 3 будет проще, безопаснее и интереснее не потому, что мы не любим Helm 2, а потому что Kubernetes становится более продвинутым.

Напомним, конференция по интеграции процессов разработки, тестирования и эксплуатации пройдет в Москве 30 сентября и 1 октября. Еще одна хорошая новость в том, что на DevOpsConf Александр Хаёров расскажет, могут ли контейнеры быть безопасными? До 20 августа еще можно подать доклад и рассказать о своем опыте решения одной из многих задач DevOps-подхода.

За чекпойнтами конференции и новостями следите в рассылке и telegram-канале.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть