Pull to refresh

Comments 28

А SSL от cloudflare нельзя использовать?

И ещё, не пробовали ли просто прописать домены в /etc/hosts? И тем же certbot'ом выпустить локальные сертификаты (по моему они локально один раз и на всегда ставятся)? (Я по работе как раз этим сценарием пользуюсь)

А SSL от cloudflare нельзя использовать?

В теории, наверное, можно, но не делал так никогда. Если у вас есть опыт и сценарий автоматизации этого - поделитесь?

В hosts прописывать желание пропало после второго раза. Ровно как и держать локальный резольвер типа dnsmasq на зону. Ты же не один работаешь, а всех в командах учить этому, да на зоопарке систем - совсем печаль-тоска. Да, забыл уточнить в статье - ttl у dns записей, что указывают на локалхост - одни сутки. Так что если моргнет сеть на пару часов, и ttl не игнорится локальным кэшем - проблем не должно быть.

Для совсем локальных историй есть mkcert (ссылка на него есть в статье), но это опять-таки телодвижения :)

Чтобы получить сертификаты Let's Encrypt для локальных сервисов, надо как-то сделать так, чтобы certbot мог подтвердить, что вы владелец домена. Обычно для этого нужно открыть сервер, где запущен certbot в интернет — это вызывает сложности при локальной разработке. Ну и плюс запихивание нужных доменов в hosts тоже боль. Особенно, когда ты работаешь не один, а командой. У нас для этого есть специальная утилита и это очень неудобно. Так что я попробую внедрить решение подобное описанному в статье.

SSL от клауда использовать тоже, кажется, не получится. В этом случае он выдаёт сертификат своему реверс-прокси-серверу, который никак не может проксировать трафик на локалхост.

Прошу прощения, но что-то я с одного прочтения не уловил фишку. Чем эта штука (реверс прокси) полезна как таковая - понятно. Но "для остальных" в чем прикол? Поднимать собственные сервисы на чужом (вашем) домене?

Во-первых — это домен. Он более привлекателен (как мне кажется), и приобретен не у российского регистратора (что важно). 

Вы говорите, что имя вашего домена indocker.app привлекательно для меня, чтобы я на нем свои сервисы поднимал? Или я вообще все неправильно понял?

Также вы пишете о работе в команде. Можете пояснить кейс? Ведь если я хочу, чтобы некто мог подключиться к моему сервису, он должен по доменному имени на мою машину ломануться.

Есть много образов (к примеру, nginxproxy/acme-companion для перевыпуска сертификатов, jwilder/nginx-proxy для перегенерации nginx reverse proxy конфигов при запуске нового контейнера). Ваш образ дает нечто похожее, но на базе Traefik, с более удобной интеграцией с Docker (обработка появления новых контейнеров)?

Просто если он всего лишь избавляет меня от регистрации собственного домена ценой подвязки всех моих сервисов на ваш - это странно, особенно при групповой работе. Условный indocker.in на 10 лет стоит меньше $50. А "временные" домены так и вообще меньше $2 в год есть.

Речь только о локальном окружении, повторюсь. Вот есть у вас, к примеру, сервис (и не редко не один), что состоит из фронтенда и нескольких бэкэндов, и все они разрабатываются с использованием докера. Чтоб с ними взаимодействовать локально вам, скорее всего надо будет пробросить их порты в хост (обращаясь к ним 127.0 0.1:8080, 127.0.0.1:8081 и так далее). И вот вопрос - что для вас удобнее - держать в голове что и на каком порту крутится, или использовать "человеческие" доменные имена вида api.indocker.app, web.indocker.app и тому подобные? Да чтоб еще и tls сертификат был из коробки для них, валидный всюду? Вот статья как раз про это :) Ничего никуда не привязывается, так как работает исключительно на вашей машине, и работает везде, где есть возможность запустить демон докера; под окнами, линуксом и маком, под amd и arm. Так стало понятнее? Извиняюсь, что не удалось это сразу отразить в статье. Видимо, "глаз уже замылился" несколько

Вы верно говорите, описиваемые вами "плюшки" хороши. Я просто не могу полноценный кейс для себя представить. Допустим, я лезу на порты 8080 и 8081 из вашего примера. Они явно не SSL, т.е. преимущество с сертифкатом из коробки здесь не скажется. Оно есть, но не проявится.

Если я правильно понимаю, преимущества в полной мере проявятся, если если я обращаюсь к другому контейнеру (условный бэкенд) через REST API. Причем делаю это не через API gateway, а как бы "напрямую", от контейнера к контейнеру. При этом, хоть и предполагается взаимодействие исключительно внутри одной машины (ноды), трафик между контейнерами зачем-то шифруется. И вот тут мы подменяем оригинальное "контейнер-контейнер" взаимодествие, заставляя их работать через реверс прокси, чтобы расшифровать на заранее подготовленном сертификате и от реверс прокси к финальному контейнеру таки пустить трафик расшифрованным.

С портами проблему тоже не понимаю. Я же не делаю expose на IP самой машины с докером, зачем разные порты? В моих мелких pet-поделках я просто создавал сетку (docker network create). Т.е. поднимается несколько контейнеров с именами app, back и т.п., каждому автоматически назначается свой IP из этой сетки, а порты у всех могут быть одинаковые (хоть 80). И обращаться друг к другу они могут по имени контейнера благодаря "встроенному DNS". Если у меня запущены контейнеры с именами MariaDB1 и MariaDB2, то в phpadmin я так и укажу MariaDB1:3306 и MariaDB2:3306 соответственно. Снаружи доступ к phpadmin и прочим "web" интерфейсам через единственный контейнер с reverse proxy, которому и нужен expose (а-ля Ingress в кубере).

Возможно я действительно испорчен Kubernetes, где для шифрования между подами на бэкенде использовал бы скорее envoy proxy, mesh и т.п., нежели шифровал бы самостоятельно внутри пода.

Я не столько к вашему решению пристаю, оно красивое, сколько к архитектуре (что за приложение с несколькими контейнерами, взаимодействующими друг с другом по https внутри одной машины без планов выставиться в Интернет, где по-любому понадобится свой домен). Фактически мне просто интересно, насколько распространен ваш кейс. Вдруг я такой тип архитектуры упустил, а где-то он мне был бы полезен, эффективен.

Пост об организации взаимодействия разработчик-контейнер, а не для контейнер-контейнер (для контейнер-контейнер и не факт, что заведется, потому как на localhost резолвятся все поддомены).

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

В данном же случае появляется возможность запустить все через полноценный домен, что может быть удобнее при большом количестве запущенных локально сервисов.

Кроме того, использование traefik приближает использование сервисов к проду (если traefik используется на проде), что тоже может быть очень хорошим фактором.

Да, подобную схему можно организовать и локально, на кастомном домене. Но браузеры требуют HTTPS для всего, а самоподписанные сертификаты добавят своей головной боли.

Так что данное решение для организации локалхоста разработчика вполне неплохо выглядит.

Ответили ровно так, как бы сам и написал, благодарю :3

Честно говоря совсем не в теме, но прочитал с удовольствием, спасибо автору, хорошая статья.

Автор сделал полезную тулзу и выложил в открытый доступ без регистрации и СМС? Боже, куда мир катится...

Спасибо!

Простите меня, но удержаться было слишком сложно

Спасибо, отличная идея по организации локалхоста разработчика!

Предложение для расширения функционала:
Резолвить *.docker.indocker.app на 172.17.0.1. Или в принципе, вместо 127.0.0.1 все резолвить на 172.17.0.1...
Это позволит использовать домен и для взаимодействия между контейнерами, что может быть полезной фишкой для отладки за счет логгирования/трейсинга запросов.
Возможно, придется какой-нибудь доп.сервис поднимать...

Будет замечательное средство отладки окружения )

Правда остается открытым вопрос безопасности (MitM?) и универсальности подобного решения (docker может и на другой подсети жить).

P.S. MitM не считаю такой уж угрозой. Просто потому, что домен предполагает узкоспециализированное использование. А если все-таки такая угроза есть - то ничего не помешает подменить DNS записи и вместо локалхоста подсунуть все, что угодно.

Интересное предложение. Давайте так - я могу для вас (теста для) зону завести, скажем, вида *.__habr_sabmaks__.indocker.app, которая будет резольвится в 172.17.0.1. Вы сможете вдоволь наиграться, а после этого поделиться обратной связью. Если подводных комней не встретите (или элегантно их обойдёте) - то конечно впилим это как перманентную фичу

Тогда давайте уж *.__docker__.indocker.app и публично тестовой фичей объявить...
Или не объявлять ) Но в любом случае, домен лучше более обезличенным делать )

Правда я не знаю, как скоро у меня дойдут руки до "поиграться", тут и с traefik полноценно "поиграться" хочется и с доп сервисами "играться" придется - как понимаю, трейсинг и логи доступа не имеют полноценного GUI в рамках traefik...

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

P.S. Как понимаю, с HTTPS-сертификатом на несколько wildcard-доменов проблем не будет - судя по mkcert.sh, эта возможность уже используется...

Домен-то такой мы завести можем, а вот certbot начинает ругаться:

An unexpected error occurred:
Error creating new order :: Cannot issue for "*.__docker__.indocker.app": Domain name contains an invalid character

Так что пользуемся *.x-docker.indocker.app:

$ dig +noall +answer -t A bar.x-docker.indocker.app
bar.x-docker.indocker.app. 300	IN	A	172.17.0.1

$ dig +noall +answer -t A foo.bar.x-docker.indocker.app
foo.bar.x-docker.indocker.app. 300 IN	A	172.17.0.1

Релизнуто в v1.1.1. Играйтесь на здоровье, как будет чем поделиться - сюда или в личку черканите, пожалуйста :3

Обязательно черкану, если/когда будет чем поделиться )
В принципе, для полноценной фичи, не хватает простого упоминания о *.x-docker.indocker.app в ReadMe )
Можно и домен как более-менее постоянный для этой фичи рассматривать )

Сперва - полноценный тест, потом - в релиз :) Сможете и PR бабахнуть с юзкейсами, если не сложно. Хорошего дня вам!

Удалось немного поиграться. И что могу сказать:

Создание своей сети - крайне неудобно. Просто потому, что теперь все контейнеры надо в этой же сети создавать. А если забыть это сделать (для traefik или другого контейнера) - то можно очень увлекательно потратить время на разбор "почему правила подтянулись, а доступа нет".

Выход есть достаточно простой: запускать traefik в контейнере с --network host. Правда есть некоторые вопросы безопасности в таком режиме... Но удобство, на мой взгляд, того стоит.

И второй момент - удаление контейнера и всей инфраструктуры на его базе после перезагрузки - может стать неожиданностью. Так что флаг --rm я бы убрал (и добавил --restart=always).

Если подводить итог, то я бы рекомендовал использовать следующую команду для старта контейнера (при желании можно в скрипт обернуть):

docker run -d -v /var/run/docker.sock:/var/run/docker.sock:ro --network host --restart=always --name indocker.app quay.io/indocker/app

P.S. до *.x-docker.indocker.app руки пока не дошли...

Создание своей сети - крайне неудобно

Да, но так правильнее что-ли... Если есть возможность ограничить ресурсы одним скоупом - то лучше так делать. Хотя, для локальной разработки, может и нахрен

запускать traefik в контейнере с --network host

Для локального контейнера с открытыми сорцами - думаю, это вполне себе ок

Так что флаг --rm я бы убрал

Этот флаг немного не про то. Он про Automatically remove the container when it exits и не более

(и добавил --restart=always)

Но только не always, а unless-stopped тогда уж :D

до *.x-docker.indocker.app руки пока не дошли...

Блин, а это же самое интересное!

Да, если есть возможность - то да, лучше максимально ограничить. Но засовывая все в одну сеть - мы для других контейнеров открываем больше информации о прочих контейнерах. Так что --network host - вполне может быть компромиссом в вопросе безопасности.

Я понимаю, что --rm - это про удаление контейнера после остановки. Но одно дело - явная остановка, а другое - неявная. И перезагрузка, если я не ошибаюсь, будет неявной остановкой... Правда я это не проверял, --rm обычно вместе с -it использую, так что остановка происходит по Ctrl-C )

Согласен с unless-stopped, always из-за краткости чаще использую )

*.x-docker.indocker.app будет крайне интересен, когда я веб-версию mitmproxy запущу перед traefik в режиме реверс-прокси )
Можно будет не только обращаться к другим контейнерам, но и полноценно отслеживать запросы/ответы )
Надо вытащить сертификат из контейнера, да с правильными флагами запустить mitmproxy )

Ну что же, запустил mitmproxy... Работает как с HTTP, так и с HTTPS-запросами на одном порту...
Заодно протестировал .x-docker.indocker.app (работает!) - mitmproxy оказывается тоже сертификат хочет валидный, просто указанием на 172.17.0.1 не удалось отделаться )))

Если кратко, то при запущенном indocker.app:

#!/bin/bash

# Экспортируем сертификаты
docker cp indocker.app:/etc/traefik/certs/fullchain1.pem fullchain1.pem
docker cp indocker.app:/etc/traefik/certs/privkey1.pem privkey1.pem
# Подготавливаем сертификат для mitmproxy
cat privkey1.pem fullchain1.pem > mitm.pem
# Чистим лишнее
rm fullchain1.pem privkey1.pem
# Запускаем обратный прокси-сервер
docker run --rm -it -p 888:888 -p 8081:8081 -v $(pwd)/mitm.pem:/mitm.pem mitmproxy/mitmproxy mitmweb --set listen_port=888 --set keep_host_header=true --mode reverse:https://host.x-docker.indocker.app --web-host 0.0.0.0 --web-port 8081 --certs *=/mitm.pem --no-web-open-browser

P.S. Есть желание написать Dockerfile, который бы готовил образ для mitmproxy с готовыми командами/сертификатами (без необходимости копирования).
И docker-compose, который бы стартовал indocker.app и mitmproxy разом (с поддоменом для mitmproxy)...

Dockerfile с вытаскиванием сертификата достаточно прост (спасибо многоступенчатый сборке и возможности копирования файлов из других образов) и практически повторяет приведенный выше скрипт.
Но mitmproxy не хочет работать на поддомене - это специально запрещено в приложении (как защита от определенных атак), а обходные пути у меня не сработали...
Так что оптимальный путь - независимо запускать mitmproxy, не встраивая его в traefik...

P.S. Нашел способ выключить верификацию сертификата для реверс-прокси - так что команда запуска может быть значительно универсальнее:

docker run --rm -it -p 888:888 -p 8081:8081 -v "$(pwd)/mitm.pem:/mitm.pem" mitmproxy/mitmproxy mitmweb --set listen_port=888 --set keep_host_header=true --set ssl_insecure=true --mode reverse:https://172.17.0.1 --web-host 0.0.0.0 --web-port 8081 --certs *=/mitm.pem --no-web-open-browser

Спасибо большое за репозитоий, очень крутая идея! Есть небольшое предложение: может добавить упоминание в документации к проекту label traefik.docker.network=indocker-app-network ? Если к контейнеру, на который нужно проксировать, привязано несколько networks помимо Traefik, то он в большинтсве стартов будет запускаться и выдавать 503 ошибку при запросах, а иногда звезды будут сходиться и Trafik выберет нужную сеть indocker-app-network . Новичков в Trafik это может сбить с толку на несколько часов, а документацию читать всем ведь лень)

Ох, круто! А могли бы вы создать PR с изменениями? Может ещё чего "по дороге" заметите :)

Пара моментов по Dockerfile:

Почему выбрана 3я версия traefik (которая в бете)?
На мой взгляд, использование актуальной релизной версии было бы оправданнее - по ней больше документации, по ней больше информации и решенных вопросов / проблем.

И я не сторонник радикальной минимизации образов - потом очень сложно диагностировать проблемы в рантайме. Уже несколько раз на этом обжигался...

Так как апгрейд будет неминуем - лучше, кажется, раньше чем позже. Как раз потом воросов (переделок) будет поменьше. Да и прям мажорных-мажорных изменений не увидел, ежели честно.
А ваш покорныйслуга как раз сторонник distro-less подхода, т.е. поставлять только то, что надо. Как вариант, держать альтернативный тег с постфиксом alpine, для дебага (так, к слову, podman делает, да и не только он), как вариант :)

Sign up to leave a comment.

Articles