Docker: насочете един контейнер през VPN и задръжте друг директен
Дайте на тунела собствен контейнер, позволете само на услугите, които имат нужда от него, да споделят това пространство от имена, а после отчетете какво следва собственика: портове, localhost, DNS.
Един хост, две работни натоварвания и изискване, което ги разделя: клиент за изтегляне, който трябва да излиза през комерсиален VPN, и интерфейс за управление, който трябва да продължи да използва връзката на хоста. Инсталирането на VPN клиента на хоста проваля изискването веднага, защото тогава двете натоварвания споделят един изход. Моделът, който наистина работи, е да преместите тунела в собствен контейнер и да позволите само на натоварванията, които имат нужда от него, да се присъединят към мрежовото пространство от имена на този контейнер. Всичко останало запазва обичайния път на Docker, а подробностите по-нататък са следствия от това кой притежава пространството от имена.
Защо споделеното пространство от имена променя правилата
Контейнер, който се присъединява към пространството от имена на друг, не получава собствен мрежов стек. Той вижда същите интерфейси, същата таблица за маршрутизация и същия loopback като контейнера, към който се е присъединил, и точно затова моделът е полезен: натоварването резолвира и се свързва нормално, а тунелът отдолу прихваща трафика, без приложението да знае. Цената е, че всяка настройка с мрежов характер вече има един собственик и присъединяващият се контейнер не е той.
Това правило за собственост отговаря на повечето въпроси за конфигурацията, преди още да са зададени. Ако настройката живее в мрежовия стек, тя принадлежи на страничния контейнер. Ако описва процеса, тя остава при натоварването.
Loopback е частта, която изненадва хората. Вътре в присъединяващия се контейнер localhost се резолвира до loopback на страничния контейнер, а не до собствен частен такъв, така че услуга, която се свързва към 127.0.0.1 с очакване за изолация, сега споделя този адрес с контейнера на тунела.
Публикувайте портове на контейнера, който притежава пространството от имена
Съпоставянето на портове принадлежи на собственика на пространството от имена, дори когато слушащият процес работи в другия контейнер. Декларирайте съпоставянето на страничния контейнер и оставете натоварването да запази собствената си конфигурация на процеса непокътната. Съпоставяне на присъединяващата се услуга не може да влезе в сила, независимо дали двигателят го отхвърля, или тихо го пренебрегва според това как е стартиран контейнерът, и затова интерфейс, който беше достъпен преди миг, изчезва, щом моделът бъде приложен.
services:
gluetun:
image: qmcgaw/gluetun
cap_add: [NET_ADMIN]
ports: ["8080:8080"]
client-vpn:
image: example/client
network_mode: "service:gluetun"
client-direct:
image: example/client
ports: ["8081:8080"]Четете този блок като три решения, а не като три услуги. Страничният контейнер притежава тунела и публикувания порт; присъединяващият се клиент не декларира никаква мрежа; директният клиент запазва собствено съпоставяне в мрежата по подразбиране. Способността NET_ADMIN е това, което позволява на страничния контейнер да създава интерфейси и да променя маршрути, и тя е причината този контейнер да се нуждае от повече привилегии от натоварванията, които обслужва.
Услугата, която остава директна, запазва собствената си мрежа
Натоварването, което не трябва да минава през тунела, запазва моста по подразбиране или собствена дефинирана от потребителя мрежа, със свой публикуван порт и свой изход. Изкушаващо е да го прикачите към мрежата на страничния контейнер, за да могат двата контейнера да говорят по име, но тази една стъпка тихо премества трафика му в тунела. Ако трябва да се достигнат един друг, изградете отделна дефинирана от потребителя мрежа, прикачете и двата към нея и оставете страничния контейнер единствен изходящ път.
Дръжте двата въпроса отделно: кои контейнери могат да се достигнат и кой откъде излиза. Сливането им е най-разпространеният начин този дизайн да се превърне отново в VPN за целия хост.
Матрица на отказите за първия час
Повечето първи опити се провалят по един от четири начина и всеки има различна причина. Проверявайте ги в този ред, защото първите два обясняват почти всеки доклад.
| Symptom | Likely cause |
|---|---|
| Web UI unreachable after the change | Port mapping declared on the joining container instead of the sidecar |
| Both containers show the provider's exit address | The direct service was attached to the sidecar network |
| Internal names stop resolving | Resolver settings live on the host, not in the shared namespace |
| Health check fails but the app works | The check probes localhost, which now means the sidecar |
Четвъртият ред е този, който хората погрешно диагностицират като дефект в приложението. Проба, написана срещу собствения loopback на натоварването, сега тества контейнера на тунела, така че или се проваля, или съобщава напълно грешната услуга.
Когато едно име на хост има нужда от тунела
Понякога пълният тунел е прекалено много: едно вътрешно име трябва да минава през корпоративен VPN, докато локален сокет или връзка за дисплей продължава да работи. Пълният тунел вътре в контейнера чупи този път и всичко, адресирано до хоста, затова маршрутизирайте само префикса, който има нужда. Обвивка за разделено маршрутизиране около клиента прави това, като изпраща именувания хост през тунела и оставя предишния маршрут непокътнат за всичко останало.
Изберете това, когато трябва да достигнете само една услуга и не се опитвате да преместите целия трафик на контейнера. Това е различен инструмент от страничния контейнер и отговаря на различен въпрос.
Проверете, после го направете устойчиво на рестарт
Попитайте всеки контейнер от кой адрес изглежда, че идва. Този в тунела трябва да отпечата адреса на доставчика, а директният — изхода на хоста. Еднаквите отговори означават, че директната услуга е наследила грешното пространство от имена или VPN на ниво хост все още прихваща всичко, и тези две поправки са различни. Пуснете проверката отново след всяка промяна в compose, защото един копиран блок на услуга е достатъчен, за да върне проблема.
Три неща обикновено се чупят по-късно. DNS следва собственика на пространството от имена, затова настройките на резолвера и домейна за търсене принадлежат на страничния контейнер, а не на хоста. VPN с пренасочване на портове сменя слушащия си порт при повторно свързване, затова пренасоченият от доставчика порт и слушащият порт от страната на контейнера трябва да се запишат обратно в конфигурацията на клиента или в съпоставянето на портове, обикновено чрез малък обновител, който чете API на доставчика. И всяка проба, написана срещу localhost, сега пита контейнера на тунела, а не натоварването.
Запишете модела, включително кой контейнер притежава пространството от имена и кои портове му принадлежат. Следващият човек ще добави услуга, ще копира грешния блок и ще възпроизведе първия отказ в тази статия.