Привет, Хабр!
Некоторое время мы присматриваемся к книгам по Kubernetes, благо, они уже выходят и в Manning, и в O'Reilly. Можно согласиться, что Kubernetes в наших краях пока интересен скорее с ознакомительной и инженерной, нежели с практической точки зрения. Однако, мы все-таки поставим здесь обложку с книги о Kubernetes и перевод статьи Дэниэла Морсинга, который сделал у себя в блоге интересный тизер об этой системе.
Приятного чтения!
Введение
Отправляясь в турне по конференциям о языке Go, я примерно по три раза в год слушаю разные лекции о том, как еще можно загрузить Kubernetes. При этом, я имел весьма зыбкое представление о том, что же такое Kubernetes, о глубоких знаниях говорить и не приходилось. Но вот у меня выдалось свободное время, и я решил: попробую портировать на Kubernetes простое приложение, повожусь с ним и опишу мои первые впечатления.
Я не собираюсь писать пост из разряда «Знакомство с Kubernetes», поскольку об этом лучше почитать в официальной документации, даже не стану с ней тягаться. На самом деле, документация очень хорошая. В частности, мне очень понравился раздел «Concepts», когда я пытался составить общее представление о системе. Авторы документации – снимаю шляпу!
Расскажу о приложении. У меня есть собственный блог-сервер, на котором лежит оригинал этой статьи. Так сложилось, что он работает на небольшом инстансе Linode, которым я управляю вручную. Может показаться, что применять целый стек управления кластером для развертывания такого маленького приложения – это стрельба из пушки по воробьям; в самом деле, так и есть. Но я обнаружил, что именно таким образом удобно напрактиковаться в работе с системой. На момент публикации оригинала мой блог работал на одноузловом кластере Google Container Engine.
Жизненные циклы подов
Kubernetes славится своим отлаженным планировщиком. Для каждой развернутой системы, управляемой через Kubernetes, назначается группа контейнеров (так называемый «под»), в которой можно запускать машины (так называемые «узлы»). В процессе выкатывания и масштабирования ресурсов новые поды создаются и упраздняются, в зависимости от требований реплики. Такое планирование обеспечивает более рациональное использование ресурсов, но мне кажется, что не так революционны сами поды, как та среда, в которой функционирует Kubernetes. Kubernetes обеспечивает «из коробки» управление образами, внутреннюю систему доменных имен и автоматизацию выкатывания. Поэтому мне кажется целесообразным выстраивать такую систему, где конкретные поды соотносятся с конкретными узлами, поэтому какая-либо надобность в планировщике фактически отпадает.
Одна из задач, которые, по-видимому, не решаются в рамках данного жизненного цикла – система не умеет работать с «горячими» кэшами. В принципе, держать такие кэши – плохая идея, но на практике они кое-где попадаются в кластерах. Если у вас есть memcache-контейнер, а вместе с ним вы эксплуатируете какой-либо сервер, то апгрейд такого сервера неизбежно требует сначала убить memcache. Существует механизм для работы с подами, сохраняющими состояние, но при этом все состояние требуется дампировать на диск, а затем считывать это состояние обратно, когда под будет перепланирован. Если такая необходимость в самом деле возникнет, то вам вряд ли импонирует перспектива дожидаться, пока все это хозяйство восстановится с диска.
Сети
Сеть, обеспечивающая обмен информацией между подами, устроена довольно красиво. На платформе Google Cloud Platform каждый узел получает /24 подсети в закрытом диапазоне 10.0.0.0/8, а каждый под затем получает собственный IP. Затем на каждом поде вы можете использовать для ваших сервисов любой диапазон портов, какой вам заблагорассудится. При такой изоляции исключается ситуация, в которой несколько приложений пытались бы подключиться к одному и тому же порту. Если вы хотите сбросить http-сервер на порту 80, можете так и сделать, не беспокоясь при этом о других http-серверах. Большинство приложений «обучены» избегать 80, но многие штуки открывают на 8080 отладочные серверы, и мне известно, как из-за этого падали целые системы.
Еще одно достоинство такой работы с пространствами имен – подобная система позволяет иметь дело с различными программами, которые могут быть настроены на взаимодействие с нестандартными портами. На самом деле, держать DNS на любом порту кроме 53 и правда чертовски сложно, поскольку на такую DNS ниоткуда будет не отправить запрос.
В основе такой изоляции лежит соотношение «у каждого узла своя подсеть». Если провайдер выделяет вам всего один IP на каждый узел, то придется устанавливать какую-либо оверлейную сеть, а такие сети (по моему опыту) работают не слишком хорошо.
Итак, коммуникация между подами налажена хорошо, но вам ведь еще потребуется точно определить все IP-адреса, чтобы обеспечить коммуникацию между ними. Для этого в Kubernetes существуют так называемые «сервисы». По умолчанию каждый «сервис» получает конкретный внутренний кластерный IP. Этот IP можно узнать по внутренней системе доменных имен и подключиться к нему. Если у вас найдется несколько подов, подходящих тому или иному сервису, то между ними автоматически включится балансировка нагрузки, обработкой которой займется узел, инициировавший коммуникацию.
Я еще не могу определиться с тем, нравится ли мне «единый IP на весь кластер». Большинство приложений изрядно плохо умеют обрабатывать несколько значений DNS, полученных скопом, и будут выбирать тот результат, что получат первым — из-за чего распределение ресурсов будет неравномерным. Единый IP на кластер снимает эту проблему. Однако, здесь мы попадаем в классическую ловушку – путаем обнаружение сервисов и поддержание их в «живом» виде. Переходя к работе со все более крупными кластерами, мы встречаем все больше несимметричных сегментов. Возможно, тот узел, на котором хостится под, сможет сам выполнять проверку работоспособности и докладывать о ней на ведущий узел Kubernetes, но с других узлов до него будет не достучаться. Когда это произойдет, балансировщик нагрузки все равно будет пытаться выйти на отказавший узел, а поскольку на весь кластер один IP – резервного варианта у вас не будет. Можно попробовать завершить соединение и попытаться еще раз, в надежде, что при повторном распределении нагрузки TCP-соединение пойдет через другой узел, рабочий, но такое решение неоптимально.
При развертывании Kubernetes советую организовать такую проверку работоспособности: узнавать, сколько запросов было подано в конкретный под со времени предыдущей проверки. Если это количество ниже некоторого предела – пометьте, что под барахлит. Таким образом, балансировщик нагрузки должен быстро научиться избегать этот под, даже если он достижим с ведущего узла Kubernetes. Можно конфигурировать сервисы так, чтобы получать IP подов напрямую, но, если вам не требуется сетевой идентификатор, я не вижу в этом необходимости.
Поскольку у подов закрытые IP, не маршрутизируемые через общедоступный Интернет, нужен какой-то механизм трансляции локальных портов в маршрутизируемые, если требуется обратиться к той или иной информации извне кластера. Обычно я не в восторге от создания NAT в таких случаях, поэтому логичнее всего выглядел бы переход на IPv6: просто выделяем каждому узлу публично маршрутизируемую подсеть, и проблема снимается сама собой. К сожалению, Kubernetes не поддерживает IPv6.
На практике NAT – не такая большая проблема. При работе с трафиком, приходящим извне кластера, Kubernetes стимулирует нас активно работать с такими сервисами, которые взаимодействуют с облачными балансировщиками нагрузки, предоставляющими одиночный IP. Поскольку многие разработчики Kubernetes занимались оркестровкой в Google, неудивительно, что описываемый механизм построен по модели Maglev.
К сожалению, я еще не придумал, как не только наладить взаимодействие сервиса со внешним миром, но при этом еще и обеспечить высокую доступность в среде, где нет такого балансировщика нагрузки. Можно приказать Kubernetes перенаправлять на внешний узел весь трафик, доходящий до кластера, но при распределении подов по узлам IP не учитываются. Если под выводится с того узла, на который маршрутизирован этот IP, то узлу приходится заниматься организацией NAT – значит, мы нагружаем его лишней работой. Другая проблема – так называемые конфликты портов на внешнем IP-уровне. Если у вас есть контроллер, обновивший внешние IP (а также информацию в DNS-службе, которой вы пользуетесь) в зависимости от того, на каких узлах «приземлились» эти IP, может случиться так, что два пода одновременно захотят трафик с порта 80, но у вас не будет никакого шанса отличить один IP от другого.
Пока я как-то выживаю с облачным балансировщиком нагрузки. Так проще, а я не собираюсь в обозримом будущем задействовать Kubernetes где-либо вне облака.
Еще немного об облачной магии
Другая область, в которой активно используется облачная магия — это управление постоянными хранилищами. Существующие варианты попадания на диск, к которому возможен доступ с пода, явственно завязаны на работу с облачными провайдерами. Да, можно создать хранилище, по сути представляющее собой просто съемный диск на узле, но эта возможность пока находится в состоянии «альфа» и в продакшене не используется. Было бы интересно проверить, можно ли сделать начальную загрузку NFS-сервиса, работающего на Kubernetes, а затем организовать, чтобы именно через него Kubernetes управлял постоянными хранилищами, но я пока не планирую браться за такую задачу.
Кому-то кажется, что облачная магия – последняя капля, заставляющая от всего этого отказаться, но сегодня кластерные вычисления так сильно зависят от облачных сервисов, что обойтись без облаков очень сложно. Я замечаю, что люди стараются не впутываться в эту магию, чтобы избежать зависимости от поставщика, однако, при этом они недооценивают стоимость самостоятельной разработки, а также множества неявных моментов, подразумеваемых при работе на облачной платформе. Kubernetes предоставляет согласованный интерфейс, через который удобно заниматься этой облачной магией. Так что, доверять ему можно, и он (как минимум) технически стандартизирован
Вывод
Вот и закончился наш маленький экскурс в Kubernetes – надеюсь, вам понравилось. Естественно, в статье рассмотрены игрушечные проблемы, поэтому я могу просто не улавливать тех сложностей, которые возникают при крупномасштабном использовании Kubernetes. Я тоже работаю на облачной платформе, поэтому совершенно не представляю, из чего там сделан машинный интерфейс – может быть, из воска. Однако, как пользователь инновационной системы я догадываюсь, почему многие люди изыскивают альтернативные способы ее загрузки.
Некоторое время мы присматриваемся к книгам по Kubernetes, благо, они уже выходят и в Manning, и в O'Reilly. Можно согласиться, что Kubernetes в наших краях пока интересен скорее с ознакомительной и инженерной, нежели с практической точки зрения. Однако, мы все-таки поставим здесь обложку с книги о Kubernetes и перевод статьи Дэниэла Морсинга, который сделал у себя в блоге интересный тизер об этой системе.
Приятного чтения!
Введение
Отправляясь в турне по конференциям о языке Go, я примерно по три раза в год слушаю разные лекции о том, как еще можно загрузить Kubernetes. При этом, я имел весьма зыбкое представление о том, что же такое Kubernetes, о глубоких знаниях говорить и не приходилось. Но вот у меня выдалось свободное время, и я решил: попробую портировать на Kubernetes простое приложение, повожусь с ним и опишу мои первые впечатления.
Я не собираюсь писать пост из разряда «Знакомство с Kubernetes», поскольку об этом лучше почитать в официальной документации, даже не стану с ней тягаться. На самом деле, документация очень хорошая. В частности, мне очень понравился раздел «Concepts», когда я пытался составить общее представление о системе. Авторы документации – снимаю шляпу!
Расскажу о приложении. У меня есть собственный блог-сервер, на котором лежит оригинал этой статьи. Так сложилось, что он работает на небольшом инстансе Linode, которым я управляю вручную. Может показаться, что применять целый стек управления кластером для развертывания такого маленького приложения – это стрельба из пушки по воробьям; в самом деле, так и есть. Но я обнаружил, что именно таким образом удобно напрактиковаться в работе с системой. На момент публикации оригинала мой блог работал на одноузловом кластере Google Container Engine.
Жизненные циклы подов
Kubernetes славится своим отлаженным планировщиком. Для каждой развернутой системы, управляемой через Kubernetes, назначается группа контейнеров (так называемый «под»), в которой можно запускать машины (так называемые «узлы»). В процессе выкатывания и масштабирования ресурсов новые поды создаются и упраздняются, в зависимости от требований реплики. Такое планирование обеспечивает более рациональное использование ресурсов, но мне кажется, что не так революционны сами поды, как та среда, в которой функционирует Kubernetes. Kubernetes обеспечивает «из коробки» управление образами, внутреннюю систему доменных имен и автоматизацию выкатывания. Поэтому мне кажется целесообразным выстраивать такую систему, где конкретные поды соотносятся с конкретными узлами, поэтому какая-либо надобность в планировщике фактически отпадает.
Одна из задач, которые, по-видимому, не решаются в рамках данного жизненного цикла – система не умеет работать с «горячими» кэшами. В принципе, держать такие кэши – плохая идея, но на практике они кое-где попадаются в кластерах. Если у вас есть memcache-контейнер, а вместе с ним вы эксплуатируете какой-либо сервер, то апгрейд такого сервера неизбежно требует сначала убить memcache. Существует механизм для работы с подами, сохраняющими состояние, но при этом все состояние требуется дампировать на диск, а затем считывать это состояние обратно, когда под будет перепланирован. Если такая необходимость в самом деле возникнет, то вам вряд ли импонирует перспектива дожидаться, пока все это хозяйство восстановится с диска.
Сети
Сеть, обеспечивающая обмен информацией между подами, устроена довольно красиво. На платформе Google Cloud Platform каждый узел получает /24 подсети в закрытом диапазоне 10.0.0.0/8, а каждый под затем получает собственный IP. Затем на каждом поде вы можете использовать для ваших сервисов любой диапазон портов, какой вам заблагорассудится. При такой изоляции исключается ситуация, в которой несколько приложений пытались бы подключиться к одному и тому же порту. Если вы хотите сбросить http-сервер на порту 80, можете так и сделать, не беспокоясь при этом о других http-серверах. Большинство приложений «обучены» избегать 80, но многие штуки открывают на 8080 отладочные серверы, и мне известно, как из-за этого падали целые системы.
Еще одно достоинство такой работы с пространствами имен – подобная система позволяет иметь дело с различными программами, которые могут быть настроены на взаимодействие с нестандартными портами. На самом деле, держать DNS на любом порту кроме 53 и правда чертовски сложно, поскольку на такую DNS ниоткуда будет не отправить запрос.
В основе такой изоляции лежит соотношение «у каждого узла своя подсеть». Если провайдер выделяет вам всего один IP на каждый узел, то придется устанавливать какую-либо оверлейную сеть, а такие сети (по моему опыту) работают не слишком хорошо.
Итак, коммуникация между подами налажена хорошо, но вам ведь еще потребуется точно определить все IP-адреса, чтобы обеспечить коммуникацию между ними. Для этого в Kubernetes существуют так называемые «сервисы». По умолчанию каждый «сервис» получает конкретный внутренний кластерный IP. Этот IP можно узнать по внутренней системе доменных имен и подключиться к нему. Если у вас найдется несколько подов, подходящих тому или иному сервису, то между ними автоматически включится балансировка нагрузки, обработкой которой займется узел, инициировавший коммуникацию.
Я еще не могу определиться с тем, нравится ли мне «единый IP на весь кластер». Большинство приложений изрядно плохо умеют обрабатывать несколько значений DNS, полученных скопом, и будут выбирать тот результат, что получат первым — из-за чего распределение ресурсов будет неравномерным. Единый IP на кластер снимает эту проблему. Однако, здесь мы попадаем в классическую ловушку – путаем обнаружение сервисов и поддержание их в «живом» виде. Переходя к работе со все более крупными кластерами, мы встречаем все больше несимметричных сегментов. Возможно, тот узел, на котором хостится под, сможет сам выполнять проверку работоспособности и докладывать о ней на ведущий узел Kubernetes, но с других узлов до него будет не достучаться. Когда это произойдет, балансировщик нагрузки все равно будет пытаться выйти на отказавший узел, а поскольку на весь кластер один IP – резервного варианта у вас не будет. Можно попробовать завершить соединение и попытаться еще раз, в надежде, что при повторном распределении нагрузки TCP-соединение пойдет через другой узел, рабочий, но такое решение неоптимально.
При развертывании Kubernetes советую организовать такую проверку работоспособности: узнавать, сколько запросов было подано в конкретный под со времени предыдущей проверки. Если это количество ниже некоторого предела – пометьте, что под барахлит. Таким образом, балансировщик нагрузки должен быстро научиться избегать этот под, даже если он достижим с ведущего узла Kubernetes. Можно конфигурировать сервисы так, чтобы получать IP подов напрямую, но, если вам не требуется сетевой идентификатор, я не вижу в этом необходимости.
Поскольку у подов закрытые IP, не маршрутизируемые через общедоступный Интернет, нужен какой-то механизм трансляции локальных портов в маршрутизируемые, если требуется обратиться к той или иной информации извне кластера. Обычно я не в восторге от создания NAT в таких случаях, поэтому логичнее всего выглядел бы переход на IPv6: просто выделяем каждому узлу публично маршрутизируемую подсеть, и проблема снимается сама собой. К сожалению, Kubernetes не поддерживает IPv6.
На практике NAT – не такая большая проблема. При работе с трафиком, приходящим извне кластера, Kubernetes стимулирует нас активно работать с такими сервисами, которые взаимодействуют с облачными балансировщиками нагрузки, предоставляющими одиночный IP. Поскольку многие разработчики Kubernetes занимались оркестровкой в Google, неудивительно, что описываемый механизм построен по модели Maglev.
К сожалению, я еще не придумал, как не только наладить взаимодействие сервиса со внешним миром, но при этом еще и обеспечить высокую доступность в среде, где нет такого балансировщика нагрузки. Можно приказать Kubernetes перенаправлять на внешний узел весь трафик, доходящий до кластера, но при распределении подов по узлам IP не учитываются. Если под выводится с того узла, на который маршрутизирован этот IP, то узлу приходится заниматься организацией NAT – значит, мы нагружаем его лишней работой. Другая проблема – так называемые конфликты портов на внешнем IP-уровне. Если у вас есть контроллер, обновивший внешние IP (а также информацию в DNS-службе, которой вы пользуетесь) в зависимости от того, на каких узлах «приземлились» эти IP, может случиться так, что два пода одновременно захотят трафик с порта 80, но у вас не будет никакого шанса отличить один IP от другого.
Пока я как-то выживаю с облачным балансировщиком нагрузки. Так проще, а я не собираюсь в обозримом будущем задействовать Kubernetes где-либо вне облака.
Еще немного об облачной магии
Другая область, в которой активно используется облачная магия — это управление постоянными хранилищами. Существующие варианты попадания на диск, к которому возможен доступ с пода, явственно завязаны на работу с облачными провайдерами. Да, можно создать хранилище, по сути представляющее собой просто съемный диск на узле, но эта возможность пока находится в состоянии «альфа» и в продакшене не используется. Было бы интересно проверить, можно ли сделать начальную загрузку NFS-сервиса, работающего на Kubernetes, а затем организовать, чтобы именно через него Kubernetes управлял постоянными хранилищами, но я пока не планирую браться за такую задачу.
Кому-то кажется, что облачная магия – последняя капля, заставляющая от всего этого отказаться, но сегодня кластерные вычисления так сильно зависят от облачных сервисов, что обойтись без облаков очень сложно. Я замечаю, что люди стараются не впутываться в эту магию, чтобы избежать зависимости от поставщика, однако, при этом они недооценивают стоимость самостоятельной разработки, а также множества неявных моментов, подразумеваемых при работе на облачной платформе. Kubernetes предоставляет согласованный интерфейс, через который удобно заниматься этой облачной магией. Так что, доверять ему можно, и он (как минимум) технически стандартизирован
Вывод
Вот и закончился наш маленький экскурс в Kubernetes – надеюсь, вам понравилось. Естественно, в статье рассмотрены игрушечные проблемы, поэтому я могу просто не улавливать тех сложностей, которые возникают при крупномасштабном использовании Kubernetes. Я тоже работаю на облачной платформе, поэтому совершенно не представляю, из чего там сделан машинный интерфейс – может быть, из воска. Однако, как пользователь инновационной системы я догадываюсь, почему многие люди изыскивают альтернативные способы ее загрузки.
Комментарии (4)
Antiarchitect
05.11.2017 14:18Для каждой развернутой системы, управляемой через Kubernetes, назначается группа контейнеров (так называемый «под»), в которой можно запускать машины (так называемые «узлы»).
Что, простите? Под — это несколько (чаще один) контейнеров, объединенных общим жизненным циклом, атом управления в kubernetes. В поде нельзя запускать узлы, наоборот, на множестве узлов запускаются сервисы, состоящие из подов.demonight
06.11.2017 20:49Да, это не совсем корректный перевод. В оригинале это звучит вот так:
For each deployment that Kubernetes manages, it schedules sets of containers (known as pods) onto machines to be run (known as nodes).
Или «внутри машин для запуска (так называемые «узлы»)»
scalavod
07.11.2017 23:46Можно согласиться, что Kubernetes в наших краях пока интересен скорее с ознакомительной и инженерной, нежели с практической точки зрения.
Интересен и с парктической. Например, на Avito уже больше года используется Kubernetes в качестве платформы для микросервисов. Так что и в наших краях книга будет полезной.
nightvich
Питер, я вас обожаю! А после того, как вы дали скидку постоянного покупателя, беру книги только у вас (на прямую). Очень буду рад выходу данной книги на русском языке.