惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MyScale Blog
MyScale Blog
Microsoft Azure Blog
Microsoft Azure Blog
H
Help Net Security
N
News and Events Feed by Topic
Recent Announcements
Recent Announcements
D
Docker
M
MIT News - Artificial intelligence
L
LangChain Blog
I
InfoQ
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
博客园_首页
MongoDB | Blog
MongoDB | Blog
美团技术团队
S
Schneier on Security
G
GRAHAM CLULEY
月光博客
月光博客
有赞技术团队
有赞技术团队
Vercel News
Vercel News
Scott Helme
Scott Helme
P
Privacy International News Feed
Last Week in AI
Last Week in AI
Recorded Future
Recorded Future
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
Attack and Defense Labs
Attack and Defense Labs
Google Online Security Blog
Google Online Security Blog
Simon Willison's Weblog
Simon Willison's Weblog
量子位
S
Security @ Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
V
Visual Studio Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
NISL@THU
NISL@THU
N
Netflix TechBlog - Medium
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Spread Privacy
Spread Privacy
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
罗磊的独立博客
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Threatpost
L
Lohrmann on Cybersecurity
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Security Affairs
Cloudbric
Cloudbric
爱范儿
爱范儿
H
Heimdal Security Blog
PCI Perspectives
PCI Perspectives

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Два Kubernetes-кластера — одна сеть: объединяем через Mesh и межкластерный роутинг
Константин · 2026-05-07 · via Все публикации подряд на Хабре

Сложный

9 мин

8.7K

Когда Kubernetes-кластеров становится больше одного, инфраструктура начинает жить по новым правилам. Один кластер развёрнут в основном датацентре, второй — в резервной площадке. Сложности начинаются в тот момент, когда этим кластерам нужно взаимодействовать друг с другом. Сервисы в одном кластере должны обращаться к сервисам в другом, приложениям требуется нормальная маршрутизация, а администраторам хочется управлять этим без набора костылей.

В этой статье разберём, как объединить два Kubernetes-кластера в единую сетевую среду, где:

  • pod'ы могут общаться напрямую между собой

  • сервисы доступны между кластерами

  • трафик можно гибко маршрутизировать

В качестве сетевого слоя будем использовать Calico, а для межкластерного взаимодействия сервисов — Istio. Первый даст маршрутизацию и связность, второй — discovery, балансировку и управление трафиком на уровне приложений.

Дисклеймер

Описанный подход на момент написания обкатывается только на тестовых контурах. Под нагрузкой поведение системы не валидировалось, поэтому гарантировать стабильность или предсказуемость в реальных сценариях не могу.

Фактически, сейчас можно опираться лишь на синтетические тесты Istio и рассчитывать на корректную работу Calico. Отдельный риск — наличие критических багов в BGP-реализации, которые в условиях межкластерной маршрутизации могут проявиться не сразу.

Зачем объединять два кластера

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

Чаще всего несколько кластеров связывают через внешний входной слой: LoadBalancer, Ingress, публичные DNS-имена. Для пользовательских запросов это нормально, но для внутреннего взаимодействия сервисов такой путь выглядит не очень. Один backend в первом кластере обращается ко второму backend во втором кластере, при этом запрос выходит наружу, проходит через ingress-контур и только потом возвращается обратно во внутреннюю сеть.

В этой статье пойдём другим путём — построим привычную внутреннюю связность между кластерами. Такую, где можно обращаться к сервисам через Service, DNS-имена Kubernetes и при необходимости напрямую по Pod IP, будто перед нами не два отдельных кластера, а единая среда.

Архитектура решения

В основе решения лежат два независимых Kubernetes-кластера, каждый со своим control plane, worker-нодами и стандартным набором сервисов. Pod CIDR и Service CIDR в кластерах не должны пересекаться.

Кластер

Pod CIDR

Service CIDR

cluster-a

10.10.0.0/16

10.96.0.0/12

cluster-b

10.20.0.0/16

10.97.0.0/12

Также стоит заранее проверить MTU, firewall и доступность портов для BGP, Istio east-west gateway. Если в качестве маршрутизации используется BGP, то перед настройкой Calico стоит отдельно согласовать с сетевой командой: какие ASN будут использоваться, где допустим eBGP, где нужен iBGP, предполагаются ли route reflectors, какие префиксы разрешено анонсировать и какие фильтры будут применяться. Брать первый попавшийся ASN — нельзя, может оказаться так, что этот номер уже используется в другой зоне или зарезервирован под существующую схему маршрутизации.

За сетевую часть отвечает Calico. В зависимости от режима он может работать как через BGP, так и через overlay (VXLAN/IPIP). Calico анонсирует маршруты:

  • по умолчанию — префиксы отдельных Pod’ов (/32);

  • либо агрегированные блоки, если используется настройка IPPool с заданным blockSize.

BGP-сессии при этом могут устанавливаться:

  • напрямую между нодами;

  • через выделенные route reflectors;

  • либо с внешними сетевыми устройствами (например, ToR-роутерами).

В межкластерном сценарии это означает, что при наличии BGP-пиринга между кластерами (напрямую, через RR или через общую внешнюю сеть) ноды одного кластера начинают знать маршруты до Pod-сетей другого кластера и наоборот. Если в первом кластере Pod имеет адрес 10.10.1.24/32, а во втором — 10.20.1.24/32, то в таблицах маршрутизации появляются соответствующие записи, и трафик начинает ходить напрямую на уровне L3.

По умолчанию Service CIDR через BGP не распространяется. Однако Calico умеет анонсировать сервисные адреса (ClusterIP), если это явно включить через настройки advertise service IP. Если это не сделано, доступ к сервисам обеспечивается через kube-proxy либо eBPF dataplane в Calico, который может работать без kube-proxy.

Настраиваем BGP через Calico

Прежде чем настраивать BGP, полезно хотя бы на базовом уровне понять, как он работает. Сначала посмотрим на глобальную конфигурацию. В каждом кластере задаём свой ASN 64512 и 64513 через ресурс BGPConfiguration:

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  asNumber: 64512
  nodeToNodeMeshEnabled: false

Mesh здесь отключаем, почти всегда используем route reflector или внешние маршрутизаторы. Дальше описываем соседство через BGPPeer, указывая IP и ASN противоположной стороны — это может быть ToR-коммутатор, route reflector или ноды второго кластера:

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: peer-to-cluster-b
spec:
  peerIP: 192.168.100.10
  asNumber: 64513

Если всё настроено корректно, на сетевом оборудовании появятся маршруты, покрывающие диапазоны 10.10.0.0/16 и 10.20.0.0/16 как правило, в виде более мелких префиксов, а не одного маршрута.

Проверка:

  • calicoctl node status — смотрим, что BGP-сессии живы,

  • на внешнем маршрутизаторе — появились ли маршруты до pod CIDR,

  • ip route — проверяем, что маршруты приходят на ноды.

Иногда нужно задать пиринг не для всех нод, а только для конкретных. В этом случае используется селектор:

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: peer-only-workers
spec:
  nodeSelector: role == "worker"
  peerIP: 192.168.100.10
  asNumber: 64513

Когда нод становится >= 100, лучше использовать некоторые ноды как отражатели. Делаем ноду route reflector’ом:

apiVersion: projectcalico.org/v3
kind: Node
metadata:
  name: node-rr-1
spec:
  bgp:
    routeReflectorClusterID: 192.168.0.1

Дальше настраиваем пиринг нод к RR. При этом RR-ноды нужно заранее промаркировать label’ом, например route-reflector=true.

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: peer-to-rr
spec:
  nodeSelector: all()
  peerSelector: route-reflector == "true"

Плюсы: резкое уменьшение количества BGP-сессий. Минусы: нужна отказоустойчивость, минимум 2 отражателя.

Анонсируем Service IP через BGP в Calico

С подами разобрались, но в реальности пользователи/приложения ходят не в Pod IP, а в Service. Calico может анонсировать IP-адреса сервисов (ClusterIP или ExternalIP) в BGP, и внешняя сеть начинает видеть их как обычные маршруты. Для этого используется настройка serviceClusterIPs и/или serviceExternalIPs в ресурсе BGPConfiguration:

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  serviceClusterIPs:
    - cidr: 10.96.0.0/12

После этого Calico начнёт анонсировать диапазон сервисов в BGP. Анонс идёт с учетом режима — в режиме ExternalTrafficPolicy=Local маршрут будет объявляться только с нод, где есть backend-поды. Если используется ExternalIP, добавляем:

spec:
  serviceExternalIPs:
    - cidr: 203.0.113.0/24

Теперь эти адреса тоже будут доступны извне через BGP — без отдельного балансировщика.

Проверка:

  • calicoctl node status — смотрим, что BGP-сессии живы,

  • на внешнем маршрутизаторе — появились ли маршруты до service CIDR,

  • ip route — проверяем, что маршруты приходят на ноды.

Отдельно стоит отметить, что это один из способов сделать Kubernetes-сервисы публичными. Вместо классической схемы с Ingress-контроллером или облачным LoadBalancer’ом, вы фактически делегируете публикацию сервисов на уровень сетевой инфраструктуры через BGP.

Дальше логично пойти ещё на уровень выше и посмотреть, как объединять кластеры между собой не только на уровне сети, но и на уровне сервисов. Для этого хорошо подходит Istio — в следующем разделе после склеивания DNS, разберём одну из моделей мультикластерного деплоя и посмотрим, как сервисы могут жить сразу в нескольких кластерах и иметь дополнительные политики отказаустойчивости и балансировки.

Если хочется расширить картину, то помимо Istio существуют и другие варианты: Linkerd с мультикластером через service mirroring, Consul с федерацией кластеров и встроенным сервис-дискавери, а также Cilium с Cluster Mesh без sidecar’ов. Для базовых сценариев можно обойтись и нативным Kubernetes Multi-Cluster Services API, если не требуется полноценный service mesh.

Склеиваем DNS-пространство кластеров через CoreDNS

Как только между кластерами появляется нормальная L3-связанность (спасибо Calico и настроенным маршрутам), возникает естественное желание: пусть сервисы резолвятся так же просто, как внутри одного кластера. Делать это через один общий домен cluster.local — нельзя, возникает коллизия имён, поэтому — развести DNS-зоны по кластерам и связать их через CoreDNS.

cluster-a.local:53 {
    kubernetes cluster-a.local
}

cluster-b.local:53 {
    forward . 10.200.0.10
}

Где 10.200.0.10 — адрес CoreDNS второго кластера. Но чтобы это работало как обычно, одного форвардинга недостаточно — нужно, чтобы сами Pod’ы знали, какие DNS-зоны искать. Для этого нужно поправить настройки kubelet (через --cluster-domain), чтобы в resolv.conf внутри Pod’ов появились search-домены вроде:

search default.svc.cluster-a.local svc.cluster-a.local cluster-a.local cluster-b.local

Если не хочется трогать основной CoreDNS, эту же логику можно вынести в NodeLocal DNSCache. Это по сути тот же CoreDNS, только запущенный на каждой ноде и выступающий как локальный DNS-прокси. В нём можно описать зоны и форвардинг, не изменяя основную конфигурацию.

cluster-a.local:53 {
    forward . 10.201.0.10
    force_tcp
}

cluster-b.local:53 {
    forward . 10.200.0.10
    force_tcp
}

.:53 {
    forward . /etc/resolv.conf
    prefer_udp
}

Здесь каждая зона указывает в какой кластер идти за ответом, а всё остальное уходит в стандартный резолвер linux.

Объединяем кластеры через Istio Multicluster

Нужно понимать, что Istio оперирует понятием сетевой модели — это напрямую влияет на то, как будет ходить трафик. Network — это группа workload’ов, которые могут напрямую достучаться друг до друга без gateway. Отсюда вытекают два базовых сценария: single network, где все Pod’ы между кластерами доступны напрямую и multi-network где прямой связности нет и трафик обязан идти через east-west gateway.

single network model

single network model

Если Calico уже обеспечивает pod-to-pod между кластерами, можно рассматривать архитектуру как single network — Istio в этом случае будет напрямую балансировать между Pod IP, без обязательного захода через gateway. Но как только появляется изоляция сетей, разные VPC или ограничения инфраструктуры, происходит переход в multi-network модель, где gateway становится обязательным элементом и точкой входа для межкластерного трафика. В этой модели Istio уже не использует Pod IP напрямую — весь межкластерный трафик идёт через east-west gateway, который выступает как точка маршрутизации и mTLS-терминации.

multiple networks model

multiple networks model

$ cat <<EOF > cluster1.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      meshID: mesh1
      multiCluster:
        clusterName: cluster-a
      network: network-a
EOF

Во втором кластере — cluster-b и network-b. Если сети разные — поднимаем east-west gateway в каждом кластере:

samples/multicluster/gen-eastwest-gateway.sh --network network-a | istioctl install -y -f -

И отдельно экспонируем сервисы через него:

kubectl apply -n istio-system -f samples/multicluster/expose-services.yaml

После этого связываем control plane через remote secrets:

istioctl create-remote-secret --context=cluster-b --name=cluster-b | kubectl apply -f - --context=cluster-a

И в обратную сторону. По сути, мы даём istiod доступ к API другого кластера, чтобы он мог подтягивать endpoints.

Если раньше istioctl proxy-config endpoints <pod> показывал только локальные IP, то теперь там появляются адреса второго кластера или IP его east-west gateway. Сервис reviews.default.svc.cluster.local становится глобальным: Envoy получает список endpoints из всех кластеров и сам решает, куда отправить запрос. По умолчанию Istio использует round-robin между всеми endpoints, независимо от того, в каком кластере они находятся. Чтобы добиться нормального поведения, сначала локально, потом failover, нужно явно настроить DestinationRule с localityLbSetting

trafficPolicy:
  loadBalancer:
    localityLbSetting:
      enabled: true
      failover:
      - from: network-a
        to: network-b

Без этого трафик может гулять между кластерами. В облаках особо критично - так как запросы между датацентрами могут стоить отдельных ресурсов.

Безопасность: mTLS между кластерами

Когда кластеры начинают общаться друг с другом напрямую, вопрос безопасности встает особо остро - трафик начинает выходить за пределы одного доверенного домена. В Istio это решается через mTLS, в основе которого лежит: шифрование, модель идентичности и доверия. Каждый workload получает SPIFFE-идентичность (spiffe://cluster.local/ns/default/sa/default), а сертификаты для этих идентичностей выпускаются Certificate Authority (CA) внутри mesh.

Sidecar-прокси автоматически шифруют весь east-west трафик между сервисами и используют эти сертификаты для взаимной аутентификации. В multi-cluster сам факт сетевой связности между кластерами не означает наличие доверия между ними.

Чтобы Pod из cluster-a мог безопасно ходить в сервис в cluster-b, необходимо настроить доверие между mesh’ами:

  • либо использовать общий root CA

  • либо обменяться trust bundle между mesh’ами (например, через SPIFFE Trust Domain Federation)

При корректно настроенном доверии Pod из cluster-a, обращаясь к сервису в cluster-b, устанавливает TLS-соединение, где обе стороны взаимно аутентифицируются по сертификатам и проверяют их через свой trust bundle. Включается через PeerAuthentication:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT

После этого любой незашифрованный трафик перестаёт проходить. Дополнительно можно ограничить, кто с кем вообще имеет право общаться, через AuthorizationPolicy

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-reviews
spec:
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/frontend/sa/frontend-sa"]

Здесь доступ даётся по конкретной SPIFFE-идентичности. Но даже если между кластерами есть полная L3-связанность, доступ на уровне приложений не становится автоматически разрешённым. Сеть отвечает за доставку пакетов, а фактическое решение пускать или нет принимается sidecar-прокси.