Как узнать версию kubernetes
Как мы обновляли Kubernetes 1.16 до 1.19… с удовольствием
На начало декабря 2020 у нас во «Фланте» было около 150 кластеров на Kubernetes 1.16. Это кластеры с разной степенью загруженности: как находящиеся под высоким трафиком production-кластеры, так и использующиеся для разработки и демонстрации новых возможностей. Кластеры работают поверх различной инфраструктуры, начиная с облачных провайдеров, таких как AWS, Azure, GCP, Яндекс.Облако, различных инсталляций OpenStack и vSphere, и заканчивая железками.
Все эти кластеры находятся под управлением Deckhouse, который является нашей внутренней разработкой и позволяет не только иметь один инструмент для создания кластеров, но и общий интерфейс для управления всеми компонентами кластера на всех поддерживаемых типах инфраструктуры. Для этого Deckhouse состоит из различных подсистем. Например, есть подсистема candi (cluster and infrastructure), которая наиболее интересна в рамках данной статьи, поскольку отвечает за управление control-plane Kubernetes и настройку узлов, предоставляет готовый к работе, актуальный кластер.
(Оценить плюсы Deckhouse пока можно в рамках нашего Managed Kubernetes, а уже в ближайшее время мы планируем сделать первый Open Source-релиз проекта для всех желающих посмотреть на него изнутри и развернуть для своих задач. Присоединиться к числу ожидающих можно в Telegram-канале.)
Итак, почему мы вообще застряли на версии 1.16, когда уже достаточно давно вышли 1.17, 1.18 и даже выпустили патч версии для 1.19? Дело в том, что у нас затянулось предыдущее обновление кластеров — с 1.15 на 1.16. Оно было очень тяжелым и вот почему:
Обновление с 1.15 на 1.16 приводило к рестарту контейнеров на узле, поэтому приходилось выполнять drain узла, что в случае большого количества stateful-приложений приводило к необходимости выполнять работы в согласованные окна и требовало особого внимания инженеров.
Эти 2 фактора сильно тормозили процесс обновления на 1.16. С одной стороны необходимо было добиваться от клиентов обновления чартов и выпуска обновлений, а с другой — аккуратно обновлять кластеры в наполовину ручном режиме. Была проделана большая работа со всеми пользователями кластеров, потому что требовалось донести, что существует реальная необходимость обновления, а подход «работает — не трогай» в данном случае может сыграть злую шутку… Попробую убедить в этом и вас.
Зачем вообще обновлять Kubernetes?
Одной из самых очевидных вещей, которой проще всего объяснить обновление, является устаревание программного обеспечения. В проекте Kubernetes поддерживаются только 3 последние минорные версии. Таким образом, на момент выхода 1.19 наша текущая версия 1.16 должна была покинуть список поддерживаемых. Хотя так совпало, что именно в этом релизе (1.19) разработчики Kubernetes пошли навстречу сообществу и увеличили окно поддержки до 1 года. Это изменение стало ответом на грустную статистику, которая показала, как медленно обновляются инсталляции K8s в реальной жизни (по результатам опроса в начале 2019 года лишь 50-60% кластеров K8s имели поддерживаемую версию).
На момент этого опроса актуальной версией Kubernetes была 1.13, т.е. все пользователи K8s 1.9 и 1.10 работали с релизами, которые уже не поддерживались.
Примечание. Тема обновлений релизов Kubernetes вообще широко обсуждается в сообществе инженеров эксплуатации. Например, опросы Platform9 от 2019-го года (№1, №2) показали, что обновление — в топ-3 главных вызовов при обслуживании Kubernetes. Легко найти в интернете и связанные с обновлениями failure stories, вебинары и т.д.
Но вернёмся к релизу 1.16. С ним также был ряд проблем, которые мы вынужденно затыкали «костылями». Вполне возможно, что большинство из читателей не сталкивались с описанным ниже, однако у нас на поддержке большое количество кластеров (с тысячами узлов), поэтому даже редкие ошибки регулярно генерировали нам дополнительную работу. К декабрю мы обросли различными забавными компонентами как в самом Kubernetes, так и в системных юнитах:
Иногда случалось совсем страшное. В кластерах можно было получить целую пачку проблем с узлами, потому что зависали kube-proxy на обращении к kube-apiserver. Причиной всему являлось отсутствие health check для HTTP/2-соединений для всех клиентов, использующихся в Kubernetes. При зависшем kube-proxy по понятным причинам начинались сетевые проблемы на узлах, что могло приводить к простою. Исправление было выпущено для 1.20 и сделан backport только в 1.19. Кстати, этот же фикс решил проблемы с зависанием kubelet. Были также проблемы, что периодически мог зависать kubectl на каких-то длительных операциях и приходилось всегда помнить, что следует использовать timeout.
Как устроен процесс обновления Kubernetes
В начале статьи уже кратко упоминался Deckhouse и подсистема candi, которая отвечает в том числе и за процесс обновления control-plane и узлов кластера. Если не погружаться в нюансы, то внутри имеется немного допиленный kubeadm, поэтому структурно процесс обновления совпадает с документацией Kubernetes по обновлению кластеров под управлением kubeadm.
Этапы обновления с 1.16 на 1.19 выглядят следующим образом:
Обновление версии control-plane до версии 1.17;
Обновление версии kubelet на узлах до версии 1.17;
Обновление версии control-plane до версии 1.18;
Обновление версии kubelet на узлах до версии 1.18;
Обновление версии control-plane до версии 1.19;
Обновление версии kubelet на узлах до версии 1.19.
В Deckhouse эти этапы выполняются автоматически. Для этого в каждом кластере есть Secret, в котором описана конфигурация кластера в YAML’е следующего формата:
Для обновления достаточно изменить значение kubernetesVersion на желаемое (можно менять сразу с 1.16 на 1.19). Внутри подсистемы candi есть два модуля, которые отвечают за управление control-plane и управление узлами.
В сontrol-plane-manager автоматически отслеживаются изменения в данном YAML.
Вычисляется текущая версия Kubernetes в кластере на основании версии control-plane и узлов кластера. Например, если kubelet на всех узлах версии 1.16 и все control-plane компоненты версии 1.16, то можно запускать обновление на следующую версию 1.17 — и так далее, пока версия не будет той, что указана в конфигурации.
Также control-plane-manager следит, чтобы обновление компонентов control-plane выполнялось по очереди на каждом из мастеров: для этого реализован алгоритм запроса и выдачи разрешения на обновление через специальный диспетчер.
Node-manager отвечает за управление узлами, в том числе и за обновление kubelet’а:
Каждый узел в кластере принадлежит к одной из NodeGroup. Как только node-manager определяет, что версия control-plane на всех узлах обновилась, то он приступает к обновлению версии kubelet. Если обновление узла не приводит к простою, оно считается безопасным и выполняется в автоматическом режиме. В данном случае обновление kubelet больше не приводит к перезапуску контейнеров, что позволило безопасно выкатывать его.
Наш опыт обновления Kubernetes на 1.19
Итак, мы решили обновляться сразу на 1.19, фактически минуя версии 1.17 и 1.18, по нескольким причинам.
Основная причина была в том, что не хотелось затягивать процесс обновления. Каждый цикл обновления требует различных согласований с пользователями кластеров, инженерного времени на контроль процесса обновления. Таким образом, мы снова продолжали бы рисковать отставать от upstream’а на несколько версий. Мы хотели, чтобы к февралю 2021 все наши кластеры были версии 1.19. Поэтому было твёрдое желание одним волевым усилием получить максимально свежую версию Kubernetes в наших кластерах — тем более, уже на подходе был 1.20 (его релиз состоялся 8 декабря 2020).
Как уже написано выше, у нас есть кластеры, которые работают поверх различных облачных провайдеров. Чтобы реализовать это требуется использование нескольких компонентов, специфичных для каждого провайдера. Для управления дисками используется Container Storage Interface, а для взаимодействия с API облачных провайдеров — Cloud Controller Manager. Тестирование работоспособности этих компонентов для каждой версии Kubernetes требует достаточно большого количества ресурсов. В данном случае нет потребности тратить столько времени впустую на «промежуточные» версии.
Процесс
Было проведено полное тестирование совместимости компонентов с версией Kubernetes 1.19 и принято решение обновляться сразу на 3 версии вперёд. Так как стандартный процесс обновления выполняется последовательно, то на момент, когда версия control-plane в облачных кластерах соответствовала 1.17 и 1.18, были отключены компоненты, описанные выше.
Фактическое время обновления зависело от числа worker-узлов и узлов control-plane, занимало от 20 до 40 минут. На это время в облачных кластерах не работал заказ и удаление узлов, любые операции с дисками. При этом уже работающие в кластере узлы и примонтированные диски продолжали работать корректно. Это был единственный очевидный минус запланированного обновления, с которым пришлось смириться на старте. Только из-за этого было принято решение проводить работы по обновлению большинства кластеров в ночное время, когда нет высокой нагрузки.
Сам процесс обновления мы сначала прогнали несколько раз на внутренних dev-кластерах, после чего начали проводить на клиентских. Естественно, тоже по нарастающей, стартовав с dev-кластеров, которые можно было обновить даже с небольшим простоем.
Первые апгрейды и первое препятствие
Массовые обновления
К концу декабря 2020 года мы имели около 50 кластеров Kubernetes, обновленных до 1.19. До сих пор выполнялось параллельное обновление по несколько (2-3, максимум — 5) кластеров. Но так как уверенность в стабильности и корректности обновления росла, отлично отметив 2021-й Новый Год, мы решили запустить обновление около 100 оставшихся кластеров одновременно, чтобы к концу января иметь только парочку последних кластеров на 1.16. Кластеры были разбиты на две группы, в каждой — около 50 кластеров.
Failed to initialize CSINodeInfo: error updating CSINode annotation
Отлаживать, к сожалению, времени не было: так как кластер находился на железе, которое почти всегда полностью загружено, выход одного узла из строя уже начинал сказываться на работоспособности приложения. Было найдено быстрое решение, которое, надеюсь, поможет и вам, если столкнётесь с похожей проблемой. Однако повторюсь, данная проблема в итоге возникла только на одном(!) из 150 кластеров в процессе обновления, поэтому вероятность получить такую же проблему минимальна.
Чтобы успеть в сроки, которые мы сами себе обозначили, на 28 января в 00:00 было запланировано обновление второй группы кластеров, в которой остались самые нагруженные producton’ы: простой в них приводит к написанию постмортемов, нарушению SLA и штрафам. Процесс обновления контролировал лично наш CTO — @distol… И всё прошло как по маслу: совсем ничего примечательно во время обновления не случилось, никакое ручное вмешательство не потребовалось. В нашем Telegram-канале появилось радостное сообщение:
Последняя боль
Но позже выяснилось, что нам досталась одна проблема, которая произошла из-за обновления, но последствия её проявились только через пару дней (и как обычно в одном из самых нагруженных и ответственных проектов…).
Во время обновления несколько раз перезапускается kube-apiserver. Исторически сложилось, что в части кластеров под управлением Deckhouse активно используется flannel, где всё это время была упоминавшаяся ранее в статье проблема с зависанием Go-клиента из-за отсутствия health check для HTTP/2-соединений при обращении к API Kubernetes. В итоге, на части узлов в кластере в логах flannel появились ошибки:
Это привело к тому, что на этих узлах некорректно работал CNI и проскакивали 500-е ошибки при обращении к сервисам. Рестарт flannel исправил проблему, а для глобального решения необходимо обновлять flannel.
К счастью, относительно недавно (15 декабря 2020) появился pull request, который обновляет версию client-go. На момент написания данной статьи это исправление доступно в релизе v0.13.1-rc2, и мы планируем глобально обновить версию flannel во всех кластерах под управлением Deckhouse.
В чем же удовольствие?
У нас уже готова и поддержка версии Kubernetes 1.20, так что в скором времени мы обновим кластеры на неё по аналогичной схеме. А уж если столкнёмся с какими-то интересными особенностями — обязательно об этом расскажем.
kubectl CLI
На этой странице рассматривается синтаксис kubectl, описаны командные операции и приведены распространённые примеры. Подробную информацию о каждой команде, включая все поддерживаемые в ней флаги и подкоманды, смотрите в справочной документации kubectl. Инструкции по установке находятся на странице Установка и настройка kubectl.
Синтаксис
Используйте следующий синтаксис для выполнения команд kubectl в терминале:
TYPE : определяет тип ресурса. Типы ресурсов не чувствительны к регистру, кроме этого вы можете использовать единственную, множественную или сокращенную форму. Например, следующие команды выведут одно и то же.
При выполнении операции с несколькими ресурсами можно выбрать каждый ресурс по типу и имени, либо сделать это в одном или нескольких файлов:
Выбор ресурсов по типу и имени:
Операции
В следующей таблице приведены краткие описания и общий синтаксис всех операций kubectl :
Примечание: подробную информацию о командных операциях смотрите в справочную документацию kubectl.
Типы ресурсов
В следующей таблице перечислены все доступные типы ресурсов вместе с сокращенными аббревиатурами.
(Это актуальный вывод команды kubectl api-resources с версии Kubernetes 1.13.3.)
Опции вывода
В следующих разделах рассматривается форматирование и сортировка вывода определенных команд. Дополнительные сведения о том, какие команды поддерживают разные варианты вывода, смотрите в справочной документации kubectl.
Форматирование вывода
Синтаксис
В зависимости от операции kubectl поддерживаются следующие форматы вывода:
Пример
В данном примере следующая команда выводит подробную информацию по указанному поду в виде объекта в YAML-формате:
Примечание: подробную информацию о доступных форматах вывода в определенной команде смотрите в справочной документации kubectl.
Пользовательские столбцы
Примеры
Столбцы указаны в самой команде:
Столбцы указаны в файле шаблона:
где файл template.txt содержит следующее:
Результат выполнения любой из показанной выше команды:
Получение вывода с сервера
kubectl может получать информацию об объектах с сервера. Это означает, что для любого указанного ресурса сервер вернет столбцы и строки по этому ресурсу, которые отобразит клиент. Благодаря тому, что сервер инкапсулирует реализацию вывода, гарантируется единообразный и человекочитаемый вывод на всех клиентах, использующих один и тот же кластер.
Примеры
Для вывода информации о состоянии пода, используйте следующую команду:
Вывод будет выглядеть следующим образом:
Сортировка списка объектов
Синтаксис
Пример
Чтобы вывести список подов, отсортированных по имени, выполните команду ниже:
Примеры: распространенные операции
Посмотрите следующие примеры, чтобы ознакомиться с часто используемыми операциями kubectl :
Примеры: создание и использование плагинов
Посмотрите следующие примеры, чтобы ознакомиться с тем, как писать и использовать плагины kubectl :
Посмотреть все доступные плагины kubectl можно с помощью подкоманды kubectl plugin list :
Плагины можно рассматривать как способ создания более сложной функциональности поверх существующих команд kubectl:
Выполнение этого плагина генерирует вывод, содержащий пользователя для текущего выбранного контекста в файле KUBECONFIG:
Чтобы узнать больше о плагинах, изучите пример CLI-плагина.
Что дальше
Начните использовать команды kubectl.
Kubectl поддерживает шаблон JSONPath.
Шаблон JSONPath состоит из выражений JSONPath, заключенных в фигурные скобки <>. Kubectl использует JSONPath-выражения для фильтрации по определенным полям в JSON-объекте и форматирования вывода. В дополнение к оригинальному синтаксису шаблона JSONPath, допустимы следующие функции и синтаксис:
Объект результата выводиться через функцию String().
Все примеры ниже будут ориентироваться на следующий JSON-объект:
Примеры использования kubectl и JSONPath-выражений:
В Windows нужно заключить в двойные кавычки JSONPath-шаблон, который содержит пробелы (не в одинарные, как в примерах выше для bash). Таким образом, любые литералы в таких шаблонов нужно оборачивать в одинарные кавычки или экранированные двойные кавычки. Например:
Synopsis
kubectl управляет кластерами Kubernetes.
Options
See Also
Вы можете использовать инструмент командной строки kubectl в Kubernetes для работы с API-сервером. Если вы знакомы с инструментом командной строки Docker, то использование kubectl не составит проблем. Однако команды docker и kubectl отличаются. В следующих разделах показана подкоманда docker и приведена эквивалентная команда в kubectl.
docker run
Для развёртывания nginx и открытия доступа к объекту Deployment используйте команду kubectl run.
Основы Kubernetes
В этой публикации я хотел рассказать об интересной, но незаслуженно мало описанной на Хабре, системе управления контейнерами Kubernetes.
Что такое Kubernetes?
Kubernetes является проектом с открытым исходным кодом, предназначенным для управления кластером контейнеров Linux как единой системой. Kubernetes управляет и запускает контейнеры Docker на большом количестве хостов, а так же обеспечивает совместное размещение и репликацию большого количества контейнеров. Проект был начат Google и теперь поддерживается многими компаниями, среди которых Microsoft, RedHat, IBM и Docker.
Компания Google пользуется контейнерной технологией уже более десяти лет. Она начинала с запуска более 2 млрд контейнеров в течение одной недели. С помощью проекта Kubernetes компания делится своим опытом создания открытой платформы, предназначенной для масштабируемого запуска контейнеров.
Проект преследует две цели. Если вы пользуетесь контейнерами Docker, возникает следующий вопрос о том, как масштабировать и запускать контейнеры сразу на большом количестве хостов Docker, а также как выполнять их балансировку. В проекте предлагается высокоуровневый API, определяющее логическое группирование контейнеров, позволяющее определять пулы контейнеров, балансировать нагрузку, а также задавать их размещение.
Концепции Kubernetes
Nodes (node.md): Нода это машина в кластере Kubernetes.
Pods (pods.md): Pod это группа контейнеров с общими разделами, запускаемых как единое целое.
Replication Controllers (replication-controller.md): replication controller гарантирует, что определенное количество «реплик» pod’ы будут запущены в любой момент времени.
Services (services.md): Сервис в Kubernetes это абстракция которая определяет логический объединённый набор pod и политику доступа к ним.
Volumes (volumes.md): Volume(раздел) это директория, возможно, с данными в ней, которая доступна в контейнере.
Labels (labels.md): Label’ы это пары ключ/значение которые прикрепляются к объектам, например pod’ам. Label’ы могут быть использованы для создания и выбора наборов объектов.
Kubectl Command Line Interface (kubectl.md): kubectl интерфейс командной строки для управления Kubernetes.
Архитектура Kubernetes
Работающий кластер Kubernetes включает в себя агента, запущенного на нодах (kubelet) и компоненты мастера (APIs, scheduler, etc), поверх решения с распределённым хранилищем. Приведённая схема показывает желаемое, в конечном итоге, состояние, хотя все ещё ведётся работа над некоторыми вещами, например: как сделать так, чтобы kubelet (все компоненты, на самом деле) самостоятельно запускался в контейнере, что сделает планировщик на 100% подключаемым.
Нода Kubernetes
При взгляде на архитектуру системы мы можем разбить его на сервисы, которые работают на каждой ноде и сервисы уровня управления кластера. На каждой ноде Kubernetes запускаются сервисы, необходимые для управления нодой со стороны мастера и для запуска приложений. Конечно, на каждой ноде запускается Docker. Docker обеспечивает загрузку образов и запуск контейнеров.
Kubelet
Kubelet управляет pod’ами их контейнерами, образами, разделами, etc.
Kube-Proxy
Также на каждой ноде запускается простой proxy-балансировщик. Этот сервис запускается на каждой ноде и настраивается в Kubernetes API. Kube-Proxy может выполнять простейшее перенаправление потоков TCP и UDP (round robin) между набором бэкендов.
Компоненты управления Kubernetes
Система управления Kubernetes разделена на несколько компонентов. В данный момент все они запускаются на мастер-ноде, но в скором времени это будет изменено для возможности создания отказоустойчивого кластера. Эти компоненты работают вместе, чтобы обеспечить единое представление кластера.
Состояние мастера хранится в экземпляре etcd. Это обеспечивает надёжное хранение конфигурационных данных и своевременное оповещение прочих компонентов об изменении состояния.
Kubernetes API Server
Kubernetes API обеспечивает работу api-сервера. Он предназначен для того, чтобы быть CRUD сервером со встроенной бизнес-логикой, реализованной в отдельных компонентах или в плагинах. Он, в основном, обрабатывает REST операции, проверяя их и обновляя соответствующие объекты в etcd (и событийно в других хранилищах).
Scheduler
Scheduler привязывает незапущенные pod’ы к нодам через вызов /binding API. Scheduler подключаем; планируется поддержка множественных scheduler’ов и пользовательских scheduler’ов.
Kubernetes Controller Manager Server
Все остальные функции уровня кластера представлены в Controller Manager. Например, ноды обнаруживаются, управляются и контролируются средствами node controller. Эта сущность в итоге может быть разделена на отдельные компоненты, чтобы сделать их независимо подключаемыми.
ReplicationController — это механизм, основывающийся на pod API. В конечном счете планируется перевести её на общий механизм plug-in, когда он будет реализован.
Пример настройки кластера
В качестве платформы для примера настройки была выбрана Ubuntu-server 14.10 как наиболее простая для примера и, в то же время, позволяющая продемонстрировать основные параметры настройки кластера.
Для создания тестового кластера будут использованы три машины для создания нод и отдельная машина для проведения удалённой установки. Можно не выделять отдельную машину и производить установку с одной из нод.
Подготовка нод
Требования для запуска:
Установка ПО на ноды
Установку Docker можно произвести по статье в официальных источниках:
Дополнительная настройка Docker после установки не нужна, т.к. будет произведена скриптом установки Kubernetes.
Установка bridge-utils:
Добавление ssh-ключей
Выполняем на машине, с которой будет запущен скрипт установки.
Если ключи ещё не созданы, создаём их:
Копируем ключи на удалённые машины, предварительно убедившись в наличии на них необходимого пользователя, в нашем случае core.
Установка Kubernetes
Далее мы займёмся установкой непосредственно Kubernetes. Для этого в первую очередь скачаем и распакуем последний доступный релиз с GitHub:
Настройка
Для того, чтобы использовать последний, на момент написания статьи, релиз 0.17.0 необходимо заменить:
На этом настройка заканчивается и можно переходить к установке.
Установка
Первым делом необходимо сообщить системе про наш ssh-agent и используемый ssh-ключ для этого выполняем:
В процессе установки скрипт потребует пароль sudo для каждой ноды. По окончанию установки проверит состояние кластера и выведет список нод и адреса Kubernetes api.
Посмотрим, какие ноды и сервисы присутствуют в новом кластере:
Видим список из установленных нод в состоянии Ready и два предустановленных сервиса kubernetes и kubernetes-ro — это прокси для непосредственного доступа к Kubernetes API. Как и к любому сервису Kubernetes к kubernetes и kubernetes-ro можно обратиться непосредственно по IP адресу с любой из нод.
Запуск тестового сервиса
Для запуска сервиса необходимо подготовить docker контейнер, на основе которого будет создан сервис. Дабы не усложнять, в примере будет использован общедоступный контейнер nginx. Обязательными составляющими сервиса являются Replication Controller, обеспечивающий запущенность необходимого набора контейнеров (точнее pod) и service, который определяет, на каких IP адресе и портах будет слушать сервис и правила распределения запросов между pod’ами.
Любой сервис можно запустить 2-я способами: вручную и с помощью конфиг-файла. Рассмотрим оба.
Запуск сервиса вручную
Начнём с создания Replication Controller’а:
Далее создаём service который будет использовать наш Replication Controller как бекенд.
Для http:
Для проверки запущенности можно зайти на любую из нод и выполнить в консоли:
В выводе curl увидим стандартную приветственную страницу nginx. Готово, сервис запущен и доступен.
Запуск сервиса с помощью конфигов
Для этого способа запуска необходимо создать конфиги для Replication Controller’а и service’а. Kubernetes принимает конфиги в форматах yaml и json. Мне ближе yaml поэтому будем использовать его.
Предварительно очистим наш кластер от предыдущего эксперимента:
Был создан Replication Controller с именем nginx и количеством реплик равным 6. Реплики в произвольном порядке запущены на нодах, местоположения каждой pod’ы указано в столбце HOST.
Можно заметить, что при использовании конфига за одним сервисом могут быть закреплены несколько портов.
Применяем конфиг:
Для проверки запущенности можно зайти на любую из нод и выполнить в консоли:
В выводе curl увидим стандартную приветственную страницу nginx.
Заметки на полях
В качестве заключения хочу описать пару важных моментов, о которые уже пришлось запнуться при проектировании системы. Связаны они были с работой kube-proxy, того самого модуля, который позволяет превратить разрозненный набор элементов в сервис.
PORTAL_NET. Сущность сама по себе интересная, предлагаю ознакомиться с тем, как же это реализовано.
Недолгие раскопки привели меня к осознанию простой, но эффективной модели, заглянем в вывод iptables-save:
На этом всё, спасибо за внимание
К сожалению, всю информацию, которую хочется передать, не получается уместить в одну статью.