439 lines
23 KiB
HTML
439 lines
23 KiB
HTML
<!doctype html>
|
||||
|
|
<html lang="ru">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="utf-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|||
|
|
<title>Control Plane и Data Plane — Cloud IP Validator</title>
|
|||
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap">
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--bg: #F1F4F6;
|
|||
|
|
--surface: #FFFFFF;
|
|||
|
|
--surface-2: #E6ECEF;
|
|||
|
|
--ink: #142027;
|
|||
|
|
--muted: #55666F;
|
|||
|
|
--line: #C7D2D8;
|
|||
|
|
--control: #2F6FA3;
|
|||
|
|
--egress: #B8681A;
|
|||
|
|
--inbound: #0E7F76;
|
|||
|
|
--shadow: 0 1px 2px rgba(20,32,39,0.07), 0 8px 24px rgba(20,32,39,0.05);
|
|||
|
|
}
|
|||
|
|
@media (prefers-color-scheme: dark) {
|
|||
|
|
:root:not([data-theme="light"]) {
|
|||
|
|
--bg: #10171C;
|
|||
|
|
--surface: #172129;
|
|||
|
|
--surface-2: #1E2932;
|
|||
|
|
--ink: #E8EEF1;
|
|||
|
|
--muted: #93A5AF;
|
|||
|
|
--line: #2B3B44;
|
|||
|
|
--control: #74B3E3;
|
|||
|
|
--egress: #E3A75D;
|
|||
|
|
--inbound: #54CBBF;
|
|||
|
|
--shadow: 0 1px 2px rgba(0,0,0,0.35), 0 8px 24px rgba(0,0,0,0.25);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
:root[data-theme="dark"] {
|
|||
|
|
--bg: #10171C;
|
|||
|
|
--surface: #172129;
|
|||
|
|
--surface-2: #1E2932;
|
|||
|
|
--ink: #E8EEF1;
|
|||
|
|
--muted: #93A5AF;
|
|||
|
|
--line: #2B3B44;
|
|||
|
|
--control: #74B3E3;
|
|||
|
|
--egress: #E3A75D;
|
|||
|
|
--inbound: #54CBBF;
|
|||
|
|
--shadow: 0 1px 2px rgba(0,0,0,0.35), 0 8px 24px rgba(0,0,0,0.25);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
* { box-sizing: border-box; }
|
|||
|
|
|
|||
|
|
html, body { margin: 0; padding: 0; }
|
|||
|
|
|
|||
|
|
body {
|
|||
|
|
background: var(--bg);
|
|||
|
|
color: var(--ink);
|
|||
|
|
font-family: 'IBM Plex Sans', system-ui, sans-serif;
|
|||
|
|
-webkit-font-smoothing: antialiased;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.sheet {
|
|||
|
|
max-width: 1100px;
|
|||
|
|
margin-inline: auto;
|
|||
|
|
padding-inline: clamp(16px, 4vw, 40px);
|
|||
|
|
padding-block: clamp(28px, 5vw, 56px) 40px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
header.title-block { margin-bottom: clamp(30px, 5vw, 46px); }
|
|||
|
|
|
|||
|
|
.eyebrow {
|
|||
|
|
display: inline-block;
|
|||
|
|
font-family: 'IBM Plex Mono', monospace;
|
|||
|
|
font-size: 12.5px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
letter-spacing: 0.09em;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
color: var(--control);
|
|||
|
|
background: var(--surface);
|
|||
|
|
border: 1px solid var(--line);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h1 {
|
|||
|
|
font-size: clamp(30px, 4.6vw, 42px);
|
|||
|
|
line-height: 1.12;
|
|||
|
|
font-weight: 700;
|
|||
|
|
letter-spacing: -0.01em;
|
|||
|
|
margin: 0 0 14px;
|
|||
|
|
text-wrap: balance;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.lead {
|
|||
|
|
font-size: clamp(16px, 1.8vw, 18px);
|
|||
|
|
line-height: 1.62;
|
|||
|
|
color: var(--muted);
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
.lead strong { color: var(--ink); font-weight: 600; }
|
|||
|
|
|
|||
|
|
section.plane { margin-bottom: clamp(20px, 4vw, 32px); }
|
|||
|
|
|
|||
|
|
h2 {
|
|||
|
|
font-size: clamp(21px, 2.6vw, 25px);
|
|||
|
|
font-weight: 600;
|
|||
|
|
letter-spacing: -0.005em;
|
|||
|
|
margin: 0 0 12px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: baseline;
|
|||
|
|
gap: 11px;
|
|||
|
|
}
|
|||
|
|
h2 .num {
|
|||
|
|
font-family: 'IBM Plex Mono', monospace;
|
|||
|
|
font-size: 15px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--control);
|
|||
|
|
border: 1px solid var(--line);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
width: 28px;
|
|||
|
|
height: 28px;
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background: var(--surface);
|
|||
|
|
flex: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.plane > p.intro {
|
|||
|
|
font-size: 15.5px;
|
|||
|
|
line-height: 1.65;
|
|||
|
|
color: var(--muted);
|
|||
|
|
margin: 0 0 18px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.panel {
|
|||
|
|
background: var(--surface);
|
|||
|
|
border: 1px solid var(--line);
|
|||
|
|
border-radius: 14px;
|
|||
|
|
box-shadow: var(--shadow);
|
|||
|
|
padding: clamp(16px, 3vw, 30px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
figure { margin: 0; }
|
|||
|
|
.panel svg { display: block; width: 100%; height: auto; }
|
|||
|
|
|
|||
|
|
figcaption {
|
|||
|
|
margin-top: 20px;
|
|||
|
|
padding-top: 18px;
|
|||
|
|
border-top: 1px solid var(--line);
|
|||
|
|
font-size: 14.5px;
|
|||
|
|
line-height: 1.68;
|
|||
|
|
color: var(--muted);
|
|||
|
|
}
|
|||
|
|
figcaption strong { color: var(--ink); font-weight: 600; }
|
|||
|
|
|
|||
|
|
.legend { display: flex; flex-wrap: wrap; gap: 10px 28px; margin-top: 18px; }
|
|||
|
|
.legend .item {
|
|||
|
|
display: flex; align-items: center; gap: 9px;
|
|||
|
|
font-size: 13.5px; color: var(--muted);
|
|||
|
|
font-family: 'IBM Plex Mono', monospace;
|
|||
|
|
}
|
|||
|
|
.legend .swatch { width: 28px; height: 0; border-top-width: 3.5px; border-top-style: solid; flex: none; }
|
|||
|
|
.legend .swatch.dashed { border-top-style: dashed; }
|
|||
|
|
.legend .c-control { border-color: var(--control); }
|
|||
|
|
.legend .c-egress { border-color: var(--egress); }
|
|||
|
|
.legend .c-inbound { border-color: var(--inbound); }
|
|||
|
|
|
|||
|
|
hr.fold { border: 0; border-top: 1.5px dashed var(--line); margin: clamp(30px, 5vw, 46px) 0; }
|
|||
|
|
|
|||
|
|
footer {
|
|||
|
|
margin-top: 8px; padding-top: 20px; border-top: 1px solid var(--line);
|
|||
|
|
font-size: 13px; line-height: 1.7; color: var(--muted);
|
|||
|
|
}
|
|||
|
|
footer code {
|
|||
|
|
font-family: 'IBM Plex Mono', monospace; background: var(--surface-2);
|
|||
|
|
border-radius: 3px; padding: 1px 5px; font-size: 12.5px; color: var(--ink);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* diagram text */
|
|||
|
|
.diagram text { font-family: 'IBM Plex Sans', system-ui, sans-serif; fill: var(--ink); }
|
|||
|
|
.diagram .lbl-title { font-weight: 600; font-size: 19px; }
|
|||
|
|
.diagram .lbl-sub { font-size: 14px; fill: var(--muted); }
|
|||
|
|
.diagram .lbl-mono { font-family: 'IBM Plex Mono', monospace; font-size: 13px; fill: var(--muted); }
|
|||
|
|
.diagram .lbl-arrow { font-family: 'IBM Plex Mono', monospace; font-size: 13px; fill: var(--ink); }
|
|||
|
|
.diagram .lbl-group { font-weight: 600; font-size: 18px; fill: var(--ink); }
|
|||
|
|
|
|||
|
|
.diagram .box { fill: var(--surface); stroke: var(--line); stroke-width: 1.5; }
|
|||
|
|
.diagram .box-stack { fill: var(--surface-2); stroke: var(--line); stroke-width: 1; }
|
|||
|
|
.diagram .box-outer { fill: none; stroke: var(--muted); stroke-width: 1.5; stroke-dasharray: 7 5; }
|
|||
|
|
.diagram .box-accent { fill: none; stroke: var(--control); stroke-width: 3; }
|
|||
|
|
.diagram .box-tag { fill: var(--surface); stroke: var(--control); stroke-width: 1.5; }
|
|||
|
|
.diagram .box-tag-egress { fill: var(--surface); stroke: var(--egress); stroke-width: 1.5; }
|
|||
|
|
.diagram .box-tag-inbound { fill: var(--surface); stroke: var(--inbound); stroke-width: 1.5; }
|
|||
|
|
|
|||
|
|
.diagram .flow-control { stroke: var(--control); stroke-width: 2.5; fill: none; }
|
|||
|
|
.diagram .flow-control-dashed { stroke: var(--control); stroke-width: 2; stroke-dasharray: 6 5; fill: none; }
|
|||
|
|
.diagram .flow-egress { stroke: var(--egress); stroke-width: 4; fill: none; }
|
|||
|
|
.diagram .flow-inbound { stroke: var(--inbound); stroke-width: 4; fill: none; }
|
|||
|
|
.diagram .flow-inbound-dashed { stroke: var(--inbound); stroke-width: 2; stroke-dasharray: 5 5; fill: none; }
|
|||
|
|
|
|||
|
|
.diagram .fip-circle { fill: var(--surface-2); stroke: var(--ink); stroke-width: 2.25; }
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
|
|||
|
|
<div class="sheet">
|
|||
|
|
|
|||
|
|
<header class="title-block">
|
|||
|
|
<span class="eyebrow">Cloud IP Validator · микросервисный деплой</span>
|
|||
|
|
<h1>Control Plane и Data Plane</h1>
|
|||
|
|
<p class="lead">
|
|||
|
|
Система ревалидации освобождённых публичных IPv4-адресов в облаке на
|
|||
|
|
базе SDN VK Cloud. С переходом на <strong>docker-compose</strong>
|
|||
|
|
каждый компонент — <strong>control-api</strong>,
|
|||
|
|
<strong>admin-dashboard</strong>, <strong>prober</strong>,
|
|||
|
|
<strong>validator-agent</strong> — разворачивается отдельным
|
|||
|
|
контейнером по своему профилю (<strong>COMPOSE_PROFILES</strong>), на
|
|||
|
|
своём хосте. Ниже показано, как они связаны между собой (control
|
|||
|
|
plane) и что именно проверяется в реальном сетевом трафике (data
|
|||
|
|
plane).
|
|||
|
|
</p>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<section class="plane">
|
|||
|
|
<h2><span class="num">1</span> Control plane: кто кем управляет</h2>
|
|||
|
|
<p class="intro">
|
|||
|
|
Оператор — через веб-панель или напрямую curl'ом — обращается к
|
|||
|
|
единственному источнику истины, control-api. <code>validator-agent</code>
|
|||
|
|
и <code>prober</code> сами инициируют все вызовы (pull-модель):
|
|||
|
|
регистрируются, шлют heartbeat, забирают назначение, отчитываются о
|
|||
|
|
результатах. control-api сам к ним не обращается.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<div class="panel">
|
|||
|
|
<figure>
|
|||
|
|
<svg class="diagram" viewBox="0 0 1000 940" role="img" aria-label="Control plane: оператор и admin-dashboard обращаются к control-api; validator-agent и prober сами инициируют вызовы к control-api по HTTP; control-api опционально обращается к OpenStack (Keystone, Neutron / Sprut) для управления floating IP. Компоненты развёрнуты по docker-compose profiles: control-plane, dashboard, prober, validator.">
|
|||
|
|
<defs>
|
|||
|
|
<marker id="arrowControl" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8.5" markerHeight="8.5" orient="auto-start-reverse">
|
|||
|
|
<path d="M0,0 L10,5 L0,10 z" style="fill:var(--control)"></path>
|
|||
|
|
</marker>
|
|||
|
|
</defs>
|
|||
|
|
|
|||
|
|
<!-- Operator -->
|
|||
|
|
<rect class="box" x="400" y="30" width="200" height="56" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="500" y="55" text-anchor="middle">Оператор</text>
|
|||
|
|
<text class="lbl-sub" x="500" y="73" text-anchor="middle">инженер эксплуатации</text>
|
|||
|
|
|
|||
|
|
<!-- Host: управляющая машина -->
|
|||
|
|
<rect class="box-outer" x="100" y="120" width="800" height="300" rx="12"></rect>
|
|||
|
|
<text class="lbl-group" x="140" y="150">Управляющая машина</text>
|
|||
|
|
<text class="lbl-mono" x="140" y="170">profiles: control-plane, dashboard</text>
|
|||
|
|
|
|||
|
|
<rect class="box" x="140" y="188" width="720" height="78" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="500" y="220" text-anchor="middle">admin-dashboard</text>
|
|||
|
|
<text class="lbl-sub" x="500" y="243" text-anchor="middle">браузерная панель оператора · :8090</text>
|
|||
|
|
|
|||
|
|
<rect class="box" x="140" y="282" width="720" height="126" rx="8"></rect>
|
|||
|
|
<rect class="box-accent" x="140" y="282" width="720" height="6" rx="3"></rect>
|
|||
|
|
<text class="lbl-title" x="500" y="320" text-anchor="middle">control-api</text>
|
|||
|
|
<text class="lbl-sub" x="500" y="345" text-anchor="middle">HTTP API + оркестратор + SQLite · единственный stateful-сервис</text>
|
|||
|
|
<text class="lbl-mono" x="500" y="368" text-anchor="middle">:8080</text>
|
|||
|
|
|
|||
|
|
<!-- Operator -> admin-dashboard -->
|
|||
|
|
<path class="flow-control" d="M500,86 L500,188" marker-end="url(#arrowControl)"></path>
|
|||
|
|
<!-- admin-dashboard -> control-api -->
|
|||
|
|
<path class="flow-control" d="M500,266 L500,282" marker-end="url(#arrowControl)"></path>
|
|||
|
|
<!-- Operator -> control-api (routed around the left, direct curl) -->
|
|||
|
|
<path class="flow-control" d="M420,86 L420,105 L40,105 L40,345 L140,345" marker-end="url(#arrowControl)"></path>
|
|||
|
|
<text class="lbl-arrow" transform="rotate(-90 24 220)" x="24" y="220" text-anchor="middle">curl /api/v1/admin/* · :8080</text>
|
|||
|
|
|
|||
|
|
<!-- OpenStack -->
|
|||
|
|
<path class="flow-control-dashed" d="M500,408 L500,460" marker-end="url(#arrowControl)"></path>
|
|||
|
|
<text class="lbl-arrow" x="516" y="438" text-anchor="start">OpenStack API · mode: real</text>
|
|||
|
|
|
|||
|
|
<rect class="box" x="300" y="460" width="400" height="112" rx="10"></rect>
|
|||
|
|
<text class="lbl-title" x="500" y="494" text-anchor="middle">OpenStack</text>
|
|||
|
|
<text class="lbl-sub" x="500" y="516" text-anchor="middle">Keystone · Neutron / Sprut</text>
|
|||
|
|
<text class="lbl-mono" x="500" y="538" text-anchor="middle">associate / disassociate floating ip</text>
|
|||
|
|
|
|||
|
|
<!-- Host 2: внешние площадки (prober) -->
|
|||
|
|
<rect class="box-outer" x="40" y="630" width="430" height="250" rx="12"></rect>
|
|||
|
|
<text class="lbl-group" x="70" y="660">Внешние площадки ×N</text>
|
|||
|
|
<text class="lbl-mono" x="70" y="680">profiles: prober</text>
|
|||
|
|
|
|||
|
|
<rect class="box-stack" x="90" y="692" width="370" height="130" rx="8"></rect>
|
|||
|
|
<rect class="box-stack" x="80" y="701" width="370" height="130" rx="8"></rect>
|
|||
|
|
<rect class="box" x="70" y="710" width="370" height="130" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="255" y="748" text-anchor="middle">prober</text>
|
|||
|
|
<text class="lbl-sub" x="255" y="772" text-anchor="middle">TCP + ICMP пробы наружу</text>
|
|||
|
|
<text class="lbl-mono" x="255" y="793" text-anchor="middle">по 1 на площадку</text>
|
|||
|
|
|
|||
|
|
<!-- Host 3: ВМ-валидаторы -->
|
|||
|
|
<rect class="box-outer" x="530" y="630" width="430" height="250" rx="12"></rect>
|
|||
|
|
<text class="lbl-group" x="560" y="660">ВМ-валидаторы ×N</text>
|
|||
|
|
<text class="lbl-mono" x="560" y="680">profiles: validator</text>
|
|||
|
|
|
|||
|
|
<rect class="box-stack" x="580" y="692" width="370" height="130" rx="8"></rect>
|
|||
|
|
<rect class="box-stack" x="570" y="701" width="370" height="130" rx="8"></rect>
|
|||
|
|
<rect class="box" x="560" y="710" width="370" height="130" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="745" y="748" text-anchor="middle">validator-agent</text>
|
|||
|
|
<text class="lbl-sub" x="745" y="772" text-anchor="middle">egress-проверки + self-check</text>
|
|||
|
|
<text class="lbl-mono" x="745" y="793" text-anchor="middle">по 1 на ВМ-валидатор</text>
|
|||
|
|
|
|||
|
|
<!-- prober -> control-api -->
|
|||
|
|
<path class="flow-control" d="M255,710 L255,408" marker-end="url(#arrowControl)"></path>
|
|||
|
|
<rect class="box-tag" x="60" y="507" width="180" height="104" rx="10"></rect>
|
|||
|
|
<text class="lbl-arrow" x="150" y="537" text-anchor="middle">register · heartbeat</text>
|
|||
|
|
<text class="lbl-arrow" x="150" y="559" text-anchor="middle">assignments · results</text>
|
|||
|
|
<text class="lbl-mono" x="150" y="581" text-anchor="middle">→ /api/v1/probers/*</text>
|
|||
|
|
|
|||
|
|
<!-- validator-agent -> control-api -->
|
|||
|
|
<path class="flow-control" d="M745,710 L745,408" marker-end="url(#arrowControl)"></path>
|
|||
|
|
<rect class="box-tag" x="760" y="507" width="180" height="104" rx="10"></rect>
|
|||
|
|
<text class="lbl-arrow" x="850" y="528" text-anchor="middle">register · heartbeat</text>
|
|||
|
|
<text class="lbl-arrow" x="850" y="548" text-anchor="middle">self-check · events</text>
|
|||
|
|
<text class="lbl-mono" x="850" y="568" text-anchor="middle">results · complete</text>
|
|||
|
|
<text class="lbl-mono" x="850" y="588" text-anchor="middle">→ /api/v1/agents/*</text>
|
|||
|
|
|
|||
|
|
<!-- legend -->
|
|||
|
|
<line class="flow-control" x1="70" y1="905" x2="108" y2="905"></line>
|
|||
|
|
<text class="lbl-mono" x="116" y="909">управляющий вызов (control-plane API)</text>
|
|||
|
|
<line class="flow-control-dashed" x1="560" y1="905" x2="598" y2="905"></line>
|
|||
|
|
<text class="lbl-mono" x="606" y="909">только при openstack.mode: real</text>
|
|||
|
|
</svg>
|
|||
|
|
</figure>
|
|||
|
|
<figcaption>
|
|||
|
|
<strong>control-api</strong> — единственный компонент с состоянием
|
|||
|
|
(SQLite) и единственная точка принятия решений: какой IP кому
|
|||
|
|
назначить и когда считать проверку завершённой. Остальные сервисы
|
|||
|
|
развёрнуты по одному контейнеру на профиль docker-compose — на
|
|||
|
|
управляющей машине, на каждой внешней площадке и на каждой
|
|||
|
|
ВМ-валидаторе; все стрелки к control-api идут <em>от</em> них — это
|
|||
|
|
они опрашивают control-api, а не наоборот. Связь с облаком идёт
|
|||
|
|
через сеть OpenStack — Neutron и его альтернативная реализация в
|
|||
|
|
SDN VK Cloud, Sprut, — поэтому на схеме указаны оба.
|
|||
|
|
</figcaption>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<hr class="fold">
|
|||
|
|
|
|||
|
|
<section class="plane">
|
|||
|
|
<h2><span class="num">2</span> Data plane: что реально проверяется</h2>
|
|||
|
|
<p class="intro">
|
|||
|
|
Один и тот же публичный адрес проверяется одновременно с двух
|
|||
|
|
независимых сторон. Этот сетевой трафик control-api не видит
|
|||
|
|
напрямую — он получает только заявленный агентами результат по
|
|||
|
|
отдельному управляющему каналу (см. схему control plane выше).
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<div class="panel">
|
|||
|
|
<figure>
|
|||
|
|
<svg class="diagram" viewBox="0 0 1000 660" role="img" aria-label="Data plane: validator-agent проверяет исходящий трафик через floating IP — self-check во внешнем IP-echo сервисе, затем HTTPS/ICMP/SSH до целевых серверов. Независимо N внешних площадок-проберов проверяют входящий трафик на тот же floating IP — TCP-коннект на портах 22, 80, 443, 8080 и ICMP echo.">
|
|||
|
|
<defs>
|
|||
|
|
<marker id="arrowEgress" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="9" markerHeight="9" orient="auto-start-reverse">
|
|||
|
|
<path d="M0,0 L10,5 L0,10 z" style="fill:var(--egress)"></path>
|
|||
|
|
</marker>
|
|||
|
|
<marker id="arrowInbound" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="9" markerHeight="9" orient="auto-start-reverse">
|
|||
|
|
<path d="M0,0 L10,5 L0,10 z" style="fill:var(--inbound)"></path>
|
|||
|
|
</marker>
|
|||
|
|
</defs>
|
|||
|
|
|
|||
|
|
<!-- FIP center -->
|
|||
|
|
<circle class="fip-circle" cx="500" cy="280" r="80"></circle>
|
|||
|
|
<text class="lbl-title" x="500" y="274" text-anchor="middle">Floating IP</text>
|
|||
|
|
<text class="lbl-mono" x="500" y="294" text-anchor="middle">203.0.113.10</text>
|
|||
|
|
|
|||
|
|
<!-- VM (left) -->
|
|||
|
|
<rect class="box" x="40" y="210" width="240" height="140" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="160" y="255" text-anchor="middle">ВМ-валидатор</text>
|
|||
|
|
<text class="lbl-sub" x="160" y="278" text-anchor="middle">validator-agent</text>
|
|||
|
|
<text class="lbl-mono" x="160" y="300" text-anchor="middle">слушает 22/80/443/8080</text>
|
|||
|
|
|
|||
|
|
<path class="flow-egress" d="M280,265 L420,265" marker-end="url(#arrowEgress)"></path>
|
|||
|
|
<rect class="box-tag-egress" x="170" y="150" width="220" height="36" rx="9"></rect>
|
|||
|
|
<text class="lbl-arrow" x="280" y="173" text-anchor="middle">1. self-check + проверки</text>
|
|||
|
|
|
|||
|
|
<path class="flow-inbound-dashed" d="M420,300 L280,300" marker-end="url(#arrowInbound)"></path>
|
|||
|
|
|
|||
|
|
<!-- IP-echo (top) -->
|
|||
|
|
<rect class="box" x="340" y="30" width="320" height="90" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="500" y="68" text-anchor="middle">IP-echo сервис</text>
|
|||
|
|
<text class="lbl-sub" x="500" y="90" text-anchor="middle">api.ipify.org и т.п., вне облака</text>
|
|||
|
|
|
|||
|
|
<path class="flow-egress" d="M500,200 L500,120" marker-end="url(#arrowEgress)"></path>
|
|||
|
|
<rect class="box-tag-egress" x="405" y="147" width="190" height="32" rx="9"></rect>
|
|||
|
|
<text class="lbl-arrow" x="500" y="168" text-anchor="middle">GET, сравнение адреса</text>
|
|||
|
|
|
|||
|
|
<!-- Targets (bottom) -->
|
|||
|
|
<rect class="box" x="340" y="470" width="320" height="100" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="500" y="508" text-anchor="middle">Целевые серверы (targets)</text>
|
|||
|
|
<text class="lbl-sub" x="500" y="530" text-anchor="middle">HTTPS · ICMP · SSH (опционально)</text>
|
|||
|
|
|
|||
|
|
<path class="flow-egress" d="M500,360 L500,470" marker-end="url(#arrowEgress)"></path>
|
|||
|
|
<rect class="box-tag-egress" x="405" y="402" width="190" height="32" rx="9"></rect>
|
|||
|
|
<text class="lbl-arrow" x="500" y="423" text-anchor="middle">2. HTTPS / ICMP / SSH</text>
|
|||
|
|
|
|||
|
|
<!-- Probers (right) -->
|
|||
|
|
<rect class="box-stack" x="760" y="222" width="240" height="140" rx="8"></rect>
|
|||
|
|
<rect class="box-stack" x="750" y="216" width="240" height="140" rx="8"></rect>
|
|||
|
|
<rect class="box" x="740" y="210" width="240" height="140" rx="8"></rect>
|
|||
|
|
<text class="lbl-title" x="860" y="255" text-anchor="middle">prober ×N площадок</text>
|
|||
|
|
<text class="lbl-sub" x="860" y="278" text-anchor="middle">внешние тестовые точки,</text>
|
|||
|
|
<text class="lbl-mono" x="860" y="300" text-anchor="middle">независимо друг от друга</text>
|
|||
|
|
|
|||
|
|
<path class="flow-inbound" d="M740,265 L580,265" marker-end="url(#arrowInbound)"></path>
|
|||
|
|
<rect class="box-tag-inbound" x="685" y="150" width="240" height="36" rx="9"></rect>
|
|||
|
|
<text class="lbl-arrow" x="805" y="173" text-anchor="middle">TCP 22/80/443/8080 + ICMP</text>
|
|||
|
|
</svg>
|
|||
|
|
</figure>
|
|||
|
|
<figcaption>
|
|||
|
|
<strong>Egress</strong> (слева): <code>validator-agent</code> сам
|
|||
|
|
всегда обращается наружу через назначенный Floating IP — сначала
|
|||
|
|
self-check во внешнем IP-echo сервисе (адрес обязан быть вне
|
|||
|
|
облака — иначе SNAT не сработает), затем проверки из конфига до
|
|||
|
|
целей. <strong>Inbound</strong> (справа): N внешних площадок
|
|||
|
|
независимо стучатся в тот же адрес снаружи. Только сочетание
|
|||
|
|
обоих направлений даёт полную картину — адрес может нормально
|
|||
|
|
работать «наружу», но быть заблокирован для конкретной внешней
|
|||
|
|
сети, и наоборот.
|
|||
|
|
</figcaption>
|
|||
|
|
<div class="legend">
|
|||
|
|
<span class="item"><span class="swatch c-egress"></span>egress-проверка (от validator-agent, через FIP)</span>
|
|||
|
|
<span class="item"><span class="swatch c-inbound"></span>inbound-проверка (от внешних площадок, в FIP)</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<footer>
|
|||
|
|
Полная конфигурация деплоя — <code>deploy/docker/docker-compose.yml</code>
|
|||
|
|
(+ <code>.override.yml</code> для dev, <code>.prod.yml</code> для прод,
|
|||
|
|
профили <code>control-plane / dashboard / prober / validator</code>).
|
|||
|
|
Логика проверок, агрегации и телеметрии подробнее — в
|
|||
|
|
<code>docs/DIAGRAMS.md</code> и <code>docs/API.md</code>. Сетевой слой
|
|||
|
|
облака — OpenStack Neutron / Sprut (SDN VK Cloud).
|
|||
|
|
</footer>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</body>
|
|||
|
|
</html>
|