Об Azure

2018-01-21

Microsoft Azure. Это такое облако с виртуалочками и всем таким. Как Amazon Web Services (aka AWS), только Microsoft. Пришлось на этой неделе поиметь с ним дело.

Заглянем в словарь. Оказывается, «azure» — это «лазурь», цвет такой, оттенок голубого. И ещё сорт ячменя.

Azure Logo

Начинаются все облака, конечно, с IaaS, то есть с виртуалок. В Azure вроде всё хорошо. Виртуалки есть. Они работают. Их можно засунуть в виртуальную частную сеть. Им можно индивидуально выдать публичные IP адреса или навесить доменное имя.

Когда я попытался самолично создать виртуалку, я запутался в чудовищном количестве доступных конфигураций. Ужос случился потом, когда оказалось, что самая дешевая конфигурация с достаточным количеством ЦПУ и памяти имеет чудовищно медленный диск. Настолько медленный, что банальный apt install htop выполняется минуты четыре, тормозя именно на построении индекса доступных пакетов.

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

Для управления облачным хозяйством есть Портал. Выполненный в стиле Microsoft, или Visual Studio, или Windows 10. Как хотите. Суть в том, что всё квадратное, куча выползающих и наползающих со всех сторон панелек. Каждая панелька, когда появляется, секунд 5-10 крутит спинером, прежде чем отобразить то, что нужно. URLы нечитабельные и часто некопипастебельные.

Сущностей тонны. Каждую нужно нежно настраивать. Связи между сущностями никак не очевидны. Свойства сущностей случайным образом сгруппированы. Бывает, о ужас, идентификаторы и ключи одной сущности нужно копипастить в поля другой сущности. Руками. Копипастить.

Как интерфейс, призванный помочь в этом разобраться и грамотно настроить, портал Азуры — полное говно.

Есть ещё командная строка. Утилитка az. Написана на Python 3. Теоретически, она может всё, что может Портал. Но она может даже больше. Есть некоторые команды, которые, если повторить в Портале, потребуют пятнадцати минут кликов и копипастов, чтобы создать с десяток связанных сущностей. Узнать, какие команды, где и зачем, можно из туториалов. Коих, слава богу, довольно много.

Для тех, у кого есть PowerShell, есть модули для PowerShell. А PowerShell вроде как можно завезти даже в Linux.

Ansible тоже может Azure. Посредством питонячьего модуля. Но вот лезть в Азуру из Ансибля как-то не пришлось. Ибо у Docker Registry и Kubernetes есть свои более специфичные API, с которыми Ансибль вполне прекрасно работает. Модули же для работы с Azure меня напрягли тем, что они пытаются создать всю тонну сущностей, которая ведёт, допустим, к деплою файлика в Blob Storage. Это требует и дополнительных прав на создание этих сущностей, и нет гарантии, что дефолтные параметры этих сущностей будут теми, какие надо. Особенно, когда ты вообще не знаешь, какие там сущности нужны, и какие параметры стоит выставить. Слишком много магии в одной команде.

Azure Portal

Первая проблема с Azure. PostgreSQL. C MS SQL всё должно быть прекрасно. А вот managed MySQL и PostgreSQL живут в статусе «preview». Типа, всё может поломаться. И ломается.

Инстансы PostgreSQL живут сами по себе. Со своим публичным, на все интернеты, IP адресом и доменным именем. Их нельзя засунуть в виртуальную сеть, где живут ваши виртуалки. Интересно, сказывается ли это на стоимости трафика? Спасибо, есть файервол.

У нас так случилось, что PostgreSQL был доступен откуда угодно, но только не из виртуалок в том же датацентре. Оказалось, что виртуальная сеть этих виртуалок была сконфигурена на доступ к MS SQL, и PostgreSQL при этой конфигурации недоступен. Для всей виртуальной сети. Странно. Самостоятельно разрулить проблему мы бы не догадались, помогла поддержка.

PostgreSQL access

Немного забегая вперёд, подняли один сервис на обычной виртуальной машинке. Как дать доступ к сервису снаружи? Через HTTPS конечно же. Можно выдать виртуальной машинке публичный IP, и разбираться с сертификатами самостоятельно. Но ведь машинка может переехать. Или понадобится больше машинок, микросервисы ведь. По-хорошему, нужен какой-то reverse proxy с правильным и зафиксированным доменным именем, который бы прикрывал собой любую мешанину микросервисов, и занимался бы HTTPS.

В Азуре для этого вроде как имеется Application Gateway. Можно навесить на него свой домен, и скормить сертификат. Кстати, сертификаты Azure не выдаёт, и получить Let's Encrypt сертификат на домены служб Azure (тысячи их) тоже не выйдет, а не все эти домены свои сертификаты уже имеют.

Можно навесить правила, на какой сервис позади отправлять вот этот вот запрос. Можно делать это по пути в запросе. Например, пусть все запросы на «/service1/*» идут на наш первый сервис. При этом было бы логично, чтобы эта начальная часть пути, по которой мы выбираем бэкенд, удалялась из URL, чтобы сам сервис не зависел от того, по какому пути его вывешивают наружу. Но нет, даже такого простого и логичного URL rewriting Application Gateway не умеет. В результате, если у нас там Java, приходится накручивать правильный context path. И ведь не то чтобы он совсем не умел переписывать запросы, «X-Forwarded-For» ведь проставляет.

А ещё этот Application Gateway очень своеобразно проверяет жизнеспособность бэкенда. Существует лишь один единственный способ для этого. Gateway делает GET запрос на бэкенд. Хорошо, что path в этом запросе можно задать. Беда в том, что наш грешный сервис был сделан только для того, чтобы принимать POSTы. Пришлось в него добавить ещё один контроллер, чтобы отвечал на «GET /service1/health». Кажется, я избалован HAProxy, с его тучей способов проверки доступности бэкендов.

Application Gateway

Но тем не менее, основная задача немного другая. Хотим задеплоить фронтенд на React. И кучку микросервисов в виде бэкенда для него. Хотим делать это serverless, то есть без виртуальных машин, а контейнерами Docker.

Фронтенд. Single page application (aka SPA) на React — это же статичные файлы: HTML, JS, CSS, немного картинок. Их же можно засунуть в простейшее файлохранилище с доступом по HTTP или даже завернуть через CDN. Так? Нет, но об этом позже.

В Азуре есть CDN. Он может принимать HTTP или HTTPS запросы, запрашивать ответы в статических хранилищах, веб сервисах или вообще куда угодно, и распределённо кэшировать ответы в нужном количестве датацентров. Можно навесить свой домен и SSL сертификат. Нормальный CDN.

Для хранения файликов за CDN наиболее подходит Blob Storage. Аналог амазоновского S3. Всё просто. Заводите себе storage, получаете домен для него. Заводите там то, что называется container, это будет путь в URL. Заливаете файлы-блобы. Имена блобов могут содержать слэш «/», что эквивалентно созданию подкаталогов. Ну и надо правильно указать mime тип, что может быть проблемой, как минимум, если заливать файлы из Ansible.

Но погодите. Имя контейнера становится частью пути. А если мы хотим наши файлики положить в корень? Для этого есть секретный контейнер с именем «$root». Окей. Но, внезапно, в контейнере «$root» нельзя создавать блобы со «/» в имени, то есть нельзя создавать подкаталоги. Причём официально про это сказано ничего. Ошибка, которая при этом возникает, совершенно невнятная, и похожа на ошибку в коде клиента, заливающего блобы. Может, кстати, так оно и есть. Ребята на Stack Overflow, обнаружившие такую особенность контейнера «$root» эмпирически, не углублялись в исследования причин.

Ладно. Залили index.html в «$root». Залили всякие CSS и JS в «static». Получится запустить SPA? Нет. Потому что «http\://какой-то-домен.blob.core.windows.net/» не открывает index.html. Нет такого понятия, как directory index, ни в Blob Storage, ни в CDN. А в S3 — есть.

Но directory index тоже недостаточно. В SPA по-хорошему нужно, чтобы все запросы, которые не идут на CSS и JS, выдавали index.html. Чтобы можно было бы спокойно манипулировать адресной строкой, и этот адрес всегда бы приводил на index.html.

Поэтому все пути по хостингу реактовых приложений на Azure ведут на Web Apps. А Web Apps — это те же контейнеры, но с IIS, PHP или Node.js внутри. Ну и зачем мне контейнер с Node, если достаточно простого Nginx? Круг замкнулся. Нужны докеровые контейнеры.

Blob Storage

Шаг первый прост. Нужно собрать образы и положить их в Docker Registry. В Азуре есть такой, называется Container Registry. Тут всё нормально и без сюрпризов. Создаём реестр. Получаем домен типа «чего-то-там.azurecr.io», и логин-пароль к нему. Делаем docker login чего-то-там.azurecr.io. Навешиваем на наши локальные образы теги вида чего-то-там.azurecr.io/имя-образа. Делаем docker push. Всё прекрасно, и можно не приплетать инструменты Azure, а пользоваться стандартными докеровыми командами. В том числе и из Ansible.

А теперь эти образы нужно развернуть. И тут, внезапно, есть три способа.

Container Instances. Фича из категории «preview». Странный, ни с чем не совместимый способ. Просто указываем образ и запускаем контейнер с публичным IP адресом. Учитывая, что у меня микросервисы, совсем не хотелось их все выставлять голой попой в интернеты. Наоборот, хотелось их все засунуть в приватную локалочку, и уже оттуда открывать только то, что нужно. Ну как это обычно делается с Docker Compose. Поэтому Container Instances были сразу же отброшены за неспортивность.

Azure Container Service (ACS). Не путать с AKS. Штука, помеченная в документации как «old version». Создаёт нормальный кластер управления контейнерами. В теории, либо DC/OS, либо Kubernetes, либо Swarm. На практике, в документации найти можно только Kubernetes. Только с этим сервисом контейнеров напрямую может работать Ansible.

Azure Container Service (AKS). Не путать с ACS. Да, блин, два сервиса с совершенно одинаковыми названиями. Этот помечен как «preview». Тут только Kubernetes. На самом деле, Kubernetes API, которое тут есть, вполне достаточно, чтобы деплоить контейнеры.

Оба, и ACS, и AKS, как-то странно вписываются в инфраструктуру Azure. Они оба хотят для своего исключительного использования отдельную Resource Group. Resource Group — это и namespace, и точки приложения прав доступа, полагаю, и биллинговая единица в Азуре. Все ресурсы принадлежат определённой группе. В рамках проекта у меня не было прав на создание ресурсных групп. Это сильно замедлило эксперименты с развёртыванием контейнеров.

Обоим сервисам при создании нужно скопипастить credentials штуки, которую в документации называют Service Principal. По сути, это OAuth параметры доступа к аккаунту для других приложений. Понятно, когда таким другим приложением является Ansible, запускаемый у меня на ноутбуке. Но непонятно, почему таким приложением является другая половинка Azure облака в том же датацентре.

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

Но погодите. Это же managed сервис. Какие виртуалки? И за них полностью платить? Ну и чем это принципиально отличается от того, что я на этих виртуалках самостоятельно запущу тот же Kubernetes?

Получилось создать AKS. Вот только подключение к Kubernetes API отваливалось на стадии TLS handshake. Рабочая версия, почему такое говно, — видимо, я снова выбрал слишком дешевую и медленную виртуалку, которая не может переварить handshake за две минуты дефолтного TCP таймаута.

Kubernetes in Azure

Ну и стоит ли продолжать с Azure? Или на всё плюнуть и уехать на AWS? Пока ещё ничего толком не развёрнуто.

На AWS бывают откровенно сырые фичи? Как там с Докером?