VPN на Pod в Kubernetes или шлюз выхода: кому принадлежит туннель?
Pod — это собственное сетевое пространство имён, поэтому настоящий выбор в том, какой слой владеет туннелем: идентичность для каждой рабочей нагрузки или выход под управлением CNI с одним общим источником.
Вы развёртываете Pod с именем vpn-gateway, направляете тестовый клиент на его адрес и видите, как пакеты исчезают. В манифесте всё правильно, и kubelet не на что жаловаться. Причина структурная, а не конфигурационная: Kubernetes даёт каждому Pod собственное сетевое пространство имён, поэтому имя, метка или общее пространство имён API ничего не меняют в том, куда идут пакеты. Поэтому решение о том, как трафик VPN покидает кластер, начинается с вопроса о владении, а не об образах контейнеров.
Два механизма покрывают большинство реальных развёртываний, и они не взаимозаменяемы. Один помещает туннель в собственное сетевое пространство имён рабочей нагрузки. Другой просит CNI направить выбранный трафик через назначенный узел шлюза. Они отказывают по-разному, масштабируются по-разному и подчиняются разным командам, поэтому вопрос о контейнерах обычно порождает споры, которые никогда не заканчиваются.
Граница Pod — это пространство имён, а не хост перехода
Сетевое пространство имён Linux владеет своими интерфейсами, адресами, маршрутами и правилами межсетевого экрана, и каждый Pod получает одно. Контейнеры внутри этого Pod присоединяются к нему, и именно поэтому вспомогательный контейнер может переписать маршруты, которым затем следует приложение. У соседнего Pod такой же на вид, но полностью отдельный стек. Нет общей таблицы, которую мог бы унаследовать Pod-шлюз, и нет неявной передачи трафика из одного пространства имён в другое.
Поэтому шаблон Pod-шлюза работает только тогда, когда что-то выше по потоку действительно отправляет ему пакеты: Service, статический маршрут на узле или политика направления в CNI. Один адрес сам по себе ничего не притягивает.
Что даёт туннель на каждый Pod и сколько он стоит
Туннель, работающий в пространстве имён рабочей нагрузки, даёт этой нагрузке собственную клиентскую идентичность, которую многие сервисы удалённого доступа требуют до авторизации или учёта сеанса. Радиус поражения тоже остаётся локальным: сломанный туннель выводит из строя один Deployment вместо общего пути, которым пользуется всё. Как он работает, зависит от реализации, и именно здесь люди ошибаются.
Настройка, которой нужно только состояние ядра, может завершиться в init-контейнере и выйти. Демону в пользовательском пространстве, который должен жить всё время существования Pod, нужна перезапускаемая форма init-контейнера, введённая как нативный сайдкар в Kubernetes 1.29, и это различие идёт от реализации туннеля, а не от вкуса. Цена этой модели — дублирование: каждая реплика аутентифицируется, получает собственный адрес туннеля и занимает место удалённого сеанса, так что событие автомасштабирования становится событием ёмкости на другом конце канала. Копирование одного удостоверения во все реплики не убирает эту цену, оно убирает след аудита. Права заслуживают такой же проверки, ведь туннелю, которому нужен NET_ADMIN в собственном пространстве имён, нет причины наследовать полномочия всего хоста из примера с privileged: true.
Когда выходом владеет CNI
Если CNI это поддерживает, шлюз выхода выбирает трафик по рабочей нагрузке и диапазону назначения, а затем отправляет его через выбранные узлы под одним предсказуемым адресом источника. Версия Cilium — обычный ориентир, и её документация прямо говорит, что функции нужны особая конфигурация Cilium и узлы шлюза с правильными интерфейсами и адресами. Она также отмечает начальный период после создания Pod, когда направление ещё не применяется, и это проектный ввод, а не сноска.
Прочитайте эту задержку относительно своего обещания. Если правило состоит в том, что никакой трафик не может выйти другим путём, новый Pod, который передаёт в этот промежуток, уже его нарушил, поэтому последовательности запуска нужен собственный ответ: удерживать приложение, пока политика не применится, или принять окно и задокументировать его.
Держите утверждение узким. Шлюз выхода выбирает и транслирует путь. Он не завершает VPN и не делает кластер клиентом вашей удалённой сети без поддерживаемого соединения за ним.
| Question | Per-Pod tunnel | CNI egress gateway |
|---|---|---|
| Who holds the client identity | Each workload | The gateway, shared |
| What scaling changes | Enrollment and sessions per replica | Throughput and capacity per node |
| What the remote side sees | Distinct addresses | One translated source |
| Failure scope | One Deployment | Every workload behind the gateway |
Трансляция тихо меняет то, что видит удалённая сторона
Как только шлюз переписывает источник, адреса отдельных Pod исчезают, поэтому список разрешённых, привязанный к идентичности, становится списком, привязанным к одному общему адресу. Это обмен, а не дефект: меньше правил в обслуживании, грубее атрибуция, когда что-то ведёт себя плохо.
Аутентификация на уровне приложения и журналирование запросов должны нести то, что сеть больше не сообщает. Относитесь к ёмкости шлюза и его замене как к общей инфраструктуре, потому что перезагрузка одного узла теперь прерывает каждую нагрузку за ним, и проверяйте обратный путь заранее: удалённая сторона должна знать, как достичь транслированного адреса, обычно через маршрут или NAT, которым должен владеть человек, а не манифест. Планирование адресов тоже здесь, ведь пересекающиеся диапазоны кластера и удалённой сети ломают любую из моделей, и переход на IPv6 не оправдывает противоречивый план.
Решайте по владению, затем проверьте обещанный отказ
Выбирайте туннель на каждый Pod, когда удалённый сервис аутентифицирует отдельные нагрузки, когда канал нужен лишь нескольким Deployment и когда отказ должен оставаться локальным. Выбирайте шлюз, когда сетевая команда уже владеет путём, когда многие нагрузки делят одно назначение и когда стабильный адрес источника — весь смысл упражнения.
В любом случае проверяйте проданное свойство, а не установившееся состояние. Создайте Pod, дайте ему сразу передавать и смотрите первые секунды, если обещание было в том, что из туннеля ничего не утекает. Масштабируйте до пяти реплик и посчитайте сеансы, о которых сообщает удалённая сторона, если обещанием была идентичность на каждую нагрузку. Эти две проверки выявляют пределы механизма гораздо быстрее, чем разбор манифеста, и говорят, какой команде принадлежит следующий инцидент.