На Хабре уже не раз писали про бесплатные вычислительные ресурсы (ARM, 4 CPU, 24ГБ), которые можно развернуть в Oracle Cloud. После регистрации вы получаете $300 и 30 дней триального аккаунта, когда для развёртывания доступны все виды ресурсов. Когда эти 30 дней заканчиваются, аккаунт получает статус Always Free, и количество доступных ресурсов значительно урезается. Чтобы снова получить доступ к ним ко всем, можно сделать upgrade своего аккаунта до Pay-as-You-Go. Это односторонняя операция, переход обратно на Always Free невозможен, и после такого перехода нужно аккуратно следить и и контролировать траты. Тем не менее, Pay-as-You-Go аккаунты также могут бесплатно использовать Always Free ресурсы.
Oracle Cloud предоставляет возможность managed развёртывания K8s кластера (в документации он называется OKE – Oracle Container Engine for Kubernetes), однако он недоступен для Always Free аккаунтов (лимит установлен в 0, все упомянутые лимиты в данной статье актуальны на январь 2022 г.).
Чтобы обойти это ограничение, используем подход полностью ручного выделения облачных ресурсов (сети, виртуальных машин) и установки необходимых компонентов. На выходе получим кластер с четырьмя узлами (один control-plane и три worker'а) с установленным K8s'ом, балансировщиком нагрузки, публичным IP адресом и (бонусом) бесплатным доменом.
TL;DR: воспроизводимые Terraform скрипты (и readme по их использованию) опубликованы на github'е.
Вычислительные ресурсы
В нашем кластере будем использовать все доступные бесплатные ARM ресурсы. Используем по одному CPU для каждого узла. Узел для развёртывания K8s control-plane'а получит 3 ГБ оперативной памяти, три worker'а получат по 7 ГБ (что в сумме даёт 24 ГБ использованной памяти). Также каждая ARM VM'ка получает по 1 Gbps сетевой пропускной способности.
Помимо узлов на ARM, в Always Free аккаунте можно создать две виртуальные машины на AMD с 1 CPU и 1 ГБ RAM. Увы, это не значит, что можно выделить 4 ARM узла и 2 AMD узла, поскольку для Always Free аккаунтов доступно 200 ГБ дискового пространства, при этом минимальный блок для выделения – 47 ГБ.
При установке на узлах K8s бросает ошибку, если доступно менее двух CPU. Однако наша цель состоит в развёртывании максимального количества узлов из доступных ресурсов (возможно, ценой готовности кластера к использованию на prod'е), поэтому при собственно установке K8s агентов на узлах мы отключим эту ошибку.
Out of host capacity
Самая частая проблема, которая возникает при выделении ARM ресурсов, – это Out of host capacity.
Эта ошибка появляется из-за ограниченности вычислительных ARM ресурсов, и к сожалению, с ней не так просто бороться. Oracle заявляет, что новые ресурсы развёртываются автоматически. В некоторых случаях помогало переключиться в другой availability domain, если он доступен в выбранном регионе (Always Free аккаунты могут выделять ресурсы только в домашнем регионе, список регионов и доступных availability domain'ов можно увидеть здесь). Также получилось заметить, что если в одном регионе есть два аккаунта, но один из них Pay-as-You-Go, а другой Always Free, то первый получает больший приоритет, и у него получается выделить ARM VM'ки. Второму аккаунту пришлось ждать несколько дней, прежде чем ресурсы стали доступны.
Архитектура сети
Чтобы узлы получили возможность выхода в интернет, нам нужно развернуть для них Virtual Cloud Network (VCN). Внутри VCN можно развёртывать публичные и приватные подсети. Чтобы открыть входящий и исходящий доступ в интернет для узлов в публичной подсети, нужно: а) наличие Internet gateway'а в VCN, и б) каждый узел должен иметь публичный IP. Чтобы открыть исходящий доступ в интернет для узлов в приватной подсети, необходимо наличие NAT gateway'а (входящий доступ по умолчанию недоступен).
Таким образом, желаемая архитектура сети будет следующей: VCN с публичной и приватной подсетями, вычислительные ресурсы назначаются в приватную подсети. К сожалению, данная архитектура невозможна для Always Free аккаунтов (на январь 2022 г.). Дело в том, что Oracle установил нулевой лимит для использования NAT gateway'ев. Отсутствие NAT'а в сети приводит к тому, что ресурсы в приватных подсетях не имеют доступа в интернет.
По этой причине приходится использовать такую сетевую архитектуру: VCN с одной публичной подсетью для всех узлов, и каждый из них получает эфемерный публичный IP. Благодаря этому все узлы становятся доступными из интернета. Тем не менее, наша цель заключается в создании единой точки, из которой были бы доступны все приложения, развёрнутые в K8s кластере (в виде подов). Для достижения этого используем load balancer.
Балансировка нагрузки
В Oracle Cloud есть возможность создавать load balancer'ы двух видов. Первый работает на седьмом уровне OSI модели (и по факту является полноценным HTTP reverse proxy). Однако для его создания нам нужно выбрать shape, который определяет его пропускную способность. Для Always Free аккаунтов доступен только один load balancer на 10 Мбит/с.
Другой вид балансировщиков нагрузки называется Network Load Balancer. Этот тип работает на уровнях 3 и 4 OSI, и для балансировки в нём используются пары IP-порт. Но главная особенность таких балансировщиков заключается в том, что для них не специфицируется пропускная способность, и именно поэтому мы и будем его использовать в нашем кластере. Поместим load balancer в созданную публичную подсеть и назначаем ему зарезервированный публичный IP адрес.
Настройка load balancer'а заключается в создании следующих ресурсов:
Listener'ов, которые конфигурируют открытые для подключения порты;
Backend set'ов, которые представляют собой группы узлов, между которыми должна распределяться нагрузка (которая приходит на определённый порт);
Backend'ов для каждого backend set'а, которые являются ссылками на узлы.
В нашем кластере созданный Network Load Balancer будет работать на следующих TCP портах:
80 – для HTTP траффика, который будет распределяться между worker'ами.
443 – для HTTPS траффика для тех же worker узлов.
6443 – для удалённого управления K8s кластером с помощью kubectl (например, для развёртывания новых приложений). Траффик с этого порта направляется на порт 6443 leader'а (порт для управления K8s control-plane'ом).
Наконец, сконфигурируем security правила для нашей публичной подсети. Нам необходимо открыть доступ в интернет для узлов (egress правило с маской 0.0.0.0/0), а также доступ из интернета для SSH, HTTP, HTTPS и kubectl траффика (ingress правила с маской 0.0.0.0/0).
Развёртывание K8s
Собственно развёртывание K8s кластера будем выполнять в полностью автоматическом режиме с помощью утилиты kubeadm.
В первую очередь, необходимо поднять control-plane. Чтобы поддержать автоматическое обнаружение worker'ами control-plane'а в кластере работает приватный DNS. Leader узел доступен внутри кластера по имени leader.public.vcn.oraclevcn.com. Помимо этого, перед инициализацией control-plane'а, мы генерируем discovery token, который будет использоваться и для control-plane'а (при вызове kubeadm init), и для worker'ов (при вызове kubeadm join).
Для того, чтобы control-plane имел возможность управлять агентами K8s'а на каждом узле (kubelet'ами) необходимо открыть TCP порт 10250. Помимо этого, в кластере используется flannel в качестве оверлейной сети, и для его работы необходимо открыть UDP порт 8472 (оба – ingress правила с маской 10.0.0.0/16).
После развёртывания базовой K8s инфраструктуры, мы добавим несколько полезных приложений. Во-первых, ingress-controller для роутинга по хостам и путям – в нашем кластере будем использовать ingress-nginx. Для него создадим K8s Service типа NodePort на портах 30080 и 30443 для HTTP и HTTPS траффика соответственно. Ingress controller является последним штрихом в архитектуре сети нашего кластера.
Далее, развернём приложение dashboard'а, и сделаем его доступным с помощью ingress controller'а. Dashboard будет доступен по адресу https://{cluster-public-ip}/dashboard.
Затем для поддержки HTTPS сертификатов установим cert-manager. Когда его создание завершено, создадим ресурс ClusterIssuer для поддержки LetsEncrypt. Здесь есть небольшая особенность, связанная с тем, что cert-manager'у необходимо время для завершения инициализации, и нет такого K8s API, с помощью которого мы бы об этом могли узнать. А создание ClusterIssuer'а завершается с загадочной ошибкой, если cert-manager не закончил свою инициализацию. Чтобы побороть эту проблему, будем пытаться создать ClusterIssuer до тех пор, пока это не удастся. Как правило, на это уходит около минуты.
Созданный ClusterIssuer работает в связке с ingress-controller'ом, но чтобы настоящий HTTPS сертификат был выпущен, необходим последний штрих.
Бонус: бесплатный публичный домен второго уровня
У регистратора Freenom мы можем бесплатно на год получить домен второго уровня (в зонах .tk. .ml, .ga, .cf, .gq). По истечению этого срока мы можем бесплатно же продлить его действие.
Когда домен зарегистрирован, мы можем его сконфигурировать, чтобы он указывал на зарезервированный ранее публичный IP load balancer'а. Для этого необходимо открыть страницу настройки DNS для купленного домена (Services - My Domains - Manage Domain - Manage Freenom DNS).
На скриншоте можно увидеть пример настройки: две записи (example.tk и cluster.example.tk) настроены на один и тот же публичный IP (pu.bl.ic.ip). Идея заключается в том, чтобы приложения, относящиеся к самому кластеру, были доступны под доменом cluster.example.tk, а все остальные приложения были доступны под доменом example.tk.
Когда у нас есть полноценный публичный домен, то мы можем открыть их в интернет с помощью K8s ресурса Ingress. Вот так, например, можно открыть dashboard:
Бесплатный сыр только в мышеловке, поэтому когда мы "покупаем" бесплатный домен, то гарантия, конечно же, не предоставляется. В моём случае я зарегистрировал домен в зоне .ga, и был неприятно удивлён, когда обнаружил, что мой домен (а также nameserver'ы зоны .ga) не резолвится из некоторых стран (конкретно, из Новой Зеландии, Сингапура, западного побережья США). Я написал в поддержку зоны .ga, и к счастью, через пару дней проблема разрешилась. Ответа на моё письмо я, естественно, не получил.
Такой проблемы с доменом в другой зоне не было.
a.ns.ga – такого быть НЕ должно
Именно так, пройдя через тернии, получилось развернуть ARM узлы, объединить их в сеть, установить на них K8s кластер и открыть к нему доступ через load balancer, – и всё это не выходя за пределы Always Free подписки.