local dns & reverse proxy
После того как я решил начать экспериментировать с докером количество сервисов увеличилось и теперь помимо роутера или NAS появились и другие. Так что мне надоело каждый раз прописывать разные порты и захотелось ходить по хосту, а не вспоминать что на каком порту висит. Для этого нам нужен локальный днс для доменов по которым мы будем заходить и прокси который будет раскидывать запросы по контейнерам.
Запуск контейнера
С докером можно работать как через сам docker так и через docker-compose.
например так выглядит запуск portainer через cli
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
А вот так через docker-compose: сначала надо составать docker-compose.yml
services:
portainer:
name: portainer
image: portainer/portainer-ce:latest
volumes:
- portainer_data:/data
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8000:8000"
- "9443:9443"
а потом запускать
docker compose up -d
Через compose можно запустить сразу несколько контейнеров необходимых для работы сервиса и они все будут видеть друг друга.
Сеть в docker
Немного про сети docker: если в двух словах, то работа сети в docker реализуется за счет сетевых драйверов:
- none: отключение всех сетевых ресурсов.
- bridge: сетевой драйвер по умолчанию. По сути, это мост между контейнером и хостовой машиной.
- host: для автономных контейнеров устраняется сетевая изолированность между контейнером и хостом Docker и напрямую используются сетевые ресурсы хоста.
При создании контейнера мы можем вручную указать к каким сетям подключаться
docker run -d -p 8000:8000 -p 9443:9443 --network=homelab --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
# docker container inspect portainer_test | grep -A1 Networks |tail -n 1| cut -d'"' -f2
homelab
сеть homelab
В противном случае докер сам поместит контейнер в bridge сеть
# docker run -d -p 1000:8000 --name portainer_test --restart=always -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer-ce:latest
# docker container inspect portainer_test | grep -A1 Networks |tail -n 1| cut -d'"' -f2
bridge
сеть bridge
По умолчанию docker-compose создает единую сеть для контейнеров в отдельно взятом сервисе и называет её по имени контейнера, так что есть несколько вариантов:
- Засунуть все контейнеры в один docker-compose. Это решает задачу, но имеет большой минус: если надо будет поменять что то в одном контейнере то пересобираться будут все контейнеры и это занимает много времени.
- Сделать свою сеть и добавить туда все сервисы. В таком случае каждый отдельный сервис можно будет выключить отдельно, без пересборки всех остальных.

Подключается просто:
services:
test:
container_name: test
...
networks:
- homelab
networks:
homelab:
external: true
docker-compose.yml
docker network connect --ip 172.20.0.x mynet container_name
docker cli
Pi-hole
В качестве локального днс был выбран Pi-hole, он не только выполняет функцию днс, но и умеет работать с блоклистами, а значит будет меньше рекламы.
version: '3.8'
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
restart: always
ports:
- "53:53/tcp"
- "53:53/udp"
- "9001:80/tcp"
environment:
DNSMASQ_USER: root
SKIPGRAVITYONBOOT: 1
TZ: Europe/Moscow
DNSMASQ_LISTENING: all
volumes:
- '/mnt/HD/HD_a2/Public/Docker/Pihole/pihole:/etc/pihole'
- '/mnt/HD/HD_a2/Public/Docker/Pihole/dnsmasq.d:/mnt'
networks:
- homelab
networks:
homelab:
external: true
настройки и логи будут доступны на первом диске в каталоге Docker/Pihole
Контейнер при запуске не будет обновлять блоклисты и сразу начнет отвечать на все запросы, вообще по умолчанию dnsmasq отвечает только на локальные запросы, но в таком случае он будет недоступен. Панель управления будет доступна на 9001 порте. По идеи нам остается только добавить домены и направить их на локальные ip.

На этом с настройкой все, по желанию можно подключить различные блоклисты доступные на github. После того как у нас заработает прокси то проброс 80 порта можно убрать т.к. все запросы пойдут через nginx.
Proxy
Вообще в докере для проксирования есть traefik. Traefik это реверс прокси написанный специально для докера, он дискаверит контейнеры и при наличии определенных меток проксирует трафик в нужный контейнер. Работает это так:
labels:
- "traefik.enable=true"
- "traefik.http.routers.pihole.rule=Host(`dns.homelab-notes.duckdns.org`)"
- "traefik.http.services.pihole.loadbalancer.server.port=80"
Включаем traefik, выдаем хост и указываем на какой порт проксировать
В теории он может проксировать и на внешние хосты, но там надо лезть в конфиги. В сумме с проблемами получения сертификата LE я от него отказался в пользу nginx proxy manager. Фактчески это nginx, certbot и удобный gui в одном флаконе, из минусов это необходимость прописывать прокси вручную.
services:
###
### Nginx proxy manager
###
nginx:
image: 'jc21/nginx-proxy-manager:latest'
restart: always
container_name: nginx_proxy
ports:
- '8080:80'
- '81:81'
- '443:443'
networks:
- homelab
networks:
homelab:
external: true
docker-compose.yml
после запуска идем на 81 порт и видим страницу авторизации

После авторизации нам предложат изменить данные и пароль. Идем в Hosts > Proxy Hosts и жмем на кнопку "add proxy host"


Не лишним будет ограничить доступ для локальных ресурсов:
allow 192.168.0.0/16
allow 172.16.0.0/12
allow 10.0.0.8/8
deny all
Бонусом nginx proxy manager является удобная генерация ssl от let's encrypt. Для начала нам нужен домен на который будет выпущен сертификат. Я воспользовался duckdns.org, там можно бесплатно взять домен 3 уровня и добавлять TXT записи через api. Для получения сертификата через dnschallenge нужно будет указать свой ключ api.