Здравствуйте, коллеги!
В январе у нас наконец-то выходит долгожданная книга по Kubernetes. Речь о «Mastering Kubernetes 2nd edition» Джиджи Сайфана:
Мы не решились издавать книгу по Kubernetes около года назад, так как на тот момент технология определенно напоминала дредноут для суперкорпораций. Однако, ситуация меняется, в подтверждение чего мы предлагаем почитать большую статью Калеба Докси (Caleb Doxsey), написавшего, кстати, книгу о языке Go. Аргументы господина Докси очень интересны, и мы надеемся, что после ознакомления с ними вам действительно захочется попробовать Kubernetes на практике.
Несколько месяцев в начале этого года я потратил на углубленное изучение Kubernetes: он требовался мне для одного проекта по работе. Kubernetes – это комплексная технология для управления инфраструктурой, в него «включено все, даже батарейки». Kubernetes решает ряд проблем, с которыми вы просто обречены столкнуться при разработке для больших предприятий. Однако, растиражировано убеждение, что Kubernetes – это чрезмерно переусложненная технология, уместная лишь при управлении крупным кластером машин. Якобы эксплуатационная нагрузка при работе с Kubernetes так велика, что использование его для малых инфраструктур, где счет машин не идет на десятки – это стрельба из пушки по воробьям.
Позволю себе с этим не согласиться. Kubernetes хорош и для малых проектов, а сегодня вы уже можете позволить себе собственный кластер Kubernetes менее чем за $5 в месяц.
Слово в защиту Kubernetes
Чуть ниже я покажу, как настроить собственный кластер Kubernetes, но сначала попробую объяснить, почему Kubernetes стоит использовать в небольших проектах:
Kubernetes основателен
Да, на первый взгляд Kubernetes представляется несколько избыточным решением. Кажется, не проще ли взять и обзавестись виртуальной машиной и не сконфигурировать собственное приложение как сервис, почему бы и нет? Избрав этот путь, вам придется определиться с некоторыми решениями, в частности:
Kubernetes позволяет решить все эти проблемы. Естественно, все они решаемы и другими способами, среди которых есть и варианты получше Kubernetes; однако, насколько же лучше вообще не задумываться обо всем этом и сосредоточиться на разработке приложения.
Kubernetes надежен
Единственный сервер обязательно когда-нибудь обваливается. Да, это бывает редко, может быть раз в год, но после такого события начинается настоящая головная боль: как все вернуть в рабочее состояние. Это особенно актуально, если всю конфигурацию вы сами настроили вручную. Помните все команды, которые запускали в прошлый раз? Помните ли вообще, что работало на сервере? Мне вспоминается одна цитата с башорга:
В Kubernetes используется описательный формат, поэтому вам всегда известно, какие вещи, когда и где предполагалось запускать; кроме того, гораздо четче просматриваются все составляющие развернутой вами системы. Более того, в контрольной плоскости отказ узлов обрабатывается аккуратно, а поды автоматически перераспределяются. При работе с сервисом, не сохраняющим состояния, например, с веб-приложением, вы, пожалуй, сможете вообще забыть об отказах.
Изучить Kubernetes не сложнее, чем имеющиеся альтернативы
Kubernetes не следует модели Unix. Он не вписывается в экосистему инструментов. Он не из тех решений, которые «делают всего одну вещь и делают ее хорошо». Kubernetes – это всеобъемлющее решение для множества проблем, способен заменить разнообразные приемы и инструменты, к которым разработчики уже успели привыкнуть.
В Kubernetes есть собственная терминология, свой инструментарий, своя парадигма обращения с серверами, которая существенно отличается от традиционного подхода unix. Когда вы ориентируетесь в этих системах, многие характерные особенности Kubernetes могут показаться случайными и переусложненными, возможно, даже жестокими. Полагаю, есть веские причины, по которым возникла такая сложность, но здесь я и не утверждаю, что Kubernetes прост и элементарен для понимания; я говорю о том, что знаний Kubernetes достаточно для создания и поддержки любой инфраструктуры.
Нельзя сказать, что у любого сисадмина есть достаточный бэкграунд в unix. Например, по окончании колледжа я 5 лет проработал в экосистеме Windows. Могу сказать, что моя первая работа на стартапе, где понадобилось иметь дело с linux, потребовала непростой трансформации. Команд я на память не знал, использовать командную строку практически на все случаи жизни не привык. Мне потребовалось некоторое время, чтобы научиться работать с новой платформой (хотя, к тому моменту у меня уже был некоторый опыт программирования), но я отчетливо помню, сколько тогда натерпелся.
С Kubernetes можно начать всю работу буквально с нуля. В Kubernetes вы без малейшего труда можете выделять сервисы, даже без SSH-подключения к серверу. Вам не придется учить systemd; не обязательно разбираться в уровнях запуска или знать, какая команда была использована:
На самом ли деле вот это:
Намного сложнее вот этого?
И это еще сравнительно удачный случай. Если вы управляете инфраструктурой на 100% удаленно, то не сможете обеспечить поддержку серверов вручную. Для этого вам понадобится какой-нибудь инструмент: ansible, salt, chef, puppet, т.д. Естественно, чтобы овладеть Kubernetes и эффективно с ним работать, нужно много выучить, но это не сложнее, чем разобраться с альтернативами.
Kubernetes опенсорсный
В эпоху огромной популярности бессерверных технологий Kubernetes примечателен своей независимостью от конкретных поставщиков. Есть как минимум 3 популярных и удобных в управлении провайдеров Kubernetes (Google, Amazon, Microsoft), которые в обозримом будущем никуда не исчезнут. Также есть множество компаний, которые успешно справляются с собственными кластерами Kubernetes, и каждый день число таких компаний множится. Сегодня работа с Kubernetes уже с первого дня – вполне очевидное решение для большинства стартапов.
Kubernetes, будучи опенсорсным проектом, хорошо документирован, стабилен и популярен, а любые проблемы можно максимально подробно разобрать на stackoverflow. Конечно же, в Kubernetes есть свои баги и технические вызовы, но я вас уверяю: есть на свете такие ребята, которые оттачивают Kubernetes с немыслимым мастерством. Их труд – ваши дивиденды; в ближайшие несколько лет эта технология будет только совершенствоваться.
Kubernetes масштабируется
Один из вызовов, связанных с поддержкой инфраструктуры, заключается в следующем: приемы, целесообразные при развертывании малых систем, нечасто получается удачно воспроизвести в более крупных системах. Определенно удобно SCP-нуть двоичный файл на сервер, убить процесс и перезапустить его, если сервер у вас всего один. Но, когда требуется поддерживать несколько серверов и одновременно отслеживать их, такая задача может получиться ошеломительно сложной. Вот почему при управлении такой инфраструктурой вам не обойтись без инструментов вроде chef или puppet.
Однако, если вы выберете неподходящий инструмент, со временем это может загнать вас в угол. Внезапно окажется, что ведущий chef-сервер не справляется с 1000-серверной нагрузкой, сине-зеленое развертывание не вписывается в вашу модель, а на выполнение задач capistrano уходят целые часы. Когда инфраструктура достигнет определенного размера, вы будете вынуждены снести все, что уже сделано, и начинать заново. Как было бы здорово, если бы удалось вырваться из этого вечного беличьего колеса с инфраструктурой и перейти на технологию, которая будет масштабироваться сообразно вашим потребностям?
Kubernetes во многом напоминает SQL-базу данных. SQL – продукт многолетних суровых уроков о хранении данных и их эффективном запрашивании. Вероятно, вам никогда не понадобится и десятой доли тех возможностей, что предоставляются в годной SQL-базе данных. Возможно, вам даже удалось бы сконструировать и более эффективную систему, опираясь на собственную базу данных. Но в абсолютном большинстве ситуаций SQL-база данных не только удовлетворит все ваши нужды, но и кардинально расширит ваши возможности по быстрой выдаче готовых решений. SQL-схемы и индексирование гораздо проще в использовании, чем собственные структуры данных, основанные на файлах, поскольку собственные структуры данных почти наверняка устареют по мере роста и развития вашего продукта со временем. Но SQL-база данных, вероятно, переживет любой неизбежный рефакторинг.
Kubernetes тоже переживет. Возможно, ваш сайд-проект никогда не дорастет до таких масштабов, где его проблемы можно будет решить только средствами Kubernetes, но в Kubernetes найдутся абсолютно все инструменты для на случай любых проблем, а навыки, которые вы приобретете при обращении с этим инструментарием, могут оказаться бесценны в будущих проектах.
Собираем собственный кластер Kubernetes
Итак, я считаю, что использовать Kubernetes в малых проектах целесообразно, но только если настройка кластера выйдет простой и малозатратной. Оказывается, и то, и другое достижимо. Существуют управляемые провайдеры Kubernetes, самостоятельно улаживающие всю путаницу с поддержкой контрольной плоскости ведущего узла Kubernetes. А недавние демпинговые войны в среде облачной инфраструктуры привели к поразительному удешевлению таких услуг.
Следующий кейс мы разберем на примере Kubernetes-движка от Google (GKE), однако, вы также можете присмотреться к предложениям от Amazon (EKS) или Microsoft (AKS), если Google вас не устроит. Для сборки собственного кластера Kubernetes нам понадобится:
Для дополнительной экономии мы постараемся обойтись без гугловского входного контроллера. Вместо этого будем использовать Nginx на каждом узле в качестве демона и создадим собственный оператор, который обеспечит нам синхронизацию внешних IP-адресов рабочего узла с Cloudflare.
Конфигурация Google
Сначала отправляемся в console.cloud.google.com и создаем проект, если еще не сделали этого. Также нужно будет создать платежный аккаунт. Затем через гамбургерное меню выходим на страницу Kubernetes и создаем новый кластер. Вот что нужно сделать далее:
Проставив все эти опции, можно переходить к следующему этапу: создавать кластер. Вот как на этом сэкономить:
Вот мы и настроили кластер Kubernetes из 3 узлов, он обошелся нам в ту же цену, что и единственная машина Digital Ocean.
Сверх настройки GKE также нужно настроить пару правил брандмауэра, чтобы на HTTP-порты наших узлов можно было достучаться из внешнего мира. Найдите в гамбургерном меню запись VPC Network, далее перейдите в Firewall Rules и добавьте правила для TCP-портов 80 и 443, с диапазоном IP-адресов 0.0.0.0/0.
Правила брандмауэра
Локальная настройка
Итак, мы подняли и запустили кластер, а теперь давайте его сконфигурируем. Установите инструмент
Конечно же, вам еще придется установить docker, а затем привязать его к GCR, так, чтобы можно было отправлять контейнеры:
Также можно установить и настроить
Упрощенно:
Кстати, просто сказка, что весь этот инструментарий работает в Windows, OSX или Linux. Как человек, иногда занимавшийся такими вещами под Windows, признаюсь, что это приятная неожиданность.
Сборка веб-приложения
Веб-приложение можете писать на любом языке программирования. Контейнер позволяет абстрагировать частности. Мы должны создать HTTP-приложение, слушающее порт. Я предпочитаю для таких целей язык Go, но для разнообразия попробуем crystal. Создаем файл
Также нам понадобится Dockerfile:
Чтобы собрать и протестировать наше приложение, запустим:
А затем перейдем в браузере по адресу localhost:8080. Наладив этот механизм, сможем отправить наше приложение в GCR, запустив:
Конфигурируем Kubernetes
Моя конфигурация Kubernetes находится здесь.
Для данного примера нам придется создать несколько файлов в формате yaml, где будут представлены наши различные сервисы, а затем запустить kubectl apply, чтобы сконфигурировать их в кластере. Конфигурация Kubernetes является описательной, и все эти yaml-файлы сообщают Kubernetes, какое состояние мы хотим получить. В широком смысле вот что мы собираемся сделать:
Конфигурация веб-приложения
Сначала давайте сконфигурируем наше веб-приложение: (убедитесь, что заменили
Так создается Deployment (развернутая конфигурация), согласно которой Kubernetes должен создать под с единственным контейнером (там будет работать наш docker-контейнер) и сервис, который мы применим для обнаружения сервисов в нашем кластере. Чтобы применить эту конфигурацию, запустите (из каталога
Можно это протестировать вот так:
Также мы можем создать прокси-API для доступа:
А затем перейти: localhost:8001/api/v1/namespaces/default/services/crystal-www-example/proxy/
Конфигурация NGINX
Как правило, при работе с HTTP-сервисами в Kubernetes применяется входной контроллер. К сожалению, HTTP-балансировщик нагрузки от Google обходится слишком дорого, поэтому мы не станем его использовать, а задействуем собственный HTTP-прокси и сконфигурируем его вручную (звучит страшно, а на деле все очень просто).
Для этого воспользуемся Daemon Set и Config Map. Daemon Set – это приложение, работающее на каждом узле. Config Map – это, в принципе, небольшой файл, который мы можем монтировать в контейнере; в этом файле будет храниться конфигурация nginx.
Файл yaml выглядит примерно так:
Вот как мы монтируем файл nginx.conf конфигурационной карты в контейнере nginx. Также мы задаем значения еще для двух полей:
Примените эти выражения – и сможете выходить на nginx по общедоступным ip ваших узлов.
Вот как можно в этом убедиться:
Итак, теперь наше веб-приложение доступно из Интернета. Осталось придумать для приложения красивое название.
Подключение DNS
Требуется задать 3 A DNS-записи для узлов нашего кластера:
Записи в UI Cloudflare
Затем добавляем запись CNAME, чтобы указать на эти A-записи. (напр. www.example.com CNAME для kubernetes.example.com). Это можно сделать вручную, но лучше – автоматически, чтобы, если когда-нибудь нам придется масштабироваться или заменить узлы в DNS-записях, эта информация также обновлялась автоматически.
Думаю, этот пример также хорошо иллюстрирует, как можно делегировать Kubernetes часть вашей работы, а не пытаться побороть его. Kubernetes отлично понимает скрипты и обладает мощным API, а имеющиеся пробелы можно заполнить собственными компонентами, написать которые не так сложно. Для этого я сделал небольшое приложение на Go, доступное по этому адресу: kubernetes-cloudflare-sync.
Для начала создал информатор:
Он будет вызывать мою функцию пересинхронизации при любом изменении узла. Затем я синхронизирую API при помощи библиотеки Cloudflare API, примерно так:
Затем, как и в случае с нашим веб-приложением, мы запускаем это приложение в Kubernetes в виде Deployment:
Нам понадобится создать секрет Kubernetes, указав ключ
Также нам понадобится создать служебный аккаунт (открывающий нашему Deployment доступ к Kubernetes API для извлечения узлов). Сначала запустите (в особенности касается GKE):
А затем примените:
Работать с RBAC немного муторно, но, надеюсь, тут все понятно. Когда конфигурация готова, а наше приложение работает с Cloudflare, это приложение можно обновлять при каком угодно изменении любого из узлов.
Заключение
Kubernetes суждено стать флагманской технологией для управления крупными системами. Хотя, крупномасштабная эксплуатация Kubernetes связана с серьезными техническими вызовами, а сама технология Kubernetes еще не вполне устоялась, количество развернутых экземпляров Kubernetes достигло критической массы, и в ближайшие годы весь Kubernetes ожидают кардинальные усовершенствования.
Здесь я постарался доказать, что Kubernetes целесообразно использовать и при развертывании небольших систем: сегодня это уже стало и просто, и недорого. Если ранее вы этого не делали – самое время попробовать!
В январе у нас наконец-то выходит долгожданная книга по Kubernetes. Речь о «Mastering Kubernetes 2nd edition» Джиджи Сайфана:
Мы не решились издавать книгу по Kubernetes около года назад, так как на тот момент технология определенно напоминала дредноут для суперкорпораций. Однако, ситуация меняется, в подтверждение чего мы предлагаем почитать большую статью Калеба Докси (Caleb Doxsey), написавшего, кстати, книгу о языке Go. Аргументы господина Докси очень интересны, и мы надеемся, что после ознакомления с ними вам действительно захочется попробовать Kubernetes на практике.
Несколько месяцев в начале этого года я потратил на углубленное изучение Kubernetes: он требовался мне для одного проекта по работе. Kubernetes – это комплексная технология для управления инфраструктурой, в него «включено все, даже батарейки». Kubernetes решает ряд проблем, с которыми вы просто обречены столкнуться при разработке для больших предприятий. Однако, растиражировано убеждение, что Kubernetes – это чрезмерно переусложненная технология, уместная лишь при управлении крупным кластером машин. Якобы эксплуатационная нагрузка при работе с Kubernetes так велика, что использование его для малых инфраструктур, где счет машин не идет на десятки – это стрельба из пушки по воробьям.
Позволю себе с этим не согласиться. Kubernetes хорош и для малых проектов, а сегодня вы уже можете позволить себе собственный кластер Kubernetes менее чем за $5 в месяц.
Слово в защиту Kubernetes
Чуть ниже я покажу, как настроить собственный кластер Kubernetes, но сначала попробую объяснить, почему Kubernetes стоит использовать в небольших проектах:
Kubernetes основателен
Да, на первый взгляд Kubernetes представляется несколько избыточным решением. Кажется, не проще ли взять и обзавестись виртуальной машиной и не сконфигурировать собственное приложение как сервис, почему бы и нет? Избрав этот путь, вам придется определиться с некоторыми решениями, в частности:
- Как развернуть приложение? Просто rsync его на сервер?
- Что насчет зависимостей? Если вы работаете с Python или Ruby, то их придется установить на сервере. Вы собираетесь просто запускать команды вручную?
- Как вы собираетесь запускать приложение? Просто выполнить двоичный файл в фоновом режиме, а затем nohup его? Вероятно, это не слишком хорошо, поэтому, если организовать приложение как сервис, то придется учить systemd?
- Как вы собираетесь справляться с эксплуатацией множества приложений, когда у всех у них разные доменные имена или http-пути? (вероятно, для этого понадобится настроить haproxy или nginx)
- Допустим, вы обновили ваше приложение. Как в таком случае будете выкатывать изменения? Остановите сервис, развернете код, перезапустите сервис? Как избежать простоев?
- Что, если запорете развертывание? Есть возможности откатиться? (Symlink каталог...? этот простой скрипт уже не кажется особо простым)
- Используются ли в вашем приложении другие сервисы, например, redis? Как сконфигурировать все эти сервисы?
Kubernetes позволяет решить все эти проблемы. Естественно, все они решаемы и другими способами, среди которых есть и варианты получше Kubernetes; однако, насколько же лучше вообще не задумываться обо всем этом и сосредоточиться на разработке приложения.
Kubernetes надежен
Единственный сервер обязательно когда-нибудь обваливается. Да, это бывает редко, может быть раз в год, но после такого события начинается настоящая головная боль: как все вернуть в рабочее состояние. Это особенно актуально, если всю конфигурацию вы сами настроили вручную. Помните все команды, которые запускали в прошлый раз? Помните ли вообще, что работало на сервере? Мне вспоминается одна цитата с башорга:
erno: Хм. Потерял комп… серьезно, _потерял_. Он пингуется, работает нормально, просто ума не приложу, куда он подевался в квартире.Именно то же самое недавно произошло со мной, в моем собственном блоге. Мне нужно было просто обновить ссылку, но я напрочь забыл, как развертывать блог. Внезапно десятиминутная починка обернулась куском работы длиной целый уикенд.
bash.org/?5273
В Kubernetes используется описательный формат, поэтому вам всегда известно, какие вещи, когда и где предполагалось запускать; кроме того, гораздо четче просматриваются все составляющие развернутой вами системы. Более того, в контрольной плоскости отказ узлов обрабатывается аккуратно, а поды автоматически перераспределяются. При работе с сервисом, не сохраняющим состояния, например, с веб-приложением, вы, пожалуй, сможете вообще забыть об отказах.
Изучить Kubernetes не сложнее, чем имеющиеся альтернативы
Kubernetes не следует модели Unix. Он не вписывается в экосистему инструментов. Он не из тех решений, которые «делают всего одну вещь и делают ее хорошо». Kubernetes – это всеобъемлющее решение для множества проблем, способен заменить разнообразные приемы и инструменты, к которым разработчики уже успели привыкнуть.
В Kubernetes есть собственная терминология, свой инструментарий, своя парадигма обращения с серверами, которая существенно отличается от традиционного подхода unix. Когда вы ориентируетесь в этих системах, многие характерные особенности Kubernetes могут показаться случайными и переусложненными, возможно, даже жестокими. Полагаю, есть веские причины, по которым возникла такая сложность, но здесь я и не утверждаю, что Kubernetes прост и элементарен для понимания; я говорю о том, что знаний Kubernetes достаточно для создания и поддержки любой инфраструктуры.
Нельзя сказать, что у любого сисадмина есть достаточный бэкграунд в unix. Например, по окончании колледжа я 5 лет проработал в экосистеме Windows. Могу сказать, что моя первая работа на стартапе, где понадобилось иметь дело с linux, потребовала непростой трансформации. Команд я на память не знал, использовать командную строку практически на все случаи жизни не привык. Мне потребовалось некоторое время, чтобы научиться работать с новой платформой (хотя, к тому моменту у меня уже был некоторый опыт программирования), но я отчетливо помню, сколько тогда натерпелся.
С Kubernetes можно начать всю работу буквально с нуля. В Kubernetes вы без малейшего труда можете выделять сервисы, даже без SSH-подключения к серверу. Вам не придется учить systemd; не обязательно разбираться в уровнях запуска или знать, какая команда была использована:
groupadd
или addgroup
; вам не придется учиться обращению с ps
или, Боже упаси, vim. Вся эта матчасть полезна и важна, ничто из нее никуда не исчезает. Я с огромным уважением отношусь к сисадминам, которые способны прокодить себе путь через любую unix-среду. Но как было бы круто, если бы разработчики могли продуктивно обзаводиться всеми подобными ресурсами, не вникая в такие тонкости администрирования? На самом ли деле вот это:
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Намного сложнее вот этого?
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
И это еще сравнительно удачный случай. Если вы управляете инфраструктурой на 100% удаленно, то не сможете обеспечить поддержку серверов вручную. Для этого вам понадобится какой-нибудь инструмент: ansible, salt, chef, puppet, т.д. Естественно, чтобы овладеть Kubernetes и эффективно с ним работать, нужно много выучить, но это не сложнее, чем разобраться с альтернативами.
Kubernetes опенсорсный
В эпоху огромной популярности бессерверных технологий Kubernetes примечателен своей независимостью от конкретных поставщиков. Есть как минимум 3 популярных и удобных в управлении провайдеров Kubernetes (Google, Amazon, Microsoft), которые в обозримом будущем никуда не исчезнут. Также есть множество компаний, которые успешно справляются с собственными кластерами Kubernetes, и каждый день число таких компаний множится. Сегодня работа с Kubernetes уже с первого дня – вполне очевидное решение для большинства стартапов.
Kubernetes, будучи опенсорсным проектом, хорошо документирован, стабилен и популярен, а любые проблемы можно максимально подробно разобрать на stackoverflow. Конечно же, в Kubernetes есть свои баги и технические вызовы, но я вас уверяю: есть на свете такие ребята, которые оттачивают Kubernetes с немыслимым мастерством. Их труд – ваши дивиденды; в ближайшие несколько лет эта технология будет только совершенствоваться.
Kubernetes масштабируется
Один из вызовов, связанных с поддержкой инфраструктуры, заключается в следующем: приемы, целесообразные при развертывании малых систем, нечасто получается удачно воспроизвести в более крупных системах. Определенно удобно SCP-нуть двоичный файл на сервер, убить процесс и перезапустить его, если сервер у вас всего один. Но, когда требуется поддерживать несколько серверов и одновременно отслеживать их, такая задача может получиться ошеломительно сложной. Вот почему при управлении такой инфраструктурой вам не обойтись без инструментов вроде chef или puppet.
Однако, если вы выберете неподходящий инструмент, со временем это может загнать вас в угол. Внезапно окажется, что ведущий chef-сервер не справляется с 1000-серверной нагрузкой, сине-зеленое развертывание не вписывается в вашу модель, а на выполнение задач capistrano уходят целые часы. Когда инфраструктура достигнет определенного размера, вы будете вынуждены снести все, что уже сделано, и начинать заново. Как было бы здорово, если бы удалось вырваться из этого вечного беличьего колеса с инфраструктурой и перейти на технологию, которая будет масштабироваться сообразно вашим потребностям?
Kubernetes во многом напоминает SQL-базу данных. SQL – продукт многолетних суровых уроков о хранении данных и их эффективном запрашивании. Вероятно, вам никогда не понадобится и десятой доли тех возможностей, что предоставляются в годной SQL-базе данных. Возможно, вам даже удалось бы сконструировать и более эффективную систему, опираясь на собственную базу данных. Но в абсолютном большинстве ситуаций SQL-база данных не только удовлетворит все ваши нужды, но и кардинально расширит ваши возможности по быстрой выдаче готовых решений. SQL-схемы и индексирование гораздо проще в использовании, чем собственные структуры данных, основанные на файлах, поскольку собственные структуры данных почти наверняка устареют по мере роста и развития вашего продукта со временем. Но SQL-база данных, вероятно, переживет любой неизбежный рефакторинг.
Kubernetes тоже переживет. Возможно, ваш сайд-проект никогда не дорастет до таких масштабов, где его проблемы можно будет решить только средствами Kubernetes, но в Kubernetes найдутся абсолютно все инструменты для на случай любых проблем, а навыки, которые вы приобретете при обращении с этим инструментарием, могут оказаться бесценны в будущих проектах.
Собираем собственный кластер Kubernetes
Итак, я считаю, что использовать Kubernetes в малых проектах целесообразно, но только если настройка кластера выйдет простой и малозатратной. Оказывается, и то, и другое достижимо. Существуют управляемые провайдеры Kubernetes, самостоятельно улаживающие всю путаницу с поддержкой контрольной плоскости ведущего узла Kubernetes. А недавние демпинговые войны в среде облачной инфраструктуры привели к поразительному удешевлению таких услуг.
Следующий кейс мы разберем на примере Kubernetes-движка от Google (GKE), однако, вы также можете присмотреться к предложениям от Amazon (EKS) или Microsoft (AKS), если Google вас не устроит. Для сборки собственного кластера Kubernetes нам понадобится:
- Доменное имя (~10$/год, в зависимости от домена)
- DNS-хостинг от cloudflare (бесплатно)
- Кластер Kubernetes от GKE, состоящий из трех узлов (~5$/месяц)
- Веб-приложение, загруженное в виде docker-контейнера в Google Container Registry (GCR) (бесплатно)
- Некоторое количество yaml-файлов для конфигурации Kubernetes
Для дополнительной экономии мы постараемся обойтись без гугловского входного контроллера. Вместо этого будем использовать Nginx на каждом узле в качестве демона и создадим собственный оператор, который обеспечит нам синхронизацию внешних IP-адресов рабочего узла с Cloudflare.
Конфигурация Google
Сначала отправляемся в console.cloud.google.com и создаем проект, если еще не сделали этого. Также нужно будет создать платежный аккаунт. Затем через гамбургерное меню выходим на страницу Kubernetes и создаем новый кластер. Вот что нужно сделать далее:
- Выбрать Zonal для типа местоположения (Location).
- Я указал мое расположение как us-central1-a
- Выбрать вашу версию kubernetes
- Создать пул на 3 узла, воспользовавшись самым дешевым типом инстанса (f1-micro).
- Для этого пула узлов, на экране «дополнительно» (advanced) установим размер загрузочного диска в 10GB, включим вытесняемые узлы (они дешевле), включим автообновление и автолечение.
- Под пулом узлов найдете ряд дополнительных опций. Мы хотим отключить HTTP-балансировку нагрузки (балансировка нагрузки в GCP дорого обходится), а также отключить все хозяйство, связанное с StackDriver (тоже может дорого обходиться и, по моему опыту, не слишком надежное). Также отключим индикаторную панель kubernetes.
Проставив все эти опции, можно переходить к следующему этапу: создавать кластер. Вот как на этом сэкономить:
- Контрольная плоскость Kubernetes: бесплатно, поскольку Google не тарифицирует ведущие узлы
- Рабочие узлы Kubernetes: $5.04/мес, как правило, 3 микроузла обойдутся вам в $11.65/мес, а, сделав их вытесняемыми, мы снизим эту ставку до $7.67/мес, а на уровне «Always Free» – до $5.04.
- Расходы на хранилище: бесплатно. Мы получаем даром 30GB постоянного дискового пространства, поэтому выше мы и выбрали размер 10GB.
- Расходы на балансировку нагрузки: бесплатно, мы отключили HTTP-балансировку нагрузки, поскольку только на нее у нас ушло бы 18$/мес. Вместо этого запустим наши собственные HTTP-прокси на каждом узле и направим DNS на общедоступные IP.
- Сетевые расходы: бесплатно, функция egress остается бесплатной до тех пор, пока вы не выбираете 1GB в месяц. (далее каждый следующий гигабайт обходится в 8 центов)
Вот мы и настроили кластер Kubernetes из 3 узлов, он обошелся нам в ту же цену, что и единственная машина Digital Ocean.
Сверх настройки GKE также нужно настроить пару правил брандмауэра, чтобы на HTTP-порты наших узлов можно было достучаться из внешнего мира. Найдите в гамбургерном меню запись VPC Network, далее перейдите в Firewall Rules и добавьте правила для TCP-портов 80 и 443, с диапазоном IP-адресов 0.0.0.0/0.
Правила брандмауэра
Локальная настройка
Итак, мы подняли и запустили кластер, а теперь давайте его сконфигурируем. Установите инструмент
gcloud
, следуя инструкциям по адресу cloud.google.com/sdk/docs. Установив его, можно перейти к настройке, сделав так:gcloud auth login
Конечно же, вам еще придется установить docker, а затем привязать его к GCR, так, чтобы можно было отправлять контейнеры:
gcloud auth configure-docker
Также можно установить и настроить
kubectl
, следуя инструкциям, изложенным здесь. Упрощенно:
gcloud components install kubectl
gcloud config set project PROJECT_ID
gcloud config set compute/zone COMPUTE_ZONE
gcloud container clusters get-credentials CLUSTER_NAME
Кстати, просто сказка, что весь этот инструментарий работает в Windows, OSX или Linux. Как человек, иногда занимавшийся такими вещами под Windows, признаюсь, что это приятная неожиданность.
Сборка веб-приложения
Веб-приложение можете писать на любом языке программирования. Контейнер позволяет абстрагировать частности. Мы должны создать HTTP-приложение, слушающее порт. Я предпочитаю для таких целей язык Go, но для разнообразия попробуем crystal. Создаем файл
main.cr
:# crystal-www-example/main.cr
require "http/server"
Signal::INT.trap do
exit
end
server = HTTP::Server.new do |context|
context.response.content_type = "text/plain"
context.response.print "Hello world from crystal-www-example! The time is #{Time.now}"
end
server.bind_tcp("0.0.0.0", 8080)
puts "Listening on http://0.0.0.0:8080"
server.listen
Также нам понадобится Dockerfile:
# crystal-www-example/Dockerfile
FROM crystallang/crystal:0.26.1 as builder
COPY main.cr main.cr
RUN crystal build -o /bin/crystal-www-example main.cr --release
ENTRYPOINT [ "/bin/crystal-www-example" ]
Чтобы собрать и протестировать наше приложение, запустим:
docker build -t gcr.io/PROJECT_ID/crystal-www-example:latest .
docker run -p 8080:8080 gcr.io/PROJECT_ID/crystal-www-example:latest
А затем перейдем в браузере по адресу localhost:8080. Наладив этот механизм, сможем отправить наше приложение в GCR, запустив:
docker push gcr.io/PROJECT_ID/crystal-www-example:latest
Конфигурируем Kubernetes
Моя конфигурация Kubernetes находится здесь.
Для данного примера нам придется создать несколько файлов в формате yaml, где будут представлены наши различные сервисы, а затем запустить kubectl apply, чтобы сконфигурировать их в кластере. Конфигурация Kubernetes является описательной, и все эти yaml-файлы сообщают Kubernetes, какое состояние мы хотим получить. В широком смысле вот что мы собираемся сделать:
- Создать Deployment и Service для нашего веб-приложения crystal-www-example
- Создать Daemon Set (набор служб) и Config Map (карту конфигурации) для nginx
- Запускаем собственное приложение, чтобы синхронизировать IP узлов с Cloudflare для DNS
Конфигурация веб-приложения
Сначала давайте сконфигурируем наше веб-приложение: (убедитесь, что заменили
PROJECT_ID
на id вашего проекта)# kubernetes-config/crystal-www-example.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: crystal-www-example
labels:
app: crystal-www-example
spec:
replicas: 1
selector:
matchLabels:
app: crystal-www-example
template:
metadata:
labels:
app: crystal-www-example
spec:
containers:
- name: crystal-www-example
image: gcr.io/PROJECT_ID/crystal-www-example:latest
ports:
- containerPort: 8080
---
kind: Service
apiVersion: v1
metadata:
name: crystal-www-example
spec:
selector:
app: crystal-www-example
ports:
- protocol: TCP
port: 8080
targetPort: 8080
Так создается Deployment (развернутая конфигурация), согласно которой Kubernetes должен создать под с единственным контейнером (там будет работать наш docker-контейнер) и сервис, который мы применим для обнаружения сервисов в нашем кластере. Чтобы применить эту конфигурацию, запустите (из каталога
kubernetes-config
):kubectl apply -f
Можно это протестировать вот так:
kubectl get pod
# Вы должны увидеть нечто вроде:
# crystal-www-example-698bbb44c5-l9hj9 1/1 Running 0 5m
Также мы можем создать прокси-API для доступа:
kubectl proxy
А затем перейти: localhost:8001/api/v1/namespaces/default/services/crystal-www-example/proxy/
Конфигурация NGINX
Как правило, при работе с HTTP-сервисами в Kubernetes применяется входной контроллер. К сожалению, HTTP-балансировщик нагрузки от Google обходится слишком дорого, поэтому мы не станем его использовать, а задействуем собственный HTTP-прокси и сконфигурируем его вручную (звучит страшно, а на деле все очень просто).
Для этого воспользуемся Daemon Set и Config Map. Daemon Set – это приложение, работающее на каждом узле. Config Map – это, в принципе, небольшой файл, который мы можем монтировать в контейнере; в этом файле будет храниться конфигурация nginx.
Файл yaml выглядит примерно так:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- image: nginx:1.15.3-alpine
name: nginx
ports:
- name: http
containerPort: 80
hostPort: 80
volumeMounts:
- name: "config"
mountPath: "/etc/nginx"
volumes:
- name: config
configMap:
name: nginx-conf
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
data:
nginx.conf: |
worker_processes 1;
error_log /dev/stdout info;
events {
worker_connections 10;
}
http {
access_log /dev/stdout;
server {
listen 80;
location / {
proxy_pass http://crystal-www-example.default.svc.cluster.local:8080;
}
}
}
Вот как мы монтируем файл nginx.conf конфигурационной карты в контейнере nginx. Также мы задаем значения еще для двух полей:
hostNetwork: true
, чтобы можно было привязать хост порта и достучаться до nginx извне и dnsPolicy: ClusterFirstWithHostNet
, чтобы можно было обращаться к сервисам внутри кластера. Если этого не сделать, то у нас выйдет совершенно стандартная конфигурация.Примените эти выражения – и сможете выходить на nginx по общедоступным ip ваших узлов.
Вот как можно в этом убедиться:
kubectl get node -o yaml
# look for:
# - address: ...
# type: ExternalIP
Итак, теперь наше веб-приложение доступно из Интернета. Осталось придумать для приложения красивое название.
Подключение DNS
Требуется задать 3 A DNS-записи для узлов нашего кластера:
Записи в UI Cloudflare
Затем добавляем запись CNAME, чтобы указать на эти A-записи. (напр. www.example.com CNAME для kubernetes.example.com). Это можно сделать вручную, но лучше – автоматически, чтобы, если когда-нибудь нам придется масштабироваться или заменить узлы в DNS-записях, эта информация также обновлялась автоматически.
Думаю, этот пример также хорошо иллюстрирует, как можно делегировать Kubernetes часть вашей работы, а не пытаться побороть его. Kubernetes отлично понимает скрипты и обладает мощным API, а имеющиеся пробелы можно заполнить собственными компонентами, написать которые не так сложно. Для этого я сделал небольшое приложение на Go, доступное по этому адресу: kubernetes-cloudflare-sync.
Для начала создал информатор:
factory := informers.NewSharedInformerFactory(client, time.Minute)
lister := factory.Core().V1().Nodes().Lister()
informer := factory.Core().V1().Nodes().Informer()
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
resync()
},
UpdateFunc: func(oldObj, newObj interface{}) {
resync()
},
DeleteFunc: func(obj interface{}) {
resync()
},
})
informer.Run(stop)
Он будет вызывать мою функцию пересинхронизации при любом изменении узла. Затем я синхронизирую API при помощи библиотеки Cloudflare API, примерно так:
var ips []string
for _, node := range nodes {
for _, addr := range node.Status.Addresses {
if addr.Type == core_v1.NodeExternalIP {
ips = append(ips, addr.Address)
}
}
}
sort.Strings(ips)
for _, ip := range ips {
api.CreateDNSRecord(zoneID, cloudflare.DNSRecord{
Type: "A",
Name: options.DNSName,
Content: ip,
TTL: 120,
Proxied: false,
})
}
Затем, как и в случае с нашим веб-приложением, мы запускаем это приложение в Kubernetes в виде Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubernetes-cloudflare-sync
labels:
app: kubernetes-cloudflare-sync
spec:
replicas: 1
selector:
matchLabels:
app: kubernetes-cloudflare-sync
template:
metadata:
labels:
app: kubernetes-cloudflare-sync
spec:
serviceAccountName: kubernetes-cloudflare-sync
containers:
- name: kubernetes-cloudflare-sync
image: gcr.io/PROJECT_ID/kubernetes-cloudflare-sync
args:
- --dns-name=kubernetes.example.com
env:
- name: CF_API_KEY
valueFrom:
secretKeyRef:
name: cloudflare
key: api-key
- name: CF_API_EMAIL
valueFrom:
secretKeyRef:
name: cloudflare
key: email
Нам понадобится создать секрет Kubernetes, указав ключ
cloudflare api
и почтовый адрес:kubectl create secret generic cloudflare --from-literal=email='EMAIL' --from-literal=api-key='API_KEY'
Также нам понадобится создать служебный аккаунт (открывающий нашему Deployment доступ к Kubernetes API для извлечения узлов). Сначала запустите (в особенности касается GKE):
kubectl create clusterrolebinding cluster-admin-binding --clusterrole cluster-admin --user YOUR_EMAIL_ADDRESS_HERE
А затем примените:
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubernetes-cloudflare-sync
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kubernetes-cloudflare-sync
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-cloudflare-sync-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubernetes-cloudflare-sync
subjects:
- kind: ServiceAccount
name: kubernetes-cloudflare-sync
namespace: default
Работать с RBAC немного муторно, но, надеюсь, тут все понятно. Когда конфигурация готова, а наше приложение работает с Cloudflare, это приложение можно обновлять при каком угодно изменении любого из узлов.
Заключение
Kubernetes суждено стать флагманской технологией для управления крупными системами. Хотя, крупномасштабная эксплуатация Kubernetes связана с серьезными техническими вызовами, а сама технология Kubernetes еще не вполне устоялась, количество развернутых экземпляров Kubernetes достигло критической массы, и в ближайшие годы весь Kubernetes ожидают кардинальные усовершенствования.
Здесь я постарался доказать, что Kubernetes целесообразно использовать и при развертывании небольших систем: сегодня это уже стало и просто, и недорого. Если ранее вы этого не делали – самое время попробовать!