Когда дело доходит до разработки сайтов, использующих поддомены, это может быть сложной или скучной задачей, поскольку localhost не является полным доменным именем. Вы не можете просто перейти, к примеру.localhost. Вы либо постоянно редактируете /etc/hosts, либо настраиваете некоторые dnsmasq в своей локальной сети.
До сих пор наиболее часто используемые мной решения были http://lvh.me (на данный момент не работает) - бесплатный сервис, который разрешает себя вместе со всеми поддоменами на localhost, и похожий на него сервис http://nip.io который позволяет кодировать ip-адрес для преобразования в доменное имя 192-168-1-250.nip.io, и, таким образом, позволяет генерировать сертификат letsencrypt для указанного домена.
Это было рабочим решением в течении половины десятилетия, но по мере того, как все больше и больше моих клиентов переходили к обзору pull request (PR) и параллельно выбирали docker или kubernetes в качестве платформы, я все больше и больше ограничивался классическими решениями выше.
То, что я хотел в прошлом году - это воспроизводимый сценарий для реализации собственной локальной среды разработки, которую я могу легко воспроизвести на удаленном сервере для реализации сценариев проверки PR, поддержки некоторых разумных привилегий, таких как валидные SSL сертификаты, некоторые возможности устранения неполадок и так далее.
Давайте рассмотрим подход, который я сейчас использую для клиентов, использующих докеризованные приложения (локальные или swarm)
Выбор компонентов для решения
Компоненты
а) docker- классический или в режиме роя. (На моем рабочем ноутбуке у меня есть докер в режиме роя, это не мешает ему также использоваться в качестве автономной среды докера)
б) traefik2 в виде официального образа докера. Traefik-это пограничный маршрутизатор с открытым исходным кодом, который делает публикацию ваших услуг веселой и простой. Он получает запросы от имени вашей системы и выясняет, какие компоненты отвечают за их обработку. Особый интерес для меня представляют два поддерживаемых бэкенда - docker и kubernetes.
в) Поддомен разработки. Для хорошо работающего решения нам нужно выделить какой-то поддомен для разработки, например: `*.lvh.voronenko.net` - указывающий на локальный хост, или `*.preview.project.net` - какой-то подстановочный домен, указывающий на ваш промежуточный сервер.
d) Wildcard сертификат от letsencrypt для организации валидных сертификатов. Хотя на удаленной виртуальной машине traefik может позаботиться о получении валидного сертификата для каждого полного доменного имени для вас, если у вас есть возможность предварительно создать сертификат wildcard - почему бы и нет?
Настроенный домен с указанием на локальный хост, как уже упоминалось `*.lvh.voronenko.net - ... ты должен сам позаботиться об этом.
Wildcard-сертификат — сертификат открытого ключа, который может использоваться с несколькими подобластями домена.
Для генерации сертификатов letsencrypt мой текущий инструмент выбора - это acme.sh - мощный и простой в использовании клиент протокола ACME, написанный исключительно на языке оболочки (оболочка Unix), совместимый с оболочками bash, dash и sh. Помогает управлять установкой, продлением, отзывом SSL-сертификатов. https://github.com/acmesh-official/acme.sh
acme.sh --issue --dns dns_gd -d lvh.voronenko.net -d "*.lvh.voronenko.net"
Обратите внимание, что инструмент также заботится о продлении сертификата, когда это необходимо.
Установка сертификатов в нужную папку также проста, как выполнение сценария оболочки
~/.acme.sh/acme.sh --install-cert -d "${DOMAIN_NAME}" \
--cert-file $TARGET/cert.pem \
--key-file $TARGET/privkey.pem \
--fullchain-file $TARGET/fullchain.pem
В худшем случае (если вы не проинструктировали acme.sh с командой, что делать при продлении сертификата) - вам нужно будет позаботиться о копировании нового набора сертификатов один раз в 93 дня
e) Какой нибудь инструмент, которым вы можете проверить, что происходит с docker. Есть много хороших консольных инструментов, но если вам нравится вариант с веб-интерфейсом - portainer, безусловно, один из лучших.
Теперь, когда мы обсудили компоненты, давайте объединим их вместе:
Локальная настройка - настройка traefik для локальной разработки
Нам нужны:
папка "traefik_certs", в которой мы будем хранить предварительно созданный wildcard сертификат (у вас может быть более одного), также здесь traefik будет хранить информацию о собственных сертификатах.
папка "traefik_conf", в которой мы будем хранить части конфигурации traefik, которую мы хотим исключить из docker-compose.
По умолчанию "certificates.toml" сообщает traefik, что у нас есть один предварительно созданный сертификат, который можно найти по указанному пути в папке certs.
[[tls.certificates]] #first certificate
certFile = "/certs/fullchain.pem"
keyFile = "/certs/privkey.pem"
#[[tls.certificates]] #second certificate
# certFile = "/path/to/other.cert"
# keyFile = "/path/to/other.key"
Управление через `Makefile" поможет вам создать общую общедоступную сеть для docker, которая будет использоваться traefik для поиска открытых служб и процедур повышения/понижения
create-traefik-network-once:
docker network create traefik-public
up:
docker-compose up -d
down:
docker-compose down
и сердце конструкции: основной докер файл с описанием компоновки для traefik, чтобы сделать историю короче, я прокомментирую наиболее важные части конфигурации
version: '3.4'
services:
traefik:
image: traefik:v2.1.4
# на моем ноутбуке traefik работает на портах http https по умолчанию
# это позволяет работать с URL-адресами, таким как https://app.lvh.voronenko.net/
ports:
- 80:80
- 443:443
# настройка может быть легко преобразована в развертывание роя
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
preferences:
- spread: node.id
labels: []
# /certs и /conf - это выделенные известные каталоги в
# официальном traefik образе.
# для того, чтобы позволить traefik обращаться бэкэнду docker, мы
# сопоставляем внутри сокеты docker.
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik_certs:/certs
- ./traefik_conf:/conf
restart: always
# если traefik должен обслуживать реальный кластер роя, следует указать `peram`
# --docker.swarmmode
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.watch=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.directory=/conf/"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.mongo.address=:27017"
- "--entrypoints.postgres.address=:5432"
- "--log.level=DEBUG"
- "--accessLog"
- "--api"
- "--metrics"
- "--metrics.prometheus"
- "--providers.docker.network=traefik-public"
networks:
- default
- traefik-public
# traefik конфигурируется с помощью метол (labels)
# откройте панель мониторинга traefik по адресу https://traefik.lvh.voronenko.net
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.lvh.voronenko.net`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=letsencryptresolver"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.tls=true"
# пользовательский интерфейс управления docker, который будет доступен по адресу
# https://docker.lvh.voronenko.net
portainer:
image: portainer/portainer
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`docker.lvh.voronenko.net`)"
- "traefik.http.routers.portainer.entrypoints=web"
- "traefik.http.routers.portainer-secure.rule=Host(`docker.lvh.voronenko.net`)"
- "traefik.http.routers.portainer-secure.entrypoints=websecure"
- "traefik.http.routers.portainer-secure.tls=true"
volumes:
portainer_data:
networks:
traefik-public:
external: true
Теперь, если вы запустите настройку, т.е. `docker-compose up -d`, вы получите:
a) traefikUI по адресу https://traefik.lvh.voronenko.net/ - не так много параметров доступных изменения, но хороший вид с высоты птичьего полета на обнаруженные в настоящее время сервисы. Обратите внимание, что эта страница отображается с валиным сертификатом, который вы настроили.
b) PortainerUI по адресу https://docker.lvh.voronenko.net/ - предоставляет подробные внутренние параметры в docker, работающем на вашей машине, контейнерах, службах и т.д.
Обратите внимание, что traefik docker-compose (контейнер) является полностью независимым компонентом вашей системы без прямого отношения к какому-либо проекту, над которым вы в настоящее время работаете. Это означает, что он может постоянно работать на вашем хосте, как и любой другой веб-сервер.
В тот момент, когда вам нужно предоставить какой-либо проект docker, над которым вы в настоящее время работаете, traefik, вам понадобится
а) добавить сервис, доступный за пределами сети `traefik-public`.
б) указать правила обнаружения, например полное доменное имя для службы - "Host(`whoami.lvh.voronenko.net`)"
в) добавить traefik параметры для любой дополнительной конфигурации, такой как перенаправление, авторизация/защита паролем и т.д.
Как только вы запустите такой docker-compose в папке проекта, он сразу же откроется с помощью traefik. Более того - все ваши товарищи по команде, работающие над проектом, имеют точно такую же среду и опыт взаимодействия с ней, но на своих собственных ноутбуках.
Пример настройки контейнера whoami
whoami:
image: "containous/whoami"
container_name: "simple-service"
networks:
- app
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.lvh.voronenko.net`)"
- "traefik.http.routers.whoami.entrypoints=web"
# - "traefik.http.middlewares.traefik-auth.basicauth.users=USER:PASSWORD"
# - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
# - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
- "traefik.http.routers.traefik-secure.entrypoints=websecure"
- "traefik.http.routers.traefik-secure.rule=Host(`whoami.lvh.voronenko.net`)"
# - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
- "traefik.http.routers.traefik-secure.tls=true"
networks:
traefik-public:
external: true
Ваши сервисы выставляются на выделенные имена, обслуживаемые валидными сертификатами - почти идентичной копией вашей производственной среды.
Установка на общедоступном сервере
Настройка экземпляра traefik для удаленной разработки (для развертывания ветки и т.д.) выполняется аналогичным образом, но вам нужно будет лучше защитить свою интеллектуальную собственность (например, вы не хотите, чтобы кто-либо в Интернете просматривал развертывание вашей ветки), а также защитить панели мониторинга portainer и traefik с учетными данными или даже полностью удалить их из общего доступа.
Traefik имеет несколько промежуточных программ на выбор https://docs.traefik.io/middlewares/overview/
Наиболее разумными промежуточными программами для рассмотрения были бы
Basic Аутентификация (логин и пароль) https://docs.traefik.io/middlewares/basicauth/
Digest Аутентификация (усиленная Basic ) https://docs.traefik.io/middlewares/digestauth/
Список белых IP https://docs.traefik.io/middlewares/ipwhitelist/
Ограничение количества запросов https://docs.traefik.io/middlewares/ratelimit/
Также для общедоступного сервера traefik позаботится о создании валидных сертификатов, если вы не предоставили предварительно созданный wildcard сертификат.
Кроме того, изменения в настройке `local_server` приведут к активации поставщика сертификатов letsencrypt.
Мы вводим новую сопоставленную папку letsencrypt для хранения автоматически полученных сертификатов.
дополнение в docker-compose.yml
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik_certs:/certs
- ./traefik_conf:/conf
- ./letsencrypt:/letsencrypt
и мы дополнительно должны активировать провайдера letsencrypt acme в traefik
labels:
- "--certificatesResolvers.letsencrypt.acme.email=<LETSENCRYPT_MAIL_ADDRESS>"
- "--certificatesResolvers.letsencrypt.acme.tlsChallenge=true"
- "--certificatesResolvers.letsencrypt.acme.httpChallenge=true"
- "--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
Тем не менее, я рекомендую, по крайней мере, для хоста, служащего в качестве предварительного просмотра развертывания ветви, предварительно создать wildcard сертификат.
Совершенно бесполезно генерировать новый сертификат для каждой новой развернутой ветви (branchname.preview.voronenko.net)
Для служб, предоставляемых через traefik, требующих автоматического сертификата от letsencrypt, вам нужно будет указать traefik использовать letsencrypt для этой службы.
labels:
- traefik.http.routers.whoami.tls.certresolver=letsencrypt
Полный пример для выставленного сервиса:
whoami:
image: "containous/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.preview.voronenko.net`)"
- "traefik.http.routers.whoami.entrypoints=web"
# - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
# - "traefik.http.middlewares.traefik-auth.basicauth.users=USER:PASSWORD"
# - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
# - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
- "traefik.http.routers.traefik-secure.entrypoints=websecure"
- "traefik.http.routers.traefik-secure.rule=Host(`whoami.preview.voronenko.net`)"
- "traefik.http.routers.traefik-secure.tls=true"
- traefik.http.middlewares.whoami.compress=true
- traefik.http.routers.whoami.tls.certresolver=letsencrypt
networks:
traefik-public
Резюме
При описанном подходе вы можете обеспечить ненавязчивую локальную разработку с помощью traefik2, docker и letsencrypt индивидуально, а также для всех ваших товарищей по команде. Владельцы стартапов могут применять "фирменную" среду разработки, например "app.lvh.mystartup.domain".
Решение универсально и хорошо работает с несколькими проектами, включая автономные инструменты, распространяемые в виде контейнера docker.
Вы можете легко расширить подход к общедоступному серверу, реализовав "сервер предварительного просмотра" для тех же компонентов. Traefik и docker позволяют вам также вводить предварительные просмотры PR в разумные сроки (в течение дня).
Упомянутые в статье примеры можно попробовать по адресу https://github.com/Voronenko/traefik2-compose-template
`local_server`- это пример среды разработки на вашем локальном хосте, т. е. https://someapp.lvh.voronenko.net/
`public_server` - это пример среды, которую можно развернуть на общедоступном сервере.
От переводчика:
Переводил через translate c правками для сохранения сути статьи. Буду рад разумной критике и правкам по стилистике.
Благодарю за внимание.