TrueNAS SCALE может использоваться как сервер приложений, поддерживая контейнейрную виртуализацию за счёт Docker’а. При этом за оркестровку контейнеризированных приложений отвечает Kubernetes. Большинство современных приложений представленных на Docker Hub’е — это веб приложения, а значит «общение» с ними должно быть организовано через 80-й (http) или 443-й (https) порт. Приложений много, а 80-й порт один — как быть? Можно одно приложение оставить на 80-м порту, а остальные раскидать по другим портам. Вариант жизнеспособный, если пользоваться закладками браузера, которые можно синхронизировать между устройствами одного пользователя, но не между разными пользователями. Другой вариант — установить Organizr, который позволит разместить на одной странице ссылки на все установленные приложения + виджеты. Решение любопытное, но по сути это некий навороченный вариант закладок и не более того. Третий вариант, и это классическое решение проблемы — использовать обратный прокси (Reverse Proxy) или в терминологии Kubernetes — контроллер входящих (Ingress Controller). Задача этого сервиса принимать все входящие соединения и далее перенаправлять их к другим сервисам в зависимости от запроса. Чаще всего разделение идёт по доменному имени, поскольку владение доменом позволяет также использовать и поддомены. Таким образом используя для каждого сервиса свой поддомен можно организовать удобную навигацию по имени сервиса — такой подход интуитивно понятен и легко запоминаем. Kubernetes поддерживает множество реализаций обратного прокси сервера, в том числе и Traefik (доки), который я выбрал только из-за упоминания в документации TrueCharts. По правде говоря я познакомился с большинством контейнезированных приложений именно благодаря TrueCharts и надеялся, что использовать эти приложения будет легко и просто совместно с другими приложениями из других источников, таких как DockerHub, например. Но на практике так не получиться, потому что команда TrueCharts адаптирует Helm Charts под интерфейс TrueNas SCALE тем самым упрощая установку для одних пользователей, и закрывая расширенные возможности для других. Например упомянутый выше Traefik от TrueCharts в качестве поставщика конфигурации поддерживает только Kubernetes и KubernetesCRD. Другими словами: конфигурация для Traefik должна быть определена в Chart’e, т.е. если я хочу разместить собственное приложение, то для него придётся создать Chart в собственном репозитории. С одной стороны это логично, поскольку TrueNas использует Kubernetes, а не Docker в чистом виде. Но с другой стороны — нет у меня ни желания ни времени на изучение структуры чартов и языков GO или Lua. Анализируя сложившееся противоречие возникает мысль: может быть я выбрал не подходящий дистрибутив..? Надо будет на досуге присмотреться к Ubuntu Server или ProxMox с LXC виртуализацией и CoreOS или Alpine Linux для поддержки Docker’а. Однако я отвлёкся.
Выходом в сложившейся ситуации будет использование официального образа Traefik с DockerHub — он не накладывает никаких ограничений и становится доступным файловый поставщик конфигурации. В терминалогии Traefik это т.н. динамическая конфигурация, получаемая от поставщиков. А есть ещё статическая конфигурация, которая определяет параметры приложения во время запуска. Эта конфигурация может быть определена в виде файла, а также аргументов командной строки и переменных окружения. И хорошо было бы сейчас начать реализацию с создания упомянутых выше файлов конфигурации, но начинать нужно с определения ресурсов. В моём случае есть 2 группы ресурсов: те, что должны быть доступными для всех, и те, что должны быть доступны только внутри сети для личного использования. В основном эти требования продиктованы безопасностью, т.к. любое не тестовое приложение с разделением прав доступа должно поддерживать аутентификацию и взаимодействие по защищённому каналу связи. При этом доступ к некоторому публичному ресурсу как внутри сети, так и снаружи должен осуществляться по одному и тому же доменному имени и порту с автоматическим перенаправлением на защищённый канал, при необходимости. И естественно, приватные приложения должны быть надёжно отделены от публичных дабы исключить возможность несанкционированного доступа. Как этого добиться? — за счёт использования разных портов для публичных и приватных приложений. Пусть внутри сети все приложения будут доступны на 80-м порту, поскольку это используемый по умолчанию браузером порт. А для доступа снаружи будет использоваться 8008 порт. Чтобы это заработало на маршрутизаторе нужно во первых в локальном DNS сервисе прописать адрес сервера приложений для используемого доменного имени, или нескольких имён. Во вторых открыть 80-й порт для входящих соединений и перенаправить пакеты на 8008 порт сервера приложений. Таким образом внешний запрос за счёт купленного доменного имени будет направляться на внешний адрес маршрутизатора на 80-й порт и далее перенаправляться на 8008 порт сервера приложений. А внутренний запрос за счёт локального DNS сервиса будет направляться напрямую на сервер приложений на тот порт, который был указан в запросе.
Теперь пришло время подключить Traefik. Поскольку конфигурация будет файлах, то для начала на сервере создам каталог /ssd/apps/traefik
. Далее помещу туда файл статической конфигурации traefik.yaml
:
entryPoints: http-internal: address: :80 http-external: address: :8008 providers: file: filename: /etc/traefik/dynamic-config.yaml watch: true # API and dashboard configuration api: insecure: true # for tests log: level: DEBUG # for tests
Как видно из содержимого я определил 2 точки входа на портах 80 и 8008, указал расположение файла для файлового поставщика конфигурации, включил информационную панель без аутентификации и логи на всякий случай. Отмечу сразу, что в не безопасном режиме информационная панель доступна на порту 8080. Теперь необходимо создать файл динамической конфигурации dynamic-config.yaml:
http: routers: wordpress: entryPoints: - "http-external" rule: "Host(`ogorodov.su`)" service: "wordpress-service" wordpress-internal: entryPoints: - "http-internal" rule: "Host(`ogorodov.su`)" service: "wordpress-service" traefik: entryPoints: - "http-internal" rule: "Host(`traefik.ogorodov.su`)" service: "traefik-service" services: wordpress-service: loadBalancer: servers: - url: "https://ogorodov.su:10000" traefik-service: loadBalancer: servers: - url: "https://ogorodov.su:8080"
Из объявления видно, что я объявил 2 сервиса и 3 маршрута, потому что к сервису wordpress-service
можно попасть по двум маршрутам: либо через точку входа http-external
, т.е. порт 8008, либо через http-internal
, т.е. порт 80. При этом доменное имя одно и то же.
Сервисы в свою очередь объявлены максимально просто: только url с реальным расположением приложения. Можно использовать как доменное имя, так и ip-адрес.
Что ж — дело за малым: запустить контейнер с Traefik:
- Application Name = traefik
- Image repository = traefik
- Provide access to the node network namespace for the workload = true
- Host Path Configuration:
- Host Path = /mnt/ssd/apps/traefik
- Mount Path = /etc/traefik
Всё очень просто, но есть «ложка дёгтя в этой бочке мёда»: чтобы иметь возможность работать с портами ниже 9000 в TrueNas приложение должно работать в режиме с прямым доступом к сетевому интерфейсу сервера (host network). Это в свою очередь делает невозможным использование внутренних сетей и DNS Kubernetes, т.е. я не могу обратиться к сервисам по имени <app name>-ix-chart.ix-<app name>
внутри Kubernetes, что влечёт за собой необходимость пробрасывать порты из контейнера наружу для каждого запущенного приложения, что конечно не добавляет удобства. Именно поэтому в файле динамической конфигурации url прописан в форме https://ogorodov.su:10000
, а не http://wordpress-ix-chart.ix-wordpress
. Да ещё и для контейнера проброшен порт с 80-го на 10000-й. Но лучше уж так, чем вообще никак.
Напоследок отмечу, что предложенный здесь вариант использования Traefik’а архитектурно ближе к Docker’у, чем Kubernetes. И он на мой взгляд значительно проще, чем вот это вот всё.