diff --git a/docs/architecture/README.md b/docs/architecture/README.md index cab1e2e..fb290c1 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -1,47 +1,55 @@ # Модель архитектуры в нотации ArchiMate -Модель контура **УГМК** (`d8-ugmk-prod`) в двух форматах — содержимое одинаковое: +Модель технологического слоя типового контура платформы. **Все данные, зависящие от конкретной инсталляции — имя кластера, узлы, домены, реестр — заменены на примеры.** Реальные значения в репозиторий не попадают: они задаются профилем контура, который держится отдельно. + +Прикладной слой присутствует только как потребители технологических сервисов. Межсервисные REST-связи приложений и бизнес-слой будут добавлены отдельно. + +## Файлы | Файл | Формат | Чем открывать | |---|---|---| -| `ugmk-technology.archimate` | нативный формат [Archi](https://www.archimatetool.com/) | десктопный Archi: File → Open | -| `ugmk-technology.xml` | The Open Group ArchiMate Model Exchange File Format 3.1 | любой инструмент с импортом Open Exchange, в том числе archi-online.com: кнопка **Import, save, and create models** → Open Exchange | +| `example-technology.archimate` | нативный формат [Archi](https://www.archimatetool.com/) | десктопный Archi: File → Open | +| `example-technology.xml` | The Open Group ArchiMate Model Exchange File Format 3.1 | любой инструмент с импортом Open Exchange, в том числе archi-online.com: **Import, save, and create models** → Open Exchange | | `preview.html` | SVG-рендер всех представлений | любой браузер, без установки чего-либо | -`ugmk-technology.xml` проверен на соответствие официальной схеме `archimate3_Diagram.xsd` — валидатор ошибок не нашёл. Если сторонний инструмент не берёт нативный `.archimate`, используйте его. - -Отличие обменной версии: вложенность узлов на схемах развёрнута в плоский список с абсолютными координатами (в обменном формате трактовка координат вложенного узла неоднозначна между реализациями). Визуально раскладка та же, но компоненты внутри кластера не будут «вложены» в него как дочерние — при перетаскивании контейнера содержимое останется на месте. - -Сейчас в модели проработан **технологический слой**. Прикладной слой присутствует только как потребители технологических сервисов — межсервисные REST-связи приложений и бизнес-слой будут добавлены отдельно. - -## Состав - 70 элементов, 153 отношения, 9 представлений. | Представление | Что показывает | |---|---| | T1 | Кластер, ПО внутри него, внешние узлы, технологические сервисы. Без приложений — обзорная схема | -| T2 | Маршрутизация HTTPS — 21 приложение | -| T3 | Аутентификация OIDC — 10 | -| T4 | Управление секретами — 25 | -| T5 | Реляционное хранилище — 23 | -| T6 | Кэш — 3 | -| T7 | Событийная шина — 12 | -| T8 | Очередь сообщений AMQP — 11 | -| T9 | Объектное хранилище — 13 | +| T2–T9 | По одному представлению на технологический сервис: кто им пользуется | -Разбиение на представления по сервисам сделано осознанно: все 118 связей «сервис → приложение» на одной схеме нечитаемы. В модели они лежат одним набором, представления — это срезы. +Разбиение по сервисам сделано осознанно: все 118 связей «сервис → приложение» на одной схеме нечитаемы. В модели они лежат одним набором, представления — это срезы. -Метамодель намеренно урезана. Технологический слой: `Node`, `SystemSoftware`, `TechnologyService`, `Artifact`. Прикладной: `ApplicationComponent`. Отношения: `Composition`, `Realization`, `Serving`, `Assignment`. Не используются `Device`, `Path`, `CommunicationNetwork`, `TechnologyFunction` — в этом контуре они не несут информации. +Метамодель намеренно урезана. Технологический слой: `Node`, `SystemSoftware`, `TechnologyService`, `Artifact`. Прикладной: `ApplicationComponent`. Отношения: `Composition`, `Realization`, `Serving`, `Assignment`. Не используются `Device`, `Path`, `CommunicationNetwork`, `TechnologyFunction` — они не несут информации на этом уровне. + +## Как собрать модель для реального контура + +1. Снять матрицу связей с нужного кластера: + + ``` + py scan_contour.py <имя-кластера> matrix.json + ``` + +2. Скопировать `contour-profile.example.json`, подставить настоящие имена кластера, узлов, доменов и реестра. **Копию хранить вне репозитория.** + +3. Собрать модель и производные форматы: + + ``` + py build_archimate.py <профиль.json> matrix.json out.archimate + py to_open_exchange.py out.archimate out.xml + py render_preview.py out.archimate out.html + ``` + +Результат шага 3 в репозиторий не коммитить — он содержит данные контура. ## Откуда взяты связи -Не нарисованы от руки. Выведены из манифестов, поэтому проверяемы: +Не нарисованы от руки, а выведены из манифестов, поэтому проверяемы. | Связь | Источник | |---|---| -| Состав кластера | `clusters/d8-ugmk-prod/kustomization.yaml` | -| Версии чартов | `infrastructure//base/helmrelease.yaml` | +| Состав кластера, версии чартов | `clusters/<контур>/kustomization.yaml`, `infrastructure//base/helmrelease.yaml` | | Приложение → PostgreSQL | env `POSTGRES_*`, `DB__HOST` или путь Vault `secrets/data/apps//postgres` | | Приложение → RabbitMQ | env `RABBITMQ*`, `AMQP__*` | | Приложение → Kafka | env `KAFKA*`, `BOOTSTRAP_SERVERS` | @@ -49,46 +57,29 @@ | Приложение → Redis | env `REDIS_HOST`, `REDIS__*` | | Приложение → Vault | аннотации `vault.hashicorp.com/agent-inject*` | | Приложение → Zitadel | env `ZITADEL*`, `JWKS`, `OIDC`, секрет `jwt-public` | -| Приложение → Istio | `service:` в маршрутах `infrastructure/istio-config/d8-ugmk-prod/istio-config.yaml` (закомментированные не считаются) | +| Приложение → Istio | `service:` в маршрутах `infrastructure/istio-config/<контур>/istio-config.yaml` (закомментированные не считаются) | -Сканируются `*.yaml` в `apps//base/` и `apps//d8-ugmk-prod/`; документация (`*.md`) игнорируется, иначе прозаические упоминания переменных дают ложные срабатывания. +Сканируются `*.yaml` в `apps//base/` и `apps//<контур>/`; документация `*.md` игнорируется, иначе прозаические упоминания переменных дают ложные срабатывания. -## Что вскрылось при построении +`matrix.example.json` — снимок такой матрицы. Он содержит только имена приложений и флаги использования сервисов; идентификаторов контура в нём нет. -- **Camunda развёрнута, но потребителей нет.** `camunda-contour` 11.0.14 в контуре есть, наружу опубликованы Operate, Tasklist, Optimize, Identity, Keycloak — но ни одно приложение не сконфигурировано на Zeebe. Единственные ссылки на Camunda в `apps/` — оверлеи `cde/brusnika-prod` и `cde/brusnika-stage`. -- **Четыре приложения ни к чему не подключены**: `projects`, `prescriptions`, `cross-section`, `faas` — не используют ни один технологический сервис и не имеют маршрута в `istio-config`. В модели они есть, но ни в одно представление не попали. Для сравнения: в `yc-ecp` маршрут `/projects/static/` существует. -- **Наблюдаемости в контуре нет** — ни vmstack, ни prometheus-stack, ни openobserve, ни otel, в отличие от Brusnika и WB. Если мониторинг там есть, он живёт вне этого репозитория. -- **Сертификаты самоподписанные** — `ClusterIssuer sarex-selfsigned`, не Let's Encrypt. -- **Redis только у `pm`.** В остальных приложениях `REDIS_HOST` встречается лишь в текстах `CONFIGURATION.md`. -- **Ingress прибит к одному узлу** `um-sarex-k8s-frontend-01` через `nodeSelector` — единственная точка отказа внешнего трафика. -- **S3 внешний** (`s3.uralmine.com`), MinIO в контуре не разворачивается. +## Про формат файлов + +Нативный `.archimate` нигде не стандартизирован, и его словарь неочевиден: объекты схем сериализуются как ``, соединения — через атрибут `relationship`. Имена EClass из метамодели (`DiagramModelArchimateObject`, `archimateRelationship`) в XML **не используются** — на них легко попасться, модель тогда открывается с пустыми представлениями. + +Эталон для сверки — любая модель из [archimatetool/ArchiModels](https://github.com/archimatetool/ArchiModels), созданная самим Archi. + +`example-technology.xml` проверен на соответствие официальной схеме `archimate3_Diagram.xsd`. Если сторонний инструмент не берёт нативный формат, используйте его. Отличие обменной версии: вложенность узлов развёрнута в плоский список с абсолютными координатами (в обменном формате трактовка координат вложенного узла неоднозначна между реализациями). Визуально раскладка та же, но компоненты внутри кластера не будут его дочерними элементами. ## Скрипты -`scan_ugmk.py` — строит матрицу «приложение → технологический сервис» и кладёт в `matrix.json`. Полезен, когда состав контура изменился: перезапустить и сравнить. +Требуется Python 3, внешних зависимостей нет. На Windows запускать через `py`, а не `python`. -``` -py scan_ugmk.py matrix.json -``` +| Скрипт | Назначение | +|---|---| +| `scan_contour.py` | матрица «приложение → технологический сервис» по кластеру | +| `build_archimate.py` | сборка модели из профиля и матрицы | +| `to_open_exchange.py` | конвертация в обменный формат | +| `render_preview.py` | рендер представлений в HTML со встроенным SVG | -`build_archimate.py` — одноразовая генерация модели из `matrix.json`. - -``` -py build_archimate.py ugmk-technology.archimate -``` - -`to_open_exchange.py` — конвертирует нативный файл в обменный формат. Запускать после каждой правки модели, иначе `.xml` разъедется с `.archimate`. - -``` -py to_open_exchange.py ugmk-technology.archimate ugmk-technology.xml -``` - -`render_preview.py` — рисует все представления в один HTML со встроенным SVG. Пересобирать вместе с обменным форматом. - -``` -py render_preview.py ugmk-technology.archimate preview.html -``` - -**Внимание:** после того как модель отредактирована в Archi, источником истины становится `.archimate`, а повторный запуск генератора затрёт расстановку элементов. Пересобирать целиком имеет смысл только при перестройке модели с нуля. - -Требуется Python 3, внешних зависимостей нет. На этой машине запускать через `py`, а не `python`. +**Внимание:** после ручной правки модели в Archi источником истины становится `.archimate`, а повторный запуск `build_archimate.py` затрёт расстановку элементов. Производные форматы (`to_open_exchange.py`, `render_preview.py`) пересобирать после каждой правки, иначе они разъедутся с моделью. diff --git a/docs/architecture/build_archimate.py b/docs/architecture/build_archimate.py index e6cf31e..3cf354c 100644 --- a/docs/architecture/build_archimate.py +++ b/docs/architecture/build_archimate.py @@ -1,126 +1,100 @@ -"""Собирает .archimate (формат Archi 5) для технологического слоя контура УГМК.""" +"""Собирает .archimate (нативный формат Archi) из профиля контура и матрицы связей. + + py build_archimate.py <профиль.json> <матрица.json> <выход.archimate> + +Все имена, зависящие от конкретной инсталляции (кластер, узлы, домены, реестр), +приходят из профиля — в самом скрипте их нет. См. contour-profile.example.json. +""" import json, pathlib, sys, io from xml.sax.saxutils import escape, quoteattr sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") -HERE = pathlib.Path(__file__).parent -DATA = json.loads((HERE / "matrix.json").read_text(encoding="utf-8")) -OUT = pathlib.Path(sys.argv[1]) +if len(sys.argv) != 4: + sys.exit(__doc__) +PROFILE = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8")) +DATA = json.loads(pathlib.Path(sys.argv[2]).read_text(encoding="utf-8")) +OUT = pathlib.Path(sys.argv[3]) -elements = [] # (folder, id, type, name, doc) -relations = [] # (id, type, src, tgt) -_rc = [0] +elements, relations, _rc = [], [], [0] +REL_INDEX = {} def el(folder, eid, etype, name, doc=""): elements.append((folder, eid, etype, name, doc)); return eid -def rel(rtype, src, tgt): +def R(rtype, src, tgt): _rc[0] += 1 rid = f"rel-{_rc[0]:03d}" - relations.append((rid, rtype, src, tgt)); return rid - -REL_INDEX = {} -def R(rtype, src, tgt): - REL_INDEX[(rtype, src, tgt)] = rel(rtype, src, tgt); return REL_INDEX[(rtype, src, tgt)] + relations.append((rid, rtype, src, tgt)) + REL_INDEX[(rtype, src, tgt)] = rid + return rid # ---------------------------------------------------------------- технологический слой T = "technology" -nd_cluster = el(T, "nd-cluster", "Node", "Кластер d8-ugmk-prod (Deckhouse)", - "clusters/d8-ugmk-prod/kustomization.yaml. GitOps через FluxCD v2.") -nd_front = el(T, "nd-frontend", "Node", "um-sarex-k8s-frontend-01", - "Выделенный frontend-узел. К нему прибит ingress-gateway через nodeSelector: " - "clusters/d8-ugmk-prod/istio-ingress/ingress-controller.yaml") -nd_s3 = el(T, "nd-s3", "Node", "s3.uralmine.com — объектное хранилище УГМК", - "Внешнее по отношению к кластеру. MinIO в контуре не разворачивается.") -nd_nfs = el(T, "nd-nfs", "Node", "NFS-хранилище (StorageClass nfs01)", - "Единственный storageClass в контуре, 9 упоминаний в манифестах.") -nd_gitea = el(T, "nd-gitea", "Node", "Gitea 10.222.255.165:3000 (infra/iac)", - "Источник для Flux: clusters/d8-ugmk-prod/flux-system/gotk-sync.yaml") -nd_cr = el(T, "nd-cr", "Node", "cr.yandex/crp3ccidau046kdj8g9q", - "Реестр чартов (OCI) и образов контейнеров.") +nd_cluster = el(T, "nd-cluster", "Node", PROFILE["cluster"]["name"], PROFILE["cluster"].get("doc", "")) -SS = [ - ("ss-istio-gw", "Istio Ingress Gateway", "Deckhouse IngressIstioController «sarex», inlet NodePort 30080/30443. clusters/d8-ugmk-prod/istio-ingress/ingress-controller.yaml"), - ("ss-istio-mesh", "Istio Service Mesh", "chart istio-config-contour 0.1.0. Gateway, VirtualService, RequestAuthentication."), - ("ss-certmanager", "cert-manager", "ClusterIssuer sarex-selfsigned — сертификаты самоподписанные, не Let's Encrypt. Wildcard *.uralmine.com и *.sarex-k8s.uralmine.com."), - ("ss-zitadel", "Zitadel", "chart idp-contour 4.12.13, ns zitadel."), - ("ss-vault", "Vault + Agent Injector", "chart vault-contour 0.2.3, ns vault. Единственный механизм доставки секретов, путь secrets/data/apps//postgres."), - ("ss-postgres", "PostgreSQL", "chart postgresql-contour 17.0.7, ns postgresql. PVC 20Gi на nfs01."), - ("ss-redis", "Redis", "Разворачивается только в ns pm: apps/pm/d8-ugmk-prod/redis.yaml."), - ("ss-kafka", "Kafka", "chart kafka-contour 0.1.15, ns kafka."), - ("ss-rabbit", "RabbitMQ", "chart rabbitmq-contour 15.2.4, ns rabbitmq. Публикуется наружу на rabbitmq.sarex-k8s.uralmine.com."), - ("ss-camunda", "Camunda Platform", "chart camunda-contour 11.0.14, ns camunda. ВНИМАНИЕ: ни одно приложение контура на неё не сконфигурировано — потребителей нет."), -] -for sid, name, doc in SS: - el(T, sid, "SystemSoftware", name, doc); R("Composition", nd_cluster, sid) +for s in PROFILE["software"]: + el(T, s["id"], "SystemSoftware", s["name"], s.get("doc", "")) + R("Composition", nd_cluster, s["id"]) -CAMUNDA_SUB = ["Zeebe", "Zeebe Gateway", "Operate", "Tasklist", "Optimize", "Identity", "Connectors"] cam_ids = [] -for n in CAMUNDA_SUB: +for n in PROFILE.get("camunda_sub", []): cid = "ss-cam-" + n.lower().replace(" ", "-") - el(T, cid, "SystemSoftware", n, "infrastructure/camunda/d8-ugmk-prod/camunda.yaml") + el(T, cid, "SystemSoftware", n) R("Composition", "ss-camunda", cid); cam_ids.append(cid) -ar_charts = el(T, "ar-charts", "Artifact", "Helm-чарты *-contour (OCI)") -ar_images = el(T, "ar-images", "Artifact", "Образы контейнеров") -R("Composition", nd_cr, ar_charts); R("Composition", nd_cr, ar_images) +for x in PROFILE["external"]: + el(T, x["id"], "Node", x["name"], x.get("doc", "")) + for a in x.get("artifacts", []): + el(T, a["id"], "Artifact", a["name"]) + R("Composition", x["id"], a["id"]) + if x.get("serves") == "cluster": + R("Serving", x["id"], nd_cluster) + elif x.get("serves"): + R("Serving", x["id"], x["serves"]) -# технологические сервисы: id -> (имя, кто реализует, ключ в матрице) -TS = [ - ("ts-ingress", "Маршрутизация HTTPS-трафика", ["ss-istio-gw", "ss-istio-mesh"], "istio"), - ("ts-tls", "Выпуск TLS-сертификатов", ["ss-certmanager"], None), - ("ts-oidc", "Аутентификация OIDC", ["ss-zitadel"], "oidc"), - ("ts-secrets", "Управление секретами", ["ss-vault"], "vault"), - ("ts-rdbms", "Реляционное хранилище", ["ss-postgres"], "postgres"), - ("ts-cache", "Кэш", ["ss-redis"], "redis"), - ("ts-events", "Событийная шина", ["ss-kafka"], "kafka"), - ("ts-amqp", "Очередь сообщений AMQP", ["ss-rabbit"], "rabbitmq"), - ("ts-bpmn", "Оркестрация процессов BPMN", ["ss-camunda"], "camunda"), - ("ts-object", "Объектное хранилище", ["nd-s3"], "s3"), -] -for tid, name, realizers, _ in TS: - el(T, tid, "TechnologyService", name) - for r in realizers: - R("Realization", r, tid) +fn = PROFILE["frontend_node"] +el(T, fn["id"], "Node", fn["name"], fn.get("doc", "")) +R("Assignment", fn["id"], fn["assigned_to"]) -R("Assignment", nd_front, "ss-istio-gw") -R("Serving", "ts-tls", "ss-istio-gw") -R("Serving", nd_nfs, "ss-postgres") -R("Serving", nd_gitea, nd_cluster) -R("Serving", nd_cr, nd_cluster) +for ts in PROFILE["services"]: + el(T, ts["id"], "TechnologyService", ts["name"]) + for r in ts["realizers"]: + R("Realization", r, ts["id"]) + if ts.get("serves_software"): + R("Serving", ts["id"], ts["serves_software"]) # ---------------------------------------------------------------- прикладной слой app_id = {a: "ac-" + a for a in DATA["apps"]} for a in DATA["apps"]: - el("application", app_id[a], "ApplicationComponent", a, f"apps/{a}/d8-ugmk-prod/") + el("application", app_id[a], "ApplicationComponent", a, f"apps/{a}//") consumers = {} -for tid, _, _, key in TS: - if key is None: - consumers[tid] = []; continue +for ts in PROFILE["services"]: + key = ts.get("key") + if not key: + consumers[ts["id"]] = []; continue if key == "istio": lst = [a for a in DATA["apps"] if a in DATA["exposed"]] else: - lst = [a for a in DATA["apps"] if DATA["matrix"][a][key]] - consumers[tid] = lst + lst = [a for a in DATA["apps"] if DATA["matrix"][a].get(key)] + consumers[ts["id"]] = lst for a in lst: - R("Serving", tid, app_id[a]) + R("Serving", ts["id"], app_id[a]) # ---------------------------------------------------------------- представления -views = [] +views, _oc = [], [0] def view(name, doc=""): - v = {"id": f"view-{len(views)+1}", "name": name, "doc": doc, "objects": [], "conns": []} + v = {"id": f"view-{len(views)+1}", "name": name, "doc": doc, "objects": []} views.append(v); return v -_oc = [0] def obj(v, eid, x, y, w, h, parent=None): _oc[0] += 1 o = {"id": f"do-{_oc[0]:04d}", "el": eid, "b": (x, y, w, h), "children": [], "src": [], "tgt": []} (parent["children"] if parent else v["objects"]).append(o) return o -def conn(v, rtype, so, to): +def conn(rtype, so, to): key = (rtype, so["el"], to["el"]) if key not in REL_INDEX: raise KeyError(f"нет отношения в модели: {key}") @@ -129,61 +103,59 @@ def conn(v, rtype, so, to): so["src"].append({"id": cid, "rel": REL_INDEX[key], "tgt": to["id"]}) to["tgt"].append(cid) -# --- T1: инфраструктура без приложений -v1 = view("T1. Технологический слой — контур УГМК", - "Что развёрнуто в кластере d8-ugmk-prod и какие технологические сервисы это даёт. " - "Приложения-потребители вынесены в отдельные представления T2–T9.") -o_ts = {} -for i, (tid, _, _, _) in enumerate(TS): - o_ts[tid] = obj(v1, tid, 20 + i * 165, 30, 150, 60) +SERVICES = PROFILE["services"] +SW = {s["id"]: s for s in PROFILE["software"]} + +v1 = view("T1. Технологический слой", + "Что развёрнуто в кластере и какие технологические сервисы это даёт. " + "Приложения-потребители вынесены в отдельные представления T2 и далее.") +o_ts = {ts["id"]: obj(v1, ts["id"], 20 + i * 165, 30, 150, 60) for i, ts in enumerate(SERVICES)} o_cluster = obj(v1, nd_cluster, 20, 140, 1270, 400) -o_ss = {} -row1 = ["ss-istio-gw", "ss-istio-mesh", "ss-certmanager", "ss-zitadel", "ss-vault"] -for i, sid in enumerate(row1): +o_ss, ids = {}, [s["id"] for s in PROFILE["software"]] +for i, sid in enumerate(ids[:5]): o_ss[sid] = obj(v1, sid, 20 + i * 240, 45, 220, 70, o_cluster) -row2 = ["ss-postgres", "ss-redis", "ss-kafka", "ss-rabbit"] -for i, sid in enumerate(row2): +for i, sid in enumerate(ids[5:9]): o_ss[sid] = obj(v1, sid, 20 + i * 240, 145, 220, 70, o_cluster) -o_ss["ss-camunda"] = obj(v1, "ss-camunda", 20, 245, 1230, 130, o_cluster) -for i, cid in enumerate(cam_ids): - obj(v1, cid, 10 + i * 174, 50, 162, 60, o_ss["ss-camunda"]) +if len(ids) > 9: + o_ss[ids[9]] = obj(v1, ids[9], 20, 245, 1230, 130, o_cluster) + for i, cid in enumerate(cam_ids): + obj(v1, cid, 10 + i * 174, 50, 162, 60, o_ss[ids[9]]) -o_s3 = obj(v1, nd_s3, 1320, 140, 330, 70) -o_nfs = obj(v1, nd_nfs, 1320, 230, 330, 70) -o_gitea = obj(v1, nd_gitea, 1320, 320, 330, 70) -o_cr = obj(v1, nd_cr, 1320, 410, 330, 130) -obj(v1, ar_charts, 15, 50, 145, 60, o_cr) -obj(v1, ar_images, 170, 50, 145, 60, o_cr) -o_front = obj(v1, nd_front, 20, 570, 300, 60) +o_ext = {} +for i, x in enumerate(PROFILE["external"]): + h = 130 if x.get("artifacts") else 70 + o_ext[x["id"]] = obj(v1, x["id"], 1320, 140 + i * 90, 330, h) + for j, a in enumerate(x.get("artifacts", [])): + obj(v1, a["id"], 15 + j * 155, 50, 145, 60, o_ext[x["id"]]) +o_front = obj(v1, fn["id"], 20, 570, 300, 60) -for tid, _, realizers, _ in TS: - for r in realizers: - conn(v1, "Realization", o_ss.get(r) or o_s3, o_ts[tid]) -conn(v1, "Assignment", o_front, o_ss["ss-istio-gw"]) -conn(v1, "Serving", o_ts["ts-tls"], o_ss["ss-istio-gw"]) -conn(v1, "Serving", o_nfs, o_ss["ss-postgres"]) -conn(v1, "Serving", o_gitea, o_cluster) -conn(v1, "Serving", o_cr, o_cluster) +for ts in SERVICES: + for r in ts["realizers"]: + conn("Realization", o_ss.get(r) or o_ext[r], o_ts[ts["id"]]) + if ts.get("serves_software"): + conn("Serving", o_ts[ts["id"]], o_ss[ts["serves_software"]]) +conn("Assignment", o_front, o_ss[fn["assigned_to"]]) +for x in PROFILE["external"]: + if x.get("serves") == "cluster": + conn("Serving", o_ext[x["id"]], o_cluster) + elif x.get("serves"): + conn("Serving", o_ext[x["id"]], o_ss[x["serves"]]) -# --- T2..T9: потребители каждого сервиса PER_ROW = 6 -for tid, tname, realizers, key in TS: - lst = consumers[tid] +for ts in SERVICES: + lst = consumers[ts["id"]] if not lst: continue - v = view(f"T{len(views)+1}. Потребители: {tname}", - f"{len(lst)} из {len(DATA['apps'])} приложений контура. Связи выведены из манифестов " - f"apps/*/base и apps/*/d8-ugmk-prod.") - ots = obj(v, tid, 20, 20, 260, 60) - oss = [obj(v, r, 320 + i * 280, 20, 260, 60) for i, r in enumerate(realizers)] - for o in oss: - conn(v, "Realization", o, ots) - oapps = [] + v = view(f"T{len(views)+1}. Потребители: {ts['name']}", + f"{len(lst)} из {len(DATA['apps'])} приложений контура. " + f"Связи выведены из манифестов приложений.") + ots = obj(v, ts["id"], 20, 20, 260, 60) + for i, r in enumerate(ts["realizers"]): + conn("Realization", obj(v, r, 320 + i * 280, 20, 260, 60), ots) for i, a in enumerate(lst): - oapps.append(obj(v, app_id[a], 20 + (i % PER_ROW) * 190, 150 + (i // PER_ROW) * 80, 170, 55)) - for oa in oapps: - conn(v, "Serving", ots, oa) + conn("Serving", ots, + obj(v, app_id[a], 20 + (i % PER_ROW) * 190, 150 + (i // PER_ROW) * 80, 170, 55)) # ---------------------------------------------------------------- сериализация FOLDERS = [("Strategy", "strategy"), ("Business", "business"), ("Application", "application"), @@ -192,12 +164,12 @@ FOLDERS = [("Strategy", "strategy"), ("Business", "business"), ("Application", " ("Relations", "relations"), ("Views", "diagrams")] def emit_obj(o, ind): + # Имена тегов — как их пишет сам Archi: , + # соединение через атрибут relationship. Имена EClass из метамодели + # (DiagramModelArchimateObject) в XML не используются. p = " " * ind tgt = f' targetConnections={quoteattr(" ".join(o["tgt"]))}' if o["tgt"] else "" x, y, w, h = o["b"] - # Порядок и имена — как их пишет сам Archi: , затем bounds, - # sourceConnection, вложенные . Имена EClass в метамодели - # (DiagramModelArchimateObject) в XML не используются. s = f'{p}\n' s += f'{p} \n' for c in o["src"]: @@ -205,16 +177,13 @@ def emit_obj(o, ind): f'source="{o["id"]}" target="{c["tgt"]}" relationship="{c["rel"]}"/>\n') for c in o["children"]: s += emit_obj(c, ind + 2) - s += f'{p}\n' - return s + return s + f'{p}\n' out = ['', '', - ' Технологический слой контура d8-ugmk-prod, выведенный из репозитория infra/iac. ' - 'Прикладной слой присутствует только как потребители технологических сервисов — ' - 'межсервисные связи приложений моделируются отдельно.'] + f'name={quoteattr(PROFILE["model"]["name"])} id="model-contour" version="5.0.0">', + f' {escape(PROFILE["model"]["purpose"])}'] for fname, ftype in FOLDERS: inner = "" @@ -224,8 +193,7 @@ for fname, ftype in FOLDERS: continue if doc: inner += (f' \n' - f' {escape(doc)}\n' - f' \n') + f' {escape(doc)}\n \n') else: inner += f' \n' elif ftype == "relations": @@ -240,16 +208,13 @@ for fname, ftype in FOLDERS: for o in v["objects"]: inner += emit_obj(o, 6) inner += ' \n' - if inner: - out.append(f' \n{inner} ') - else: - out.append(f' ') + out.append(f' \n{inner} ' + if inner else f' ') out.append('') -OUT.write_text("\n".join(out) + "\n", encoding="utf-8") +OUT.write_text("\n".join(out) + "\n", encoding="utf-8", newline="\n") print(f"записано: {OUT}") print(f"элементов: {len(elements)} отношений: {len(relations)} представлений: {len(views)}") for v in views: - n_obj = sum(1 + len(o["children"]) + sum(len(c["children"]) for c in o["children"]) for o in v["objects"]) - print(f" {v['name']} ({n_obj} объектов)") + print(f" {v['name']}") diff --git a/docs/architecture/contour-profile.example.json b/docs/architecture/contour-profile.example.json new file mode 100644 index 0000000..e2e7086 --- /dev/null +++ b/docs/architecture/contour-profile.example.json @@ -0,0 +1,59 @@ +{ + "_комментарий": "Профиль контура: всё, что зависит от конкретной инсталляции. Значения здесь — ПРИМЕР. Для реального контура скопируйте файл, подставьте настоящие имена и держите копию вне репозитория.", + + "model": { + "name": "СДЕ Sarex — типовой контур (пример)", + "purpose": "Технологический слой типового контура. Имена кластера, узлов, доменов и реестра заменены на примеры. Прикладной слой присутствует только как потребители технологических сервисов — межсервисные связи приложений моделируются отдельно." + }, + + "cluster": { + "name": "Кластер k8s-prod", + "doc": "clusters//kustomization.yaml. Развёртывание через FluxCD v2 (GitOps)." + }, + + "software": [ + { "id": "ss-istio-gw", "name": "Istio Ingress Gateway", "doc": "Ingress-контроллер, публикует контур наружу через NodePort." }, + { "id": "ss-istio-mesh", "name": "Istio Service Mesh", "doc": "chart istio-config-contour. Gateway, VirtualService, RequestAuthentication." }, + { "id": "ss-certmanager", "name": "cert-manager", "doc": "Выпуск сертификатов. В примере — самоподписанный ClusterIssuer, wildcard на домен контура." }, + { "id": "ss-zitadel", "name": "Zitadel", "doc": "chart idp-contour, ns zitadel. Основной поставщик идентичности." }, + { "id": "ss-vault", "name": "Vault + Agent Injector", "doc": "chart vault-contour, ns vault. Единственный механизм доставки секретов, путь secrets/data/apps//postgres." }, + { "id": "ss-postgres", "name": "PostgreSQL", "doc": "chart postgresql-contour, ns postgresql. PVC на сетевом хранилище." }, + { "id": "ss-redis", "name": "Redis", "doc": "Разворачивается не во всех контурах и не для всех приложений." }, + { "id": "ss-kafka", "name": "Kafka", "doc": "chart kafka-contour, ns kafka." }, + { "id": "ss-rabbit", "name": "RabbitMQ", "doc": "chart rabbitmq-contour, ns rabbitmq." }, + { "id": "ss-camunda", "name": "Camunda Platform", "doc": "chart camunda-contour, ns camunda. В этом примере потребителей не показано." } + ], + + "camunda_sub": ["Zeebe", "Zeebe Gateway", "Operate", "Tasklist", "Optimize", "Identity", "Connectors"], + + "external": [ + { "id": "nd-s3", "name": "s3.example.com — объектное хранилище", "doc": "Внешнее по отношению к кластеру.", "realizes": "ts-object" }, + { "id": "nd-nfs", "name": "Сетевое хранилище (StorageClass)", "doc": "Класс хранения для PVC.", "serves": "ss-postgres" }, + { "id": "nd-gitea", "name": "Git-сервер контура", "doc": "Источник манифестов для Flux.", "serves": "cluster" }, + { "id": "nd-cr", "name": "registry.example.com", "doc": "Реестр Helm-чартов (OCI) и образов контейнеров.", "serves": "cluster", + "artifacts": [ + { "id": "ar-charts", "name": "Helm-чарты (OCI)" }, + { "id": "ar-images", "name": "Образы контейнеров" } + ] } + ], + + "frontend_node": { + "id": "nd-frontend", + "name": "frontend-node-01", + "doc": "Выделенный узел, к которому ingress-gateway прибит через nodeSelector.", + "assigned_to": "ss-istio-gw" + }, + + "services": [ + { "id": "ts-ingress", "name": "Маршрутизация HTTPS-трафика", "realizers": ["ss-istio-gw", "ss-istio-mesh"], "key": "istio" }, + { "id": "ts-tls", "name": "Выпуск TLS-сертификатов", "realizers": ["ss-certmanager"], "key": null, "serves_software": "ss-istio-gw" }, + { "id": "ts-oidc", "name": "Аутентификация OIDC", "realizers": ["ss-zitadel"], "key": "oidc" }, + { "id": "ts-secrets", "name": "Управление секретами", "realizers": ["ss-vault"], "key": "vault" }, + { "id": "ts-rdbms", "name": "Реляционное хранилище", "realizers": ["ss-postgres"], "key": "postgres" }, + { "id": "ts-cache", "name": "Кэш", "realizers": ["ss-redis"], "key": "redis" }, + { "id": "ts-events", "name": "Событийная шина", "realizers": ["ss-kafka"], "key": "kafka" }, + { "id": "ts-amqp", "name": "Очередь сообщений AMQP", "realizers": ["ss-rabbit"], "key": "rabbitmq" }, + { "id": "ts-bpmn", "name": "Оркестрация процессов BPMN", "realizers": ["ss-camunda"], "key": "camunda" }, + { "id": "ts-object", "name": "Объектное хранилище", "realizers": ["nd-s3"], "key": "s3" } + ] +} diff --git a/docs/architecture/ugmk-technology.archimate b/docs/architecture/example-technology.archimate similarity index 66% rename from docs/architecture/ugmk-technology.archimate rename to docs/architecture/example-technology.archimate index 6c403e9..8bfef96 100644 --- a/docs/architecture/ugmk-technology.archimate +++ b/docs/architecture/example-technology.archimate @@ -1,187 +1,173 @@ - - Технологический слой контура d8-ugmk-prod, выведенный из репозитория infra/iac. Прикладной слой присутствует только как потребители технологических сервисов — межсервисные связи приложений моделируются отдельно. + + Технологический слой типового контура. Имена кластера, узлов, доменов и реестра заменены на примеры. Прикладной слой присутствует только как потребители технологических сервисов — межсервисные связи приложений моделируются отдельно. - apps/attachments/d8-ugmk-prod/ + apps/attachments/<contour>/ - apps/auth-flow/d8-ugmk-prod/ + apps/auth-flow/<contour>/ - apps/bim/d8-ugmk-prod/ + apps/bim/<contour>/ - apps/cde/d8-ugmk-prod/ + apps/cde/<contour>/ - apps/checklists/d8-ugmk-prod/ + apps/checklists/<contour>/ - apps/comparisons/d8-ugmk-prod/ + apps/comparisons/<contour>/ - apps/contracts/d8-ugmk-prod/ + apps/contracts/<contour>/ - apps/control-interface/d8-ugmk-prod/ + apps/control-interface/<contour>/ - apps/cross-section/d8-ugmk-prod/ + apps/cross-section/<contour>/ - apps/django/d8-ugmk-prod/ + apps/django/<contour>/ - apps/document-link/d8-ugmk-prod/ + apps/document-link/<contour>/ - apps/documentations/d8-ugmk-prod/ + apps/documentations/<contour>/ - apps/drawings/d8-ugmk-prod/ + apps/drawings/<contour>/ - apps/eav/d8-ugmk-prod/ + apps/eav/<contour>/ - apps/faas/d8-ugmk-prod/ + apps/faas/<contour>/ - apps/flows/d8-ugmk-prod/ + apps/flows/<contour>/ - apps/iam/d8-ugmk-prod/ + apps/iam/<contour>/ - apps/inspections/d8-ugmk-prod/ + apps/inspections/<contour>/ - apps/issues/d8-ugmk-prod/ + apps/issues/<contour>/ - apps/mapper/d8-ugmk-prod/ + apps/mapper/<contour>/ - apps/measurements/d8-ugmk-prod/ + apps/measurements/<contour>/ - apps/message-hub/d8-ugmk-prod/ + apps/message-hub/<contour>/ - apps/notes/d8-ugmk-prod/ + apps/notes/<contour>/ - apps/pm/d8-ugmk-prod/ + apps/pm/<contour>/ - apps/prescriptions/d8-ugmk-prod/ + apps/prescriptions/<contour>/ - apps/processing/d8-ugmk-prod/ + apps/processing/<contour>/ - apps/projects/d8-ugmk-prod/ + apps/projects/<contour>/ - apps/remarks/d8-ugmk-prod/ + apps/remarks/<contour>/ - apps/reviews/d8-ugmk-prod/ + apps/reviews/<contour>/ - apps/rfi/d8-ugmk-prod/ + apps/rfi/<contour>/ - apps/stamp-verification/d8-ugmk-prod/ + apps/stamp-verification/<contour>/ - apps/subscriptions/d8-ugmk-prod/ + apps/subscriptions/<contour>/ - apps/system-log/d8-ugmk-prod/ + apps/system-log/<contour>/ - apps/transmittal/d8-ugmk-prod/ + apps/transmittal/<contour>/ - apps/workspaces/d8-ugmk-prod/ + apps/workspaces/<contour>/ - - clusters/d8-ugmk-prod/kustomization.yaml. GitOps через FluxCD v2. - - - Выделенный frontend-узел. К нему прибит ingress-gateway через nodeSelector: clusters/d8-ugmk-prod/istio-ingress/ingress-controller.yaml - - - Внешнее по отношению к кластеру. MinIO в контуре не разворачивается. - - - Единственный storageClass в контуре, 9 упоминаний в манифестах. - - - Источник для Flux: clusters/d8-ugmk-prod/flux-system/gotk-sync.yaml - - - Реестр чартов (OCI) и образов контейнеров. + + clusters/<contour>/kustomization.yaml. Развёртывание через FluxCD v2 (GitOps). - Deckhouse IngressIstioController «sarex», inlet NodePort 30080/30443. clusters/d8-ugmk-prod/istio-ingress/ingress-controller.yaml + Ingress-контроллер, публикует контур наружу через NodePort. - chart istio-config-contour 0.1.0. Gateway, VirtualService, RequestAuthentication. + chart istio-config-contour. Gateway, VirtualService, RequestAuthentication. - ClusterIssuer sarex-selfsigned — сертификаты самоподписанные, не Let's Encrypt. Wildcard *.uralmine.com и *.sarex-k8s.uralmine.com. + Выпуск сертификатов. В примере — самоподписанный ClusterIssuer, wildcard на домен контура. - chart idp-contour 4.12.13, ns zitadel. + chart idp-contour, ns zitadel. Основной поставщик идентичности. - chart vault-contour 0.2.3, ns vault. Единственный механизм доставки секретов, путь secrets/data/apps/<app>/postgres. + chart vault-contour, ns vault. Единственный механизм доставки секретов, путь secrets/data/apps/<app>/postgres. - chart postgresql-contour 17.0.7, ns postgresql. PVC 20Gi на nfs01. + chart postgresql-contour, ns postgresql. PVC на сетевом хранилище. - Разворачивается только в ns pm: apps/pm/d8-ugmk-prod/redis.yaml. + Разворачивается не во всех контурах и не для всех приложений. - chart kafka-contour 0.1.15, ns kafka. + chart kafka-contour, ns kafka. - chart rabbitmq-contour 15.2.4, ns rabbitmq. Публикуется наружу на rabbitmq.sarex-k8s.uralmine.com. + chart rabbitmq-contour, ns rabbitmq. - chart camunda-contour 11.0.14, ns camunda. ВНИМАНИЕ: ни одно приложение контура на неё не сконфигурировано — потребителей нет. + chart camunda-contour, ns camunda. В этом примере потребителей не показано. - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml + + + + + + + + + Внешнее по отношению к кластеру. - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml + + Класс хранения для PVC. - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml + + Источник манифестов для Flux. - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml + + Реестр Helm-чартов (OCI) и образов контейнеров. - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml - - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml - - - infrastructure/camunda/d8-ugmk-prod/camunda.yaml - - + + + Выделенный узел, к которому ingress-gateway прибит через nodeSelector. + @@ -214,24 +200,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -352,80 +338,80 @@ - - Что развёрнуто в кластере d8-ugmk-prod и какие технологические сервисы это даёт. Приложения-потребители вынесены в отдельные представления T2–T9. + + Что развёрнуто в кластере и какие технологические сервисы это даёт. Приложения-потребители вынесены в отдельные представления T2 и далее. - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -451,19 +437,19 @@ - + - + - + - + @@ -473,562 +459,562 @@ - + - 21 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. - + 21 из 35 приложений контура. Связи выведены из манифестов приложений. + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 10 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 10 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - 25 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 25 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 23 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 23 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 3 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 3 из 35 приложений контура. Связи выведены из манифестов приложений. - - - + + + - + - + - + - + - 12 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 12 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - 11 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 11 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - 13 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 13 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/architecture/ugmk-technology.xml b/docs/architecture/example-technology.xml similarity index 58% rename from docs/architecture/ugmk-technology.xml rename to docs/architecture/example-technology.xml index ed3c10c..7934469 100644 --- a/docs/architecture/ugmk-technology.xml +++ b/docs/architecture/example-technology.xml @@ -1,248 +1,241 @@ - СДЕ Sarex — контур УГМК - Технологический слой контура d8-ugmk-prod, выведенный из репозитория infra/iac. Прикладной слой присутствует только как потребители технологических сервисов — межсервисные связи приложений моделируются отдельно. + identifier="model-contour"> + СДЕ Sarex — типовой контур (пример) + Технологический слой типового контура. Имена кластера, узлов, доменов и реестра заменены на примеры. Прикладной слой присутствует только как потребители технологических сервисов — межсервисные связи приложений моделируются отдельно. attachments - apps/attachments/d8-ugmk-prod/ + apps/attachments/<contour>/ auth-flow - apps/auth-flow/d8-ugmk-prod/ + apps/auth-flow/<contour>/ bim - apps/bim/d8-ugmk-prod/ + apps/bim/<contour>/ cde - apps/cde/d8-ugmk-prod/ + apps/cde/<contour>/ checklists - apps/checklists/d8-ugmk-prod/ + apps/checklists/<contour>/ comparisons - apps/comparisons/d8-ugmk-prod/ + apps/comparisons/<contour>/ contracts - apps/contracts/d8-ugmk-prod/ + apps/contracts/<contour>/ control-interface - apps/control-interface/d8-ugmk-prod/ + apps/control-interface/<contour>/ cross-section - apps/cross-section/d8-ugmk-prod/ + apps/cross-section/<contour>/ django - apps/django/d8-ugmk-prod/ + apps/django/<contour>/ document-link - apps/document-link/d8-ugmk-prod/ + apps/document-link/<contour>/ documentations - apps/documentations/d8-ugmk-prod/ + apps/documentations/<contour>/ drawings - apps/drawings/d8-ugmk-prod/ + apps/drawings/<contour>/ eav - apps/eav/d8-ugmk-prod/ + apps/eav/<contour>/ faas - apps/faas/d8-ugmk-prod/ + apps/faas/<contour>/ flows - apps/flows/d8-ugmk-prod/ + apps/flows/<contour>/ iam - apps/iam/d8-ugmk-prod/ + apps/iam/<contour>/ inspections - apps/inspections/d8-ugmk-prod/ + apps/inspections/<contour>/ issues - apps/issues/d8-ugmk-prod/ + apps/issues/<contour>/ mapper - apps/mapper/d8-ugmk-prod/ + apps/mapper/<contour>/ measurements - apps/measurements/d8-ugmk-prod/ + apps/measurements/<contour>/ message-hub - apps/message-hub/d8-ugmk-prod/ + apps/message-hub/<contour>/ notes - apps/notes/d8-ugmk-prod/ + apps/notes/<contour>/ pm - apps/pm/d8-ugmk-prod/ + apps/pm/<contour>/ prescriptions - apps/prescriptions/d8-ugmk-prod/ + apps/prescriptions/<contour>/ processing - apps/processing/d8-ugmk-prod/ + apps/processing/<contour>/ projects - apps/projects/d8-ugmk-prod/ + apps/projects/<contour>/ remarks - apps/remarks/d8-ugmk-prod/ + apps/remarks/<contour>/ reviews - apps/reviews/d8-ugmk-prod/ + apps/reviews/<contour>/ rfi - apps/rfi/d8-ugmk-prod/ + apps/rfi/<contour>/ stamp-verification - apps/stamp-verification/d8-ugmk-prod/ + apps/stamp-verification/<contour>/ subscriptions - apps/subscriptions/d8-ugmk-prod/ + apps/subscriptions/<contour>/ system-log - apps/system-log/d8-ugmk-prod/ + apps/system-log/<contour>/ transmittal - apps/transmittal/d8-ugmk-prod/ + apps/transmittal/<contour>/ workspaces - apps/workspaces/d8-ugmk-prod/ + apps/workspaces/<contour>/ - Кластер d8-ugmk-prod (Deckhouse) - clusters/d8-ugmk-prod/kustomization.yaml. GitOps через FluxCD v2. - - - um-sarex-k8s-frontend-01 - Выделенный frontend-узел. К нему прибит ingress-gateway через nodeSelector: clusters/d8-ugmk-prod/istio-ingress/ingress-controller.yaml - - - s3.uralmine.com — объектное хранилище УГМК - Внешнее по отношению к кластеру. MinIO в контуре не разворачивается. - - - NFS-хранилище (StorageClass nfs01) - Единственный storageClass в контуре, 9 упоминаний в манифестах. - - - Gitea 10.222.255.165:3000 (infra/iac) - Источник для Flux: clusters/d8-ugmk-prod/flux-system/gotk-sync.yaml - - - cr.yandex/crp3ccidau046kdj8g9q - Реестр чартов (OCI) и образов контейнеров. + Кластер k8s-prod + clusters/<contour>/kustomization.yaml. Развёртывание через FluxCD v2 (GitOps). Istio Ingress Gateway - Deckhouse IngressIstioController «sarex», inlet NodePort 30080/30443. clusters/d8-ugmk-prod/istio-ingress/ingress-controller.yaml + Ingress-контроллер, публикует контур наружу через NodePort. Istio Service Mesh - chart istio-config-contour 0.1.0. Gateway, VirtualService, RequestAuthentication. + chart istio-config-contour. Gateway, VirtualService, RequestAuthentication. cert-manager - ClusterIssuer sarex-selfsigned — сертификаты самоподписанные, не Let's Encrypt. Wildcard *.uralmine.com и *.sarex-k8s.uralmine.com. + Выпуск сертификатов. В примере — самоподписанный ClusterIssuer, wildcard на домен контура. Zitadel - chart idp-contour 4.12.13, ns zitadel. + chart idp-contour, ns zitadel. Основной поставщик идентичности. Vault + Agent Injector - chart vault-contour 0.2.3, ns vault. Единственный механизм доставки секретов, путь secrets/data/apps/<app>/postgres. + chart vault-contour, ns vault. Единственный механизм доставки секретов, путь secrets/data/apps/<app>/postgres. PostgreSQL - chart postgresql-contour 17.0.7, ns postgresql. PVC 20Gi на nfs01. + chart postgresql-contour, ns postgresql. PVC на сетевом хранилище. Redis - Разворачивается только в ns pm: apps/pm/d8-ugmk-prod/redis.yaml. + Разворачивается не во всех контурах и не для всех приложений. Kafka - chart kafka-contour 0.1.15, ns kafka. + chart kafka-contour, ns kafka. RabbitMQ - chart rabbitmq-contour 15.2.4, ns rabbitmq. Публикуется наружу на rabbitmq.sarex-k8s.uralmine.com. + chart rabbitmq-contour, ns rabbitmq. Camunda Platform - chart camunda-contour 11.0.14, ns camunda. ВНИМАНИЕ: ни одно приложение контура на неё не сконфигурировано — потребителей нет. + chart camunda-contour, ns camunda. В этом примере потребителей не показано. Zeebe - infrastructure/camunda/d8-ugmk-prod/camunda.yaml Zeebe Gateway - infrastructure/camunda/d8-ugmk-prod/camunda.yaml Operate - infrastructure/camunda/d8-ugmk-prod/camunda.yaml Tasklist - infrastructure/camunda/d8-ugmk-prod/camunda.yaml Optimize - infrastructure/camunda/d8-ugmk-prod/camunda.yaml Identity - infrastructure/camunda/d8-ugmk-prod/camunda.yaml Connectors - infrastructure/camunda/d8-ugmk-prod/camunda.yaml + + + s3.example.com — объектное хранилище + Внешнее по отношению к кластеру. + + + Сетевое хранилище (StorageClass) + Класс хранения для PVC. + + + Git-сервер контура + Источник манифестов для Flux. + + + registry.example.com + Реестр Helm-чартов (OCI) и образов контейнеров. - Helm-чарты *-contour (OCI) + Helm-чарты (OCI) Образы контейнеров + + frontend-node-01 + Выделенный узел, к которому ingress-gateway прибит через nodeSelector. + Маршрутизация HTTPS-трафика @@ -292,24 +285,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -432,8 +425,8 @@ - T1. Технологический слой — контур УГМК - Что развёрнуто в кластере d8-ugmk-prod и какие технологические сервисы это даёт. Приложения-потребители вынесены в отдельные представления T2–T9. + T1. Технологический слой + Что развёрнуто в кластере и какие технологические сервисы это даёт. Приложения-потребители вынесены в отдельные представления T2 и далее. @@ -469,316 +462,316 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + T2. Потребители: Маршрутизация HTTPS-трафика - 21 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 21 из 35 приложений контура. Связи выведены из манифестов приложений. - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T3. Потребители: Аутентификация OIDC - 10 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 10 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + T4. Потребители: Управление секретами - 25 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 25 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T5. Потребители: Реляционное хранилище - 23 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 23 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T6. Потребители: Кэш - 3 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 3 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - + + + + + + T7. Потребители: Событийная шина - 12 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 12 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + T8. Потребители: Очередь сообщений AMQP - 11 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 11 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + T9. Потребители: Объектное хранилище - 13 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod. + 13 из 35 приложений контура. Связи выведены из манифестов приложений. - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/architecture/matrix.json b/docs/architecture/matrix.example.json similarity index 100% rename from docs/architecture/matrix.json rename to docs/architecture/matrix.example.json diff --git a/docs/architecture/preview.html b/docs/architecture/preview.html index 734be71..033f1b3 100644 --- a/docs/architecture/preview.html +++ b/docs/architecture/preview.html @@ -1,6 +1,6 @@ -СДЕ Sarex — контур УГМК +СДЕ Sarex — типовой контур (пример) -

СДЕ Sarex — контур УГМК

+

СДЕ Sarex — типовой контур (пример)

Автогенерация из ugmk-technology.archimate. Зелёный — технологический слой, голубой — прикладной. Пунктир с полым треугольником — realization, стрелка — serving, ромб — composition, линия с шариком — assignment.

-

T1. Технологический слой — контур УГМК

Что развёрнуто в кластере d8-ugmk-prod и какие технологические сервисы это даёт. Приложения-потребители вынесены в отдельные представления T2–T9.

+

T1. Технологический слой

Что развёрнуто в кластере и какие технологические сервисы это даёт. Приложения-потребители вынесены в отдельные представления T2 и далее.

@@ -28,7 +28,7 @@ -Кластер d8-ugmk-prod (Deckhouse)Camunda Platformcr.yandex/crp3ccidau046kdj8g9qs3.uralmine.com — объектное хранилище УГМКNFS-хранилище (StorageClass nfs01)Gitea 10.222.255.165:3000 (infra/iac)um-sarex-k8s-frontend-01Istio Ingress GatewayIstio Service Meshcert-managerZitadelVault + Agent InjectorPostgreSQLRedisKafkaRabbitMQZeebeZeebe GatewayOperateTasklistOptimizeIdentityConnectorsМаршрутизацияHTTPS-трафикаВыпускTLS-сертификатовАутентификация OIDCУправление секретамиРеляционное хранилищеКэшСобытийная шинаОчередь сообщенийAMQPОркестрация процессовBPMNОбъектное хранилищеHelm-чарты *-contour(OCI)Образы контейнеров

T2. Потребители: Маршрутизация HTTPS-трафика

21 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Кластер k8s-prodCamunda Platformregistry.example.coms3.example.com — объектное хранилищеСетевое хранилище (StorageClass)Git-сервер контураfrontend-node-01Istio Ingress GatewayIstio Service Meshcert-managerZitadelVault + Agent InjectorPostgreSQLRedisKafkaRabbitMQZeebeZeebe GatewayOperateTasklistOptimizeIdentityConnectorsМаршрутизацияHTTPS-трафикаВыпускTLS-сертификатовАутентификация OIDCУправление секретамиРеляционное хранилищеКэшСобытийная шинаОчередь сообщенийAMQPОркестрация процессовBPMNОбъектное хранилищеHelm-чарты (OCI)Образы контейнеров

T2. Потребители: Маршрутизация HTTPS-трафика

21 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -40,7 +40,7 @@ -Маршрутизация HTTPS-трафикаIstio Ingress GatewayIstio Service Meshauth-flowbimcdechecklistscontrol-interfacedjangodocument-linkdocumentationseavflowsiaminspectionsissuespmprocessingremarksreviewsrfistamp-verificationtransmittalworkspaces

T3. Потребители: Аутентификация OIDC

10 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Маршрутизация HTTPS-трафикаIstio Ingress GatewayIstio Service Meshauth-flowbimcdechecklistscontrol-interfacedjangodocument-linkdocumentationseavflowsiaminspectionsissuespmprocessingremarksreviewsrfistamp-verificationtransmittalworkspaces

T3. Потребители: Аутентификация OIDC

10 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -52,7 +52,7 @@ -Аутентификация OIDCZitadelchecklistscomparisonscontractsdjangodocumentationseavflowsiamissuesprocessing

T4. Потребители: Управление секретами

25 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Аутентификация OIDCZitadelchecklistscomparisonscontractsdjangodocumentationseavflowsiamissuesprocessing

T4. Потребители: Управление секретами

25 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -64,7 +64,7 @@ -Управление секретамиVault + Agent Injectorattachmentsbimcdechecklistscomparisonscontractsdjangodocumentationsdrawingseavflowsiaminspectionsissuesmappermeasurementsmessage-hubnotespmprocessingrfisubscriptionssystem-logtransmittalworkspaces

T5. Потребители: Реляционное хранилище

23 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Управление секретамиVault + Agent Injectorattachmentsbimcdechecklistscomparisonscontractsdjangodocumentationsdrawingseavflowsiaminspectionsissuesmappermeasurementsmessage-hubnotespmprocessingrfisubscriptionssystem-logtransmittalworkspaces

T5. Потребители: Реляционное хранилище

23 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -76,7 +76,7 @@ -Реляционное хранилищеPostgreSQLattachmentsbimchecklistscomparisonscontractsdjangodocumentationsdrawingseavflowsiaminspectionsissuesmappermessage-hubnotespmprocessingrfisubscriptionssystem-logtransmittalworkspaces

T6. Потребители: Кэш

3 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Реляционное хранилищеPostgreSQLattachmentsbimchecklistscomparisonscontractsdjangodocumentationsdrawingseavflowsiaminspectionsissuesmappermessage-hubnotespmprocessingrfisubscriptionssystem-logtransmittalworkspaces

T6. Потребители: Кэш

3 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -88,7 +88,7 @@ -КэшRedisdjangomessage-hubpm

T7. Потребители: Событийная шина

12 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+КэшRedisdjangomessage-hubpm

T7. Потребители: Событийная шина

12 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -100,7 +100,7 @@ -Событийная шинаKafkacontractsdjangoeavflowsiaminspectionsissuesmappermessage-hubnotespmsystem-log

T8. Потребители: Очередь сообщений AMQP

11 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Событийная шинаKafkacontractsdjangoeavflowsiaminspectionsissuesmappermessage-hubnotespmsystem-log

T8. Потребители: Очередь сообщений AMQP

11 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -112,7 +112,7 @@ -Очередь сообщений AMQPRabbitMQcontractsdjangodocumentationsflowsissuesmappernotespmprocessingrfitransmittal

T9. Потребители: Объектное хранилище

13 из 35 приложений контура. Связи выведены из манифестов apps/*/base и apps/*/d8-ugmk-prod.

+Очередь сообщений AMQPRabbitMQcontractsdjangodocumentationsflowsissuesmappernotespmprocessingrfitransmittal

T9. Потребители: Объектное хранилище

13 из 35 приложений контура. Связи выведены из манифестов приложений.

@@ -124,5 +124,5 @@ -Объектное хранилищеs3.uralmine.com — объектное хранилищеУГМКattachmentscontractsdjangodocumentationseaviamissuesmappermessage-hubnotespmrfisubscriptions
+Объектное хранилищеs3.example.com — объектное хранилищеattachmentscontractsdjangodocumentationseaviamissuesmappermessage-hubnotespmrfisubscriptions \ No newline at end of file diff --git a/docs/architecture/scan_contour.py b/docs/architecture/scan_contour.py new file mode 100644 index 0000000..4227e43 --- /dev/null +++ b/docs/architecture/scan_contour.py @@ -0,0 +1,69 @@ +"""Строит матрицу «приложение -> используемый технологический сервис» для контура. + + py scan_contour.py <имя-кластера> <выход.json> + +Имя кластера — директория в clusters/, например любая из перечисленных +в корневом CLAUDE.md. Сканируются только *.yaml в apps//base и +apps//<кластер>; документация (*.md) игнорируется, иначе прозаические +упоминания переменных дают ложные срабатывания. +""" +import re, pathlib, json, sys, io + +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") +if len(sys.argv) != 3: + sys.exit(__doc__) +CLUSTER, OUT = sys.argv[1], pathlib.Path(sys.argv[2]) +ROOT = pathlib.Path(__file__).resolve().parents[2] + +kust = (ROOT / "clusters" / CLUSTER / "kustomization.yaml").read_text(encoding="utf-8") +apps = sorted(set(re.findall(r"\.\./\.\./apps/([a-z0-9-]+)/" + re.escape(CLUSTER), kust))) +if not apps: + sys.exit(f"в clusters/{CLUSTER}/kustomization.yaml не найдено ни одного приложения") + +MARKERS = { + "postgres": [r"POSTGRES_[A-Z]", r"DB__HOST", r"postgresql\.postgresql\.svc", + r"secrets/data/apps/[a-z0-9-]+/postgres"], + "rabbitmq": [r"RABBITMQ", r"AMQP__", r"rabbitmq\.rabbitmq\.svc"], + "kafka": [r"KAFKA", r"kafka\.kafka\.svc", r"BOOTSTRAP_SERVERS"], + "camunda": [r"ZEEBE", r"CAMUNDA"], + "s3": [r"S3__", r"S3_ENDPOINT", r"S3_ACCESS", r"S3_BUCKET"], + "redis": [r"REDIS_HOST", r"REDIS__", r"redis\.[a-z-]+\.svc"], + "vault": [r"vault\.hashicorp\.com/agent-inject"], + "oidc": [r"ZITADEL", r"JWKS", r"OIDC", r"jwt-public"], +} + +def scan(app): + blob = [] + for sub in ("base", CLUSTER): + d = ROOT / "apps" / app / sub + if d.is_dir(): + blob += [f.read_text(encoding="utf-8", errors="replace") for f in d.rglob("*.yaml")] + blob = "\n".join(blob) + return {svc: any(re.search(p, blob) for p in pats) for svc, pats in MARKERS.items()} + +matrix = {a: scan(a) for a in apps} + +istio_path = ROOT / "infrastructure" / "istio-config" / CLUSTER / "istio-config.yaml" +exposed_ns = [] +if istio_path.is_file(): + raw = istio_path.read_text(encoding="utf-8") + body = "\n".join(l for l in raw.splitlines() if not l.lstrip().startswith("#")) + exposed_ns = sorted({ns for _, ns in re.findall(r"service:\s*([a-z0-9-]+)\.([a-z0-9-]+)\.svc", body)}) + +hdr = ["app"] + list(MARKERS) + ["istio"] +print(f"приложений: {len(apps)}\n") +print(" | ".join(h.ljust(9) for h in hdr)) +print("-" * 112) +for app, row in matrix.items(): + cells = [app.ljust(17)] + ["+".ljust(9) if row[s] else "".ljust(9) for s in MARKERS] + cells.append("+".ljust(9) if app in exposed_ns else "".ljust(9)) + print(" | ".join(cells)) + +print("\nпотребителей на сервис:") +for s in MARKERS: + print(f" {s:10s} {sum(1 for r in matrix.values() if r[s]):2d}") +print(f" {'istio':10s} {len([a for a in apps if a in exposed_ns]):2d}") + +OUT.write_text(json.dumps({"apps": apps, "matrix": matrix, "exposed": exposed_ns}, + ensure_ascii=False, indent=2), encoding="utf-8") +print(f"\nзаписано: {OUT}") diff --git a/docs/architecture/scan_ugmk.py b/docs/architecture/scan_ugmk.py deleted file mode 100644 index 9d79bd9..0000000 --- a/docs/architecture/scan_ugmk.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Собирает матрицу «приложение UGMK -> используемый технологический сервис» -из манифестов base + d8-ugmk-prod. Только *.yaml, документацию не читаем.""" -import re, pathlib, json, sys, io -sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") - -ROOT = pathlib.Path(r"C:\Users\user\PycharmProjects\infra\iac") -CLUSTER = "d8-ugmk-prod" - -# 1. какие приложения подключены к кластеру -kust = (ROOT / "clusters" / CLUSTER / "kustomization.yaml").read_text(encoding="utf-8") -apps = sorted(set(re.findall(r"\.\./\.\./apps/([a-z0-9-]+)/" + CLUSTER, kust))) - -# 2. маркеры использования технологических сервисов -MARKERS = { - "postgres": [r"POSTGRES_[A-Z]", r"DB__HOST", r"postgresql\.postgresql\.svc", - r"secrets/data/apps/[a-z0-9-]+/postgres"], - "rabbitmq": [r"RABBITMQ", r"AMQP__", r"rabbitmq\.rabbitmq\.svc"], - "kafka": [r"KAFKA", r"kafka\.kafka\.svc", r"BOOTSTRAP_SERVERS"], - "camunda": [r"ZEEBE", r"CAMUNDA"], - "s3": [r"S3__", r"S3_ENDPOINT", r"S3_ACCESS", r"S3_BUCKET"], - "redis": [r"REDIS_HOST", r"REDIS__", r"redis\.[a-z-]+\.svc"], - "vault": [r"vault\.hashicorp\.com/agent-inject"], - "oidc": [r"ZITADEL", r"JWKS", r"OIDC", r"jwt-public"], -} - -def scan(app): - text = [] - for sub in ("base", CLUSTER): - d = ROOT / "apps" / app / sub - if not d.is_dir(): - continue - for f in d.rglob("*.yaml"): - text.append(f.read_text(encoding="utf-8", errors="replace")) - blob = "\n".join(text) - return {svc: any(re.search(p, blob) for p in pats) for svc, pats in MARKERS.items()} - -matrix = {app: scan(app) for app in apps} - -# 3. кто опубликован наружу через istio (destination host -> namespace) -istio_raw = (ROOT / "infrastructure" / "istio-config" / CLUSTER / "istio-config.yaml").read_text(encoding="utf-8") -# закомментированные маршруты не считаем развёрнутыми -istio = "\n".join(l for l in istio_raw.splitlines() if not l.lstrip().startswith("#")) -exposed = sorted(set(re.findall(r"service:\s*([a-z0-9-]+)\.([a-z0-9-]+)\.svc", istio))) -exposed_ns = sorted({ns for _, ns in exposed}) - -print("ПРИЛОЖЕНИЙ В КОНТУРЕ:", len(apps)) -print() -hdr = ["app"] + list(MARKERS) + ["istio"] -print(" | ".join(h.ljust(9) for h in hdr)) -print("-" * 110) -for app, row in matrix.items(): - cells = [app.ljust(9)] + ["+".ljust(9) if row[s] else "".ljust(9) for s in MARKERS] - cells.append("+".ljust(9) if app in exposed_ns else "".ljust(9)) - print(" | ".join(cells)) - -print() -print("ИТОГО потребителей на сервис:") -for s in MARKERS: - n = sum(1 for r in matrix.values() if r[s]) - print(f" {s:10s} {n:2d}") -print(f" {'istio':10s} {len([a for a in apps if a in exposed_ns]):2d}") -print() -print("namespace, опубликованные через istio:", ", ".join(exposed_ns)) -print() -print("ВСЕГО SERVING-СВЯЗЕЙ:", sum(sum(r.values()) for r in matrix.values()) + len([a for a in apps if a in exposed_ns])) - -(pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else "matrix.json")).write_text( - json.dumps({"apps": apps, "matrix": matrix, "exposed": exposed_ns}, ensure_ascii=False, indent=2), - encoding="utf-8")