Агуулгыг алгасах

PetroNet System

Платформын бусад баримт цөмийн зан төлөвийг тайлбарладаг. Энэ нэг нь өөр асуултад хариулна: энэ хост дээр юу хаана ажиллаж байна, ямар хаалга нээлттэй, юу нөөцлөгдөж байна.

Баримтын төв рүү буцах


Найман нэр, нэг хост

Нэр Юу Ард нь
petronet.mn Платформ — frontend ба /api/v1/* Next.js + Go
admin.petronet.mn Операторын консол — /api/platform/v1/* Мөн тэр хоёр
developer.petronet.mn Хөгжүүлэгчийн портал — OAuth клиент, scope, түлхүүр (цөмийн /dev) Мөн тэр хоёр, DEVELOPER_HOST
monitoring.petronet.mn Grafana Prometheus + хоёр exporter
dwh.petronet.mn Дата агуулахын зураглал Статик, өөрийн контейнер
docs.petronet.mn Энэ баримт Статик, MkDocs
plan.petronet.mn Төслийн баримт — шаардлага, төлөвлөгөө, жишиг Статик, MkDocs
backups.petronet.mn Нөөцлөлтийн тайлбар Статик, өөрийн контейнер

Хоёр урсгал өөр origin дээр байгаа нь гоо зүйн биш: cookie нь hostname-аар хязгаарлагддаг тул тусдаа нэр нь тусдаа эрх гэсэн үг.

Хаягууд кодод биш, .env-ийн SERVICE_URL_*-д. Нүүр хуудас тохируулсныг нь л карт болгож зурна — хаяггүй үйлчилгээ зурагдахгүй.

Төхөөрөмжийн шугамууд

Дээрх долоо нь ӨӨР ӨӨР үйлчилгээ. Доорх дөрөв нь тийм биш: яг ижил frontend ба API, зөвхөн өөр origin дээр. Native клиент бүр өөрийн host-оор ханддаг тул webview доторх дуудлага same-origin болж, session cookie нь SameSite=Strict хэвээр ажиллаж, CORS preflight огт үүсэхгүй.

Шугам Аппын нэр Клиент
desktop.petronet.mn PetroNetDesktop macOS, Windows
mobile.petronet.mn PetroNetMobile iOS/iPadOS, Android
kiosk.petronet.mn PetroNetKiosk ЗАХИАЛГАТАЙ — клиент хараахан байхгүй
pos.petronet.mn PetroNetPos ЗАХИАЛГАТАЙ — клиент хараахан байхгүй

Хаяг нь хүн төхөөрөмжийг ХЭРХЭН БАРЬДГИЙГ нэрлэнэ, үйлдлийн системийг биш: нэг ширээн дээрх Mac ба Windows хоёр нэг шугам. vhost нь nginx/device-lines.petronet.mn.conf, эх сурвалж нь native-apps/shared/device_lines.json.

Дөрвүүлэн 2026-09-05-нд асав. DNS дээр *.petronet.mn wildcard байсан тул A бичлэг нэмэх алхам хэрэггүй байв; үлдсэнийг дарааллаар нь хийв:

  1. nginx/device-lines.petronet.mn.conf → sites-available/, -enabled/
  2. certbot --nginx -d desktop.petronet.mn -d mobile.petronet.mn -d kiosk.petronet.mn -d pos.petronet.mn
  3. .env-ийн DEVICE_LINE_ORIGINS → ALLOWED_ORIGINS
  4. ХАМГИЙН СҮҮЛД клиентийн доторх хаяг (device_lines.json → client)

Дараалал нь ДҮРЭМ: эсрэгээр явбал апп байхгүй host руу чиглэж унана. Шинэ шугам нэмэхэд ч мөн адил — DNS нь wildcard тул нэр өөрөө хүрч ирнэ, харин nginx-ийн server_name-д бичих хүртэл 404 хариулна.

Портууд

Цөмийн анхдагч портуудыг энэ хост дээр урьд нь хөрш систем эзэлж байсан. Тэр систем 2026-09-03-нд алга болж, 3008/8082/5434 чөлөөтэй болсон ч энэ стек өөрийн портууд дээрээ үлдэнэ: ажиллаж буй суулгацын портыг солих нь compose, vhost, гэрчилгээ бүхнийг хөндөөд хариуд нь юу ч өгөхгүй. Энэ стекийнх:

Порт Юу
5441 postgres
8099 backend API
3028 бүрхүүл (Next.js)
9093 Prometheus
3029 Grafana
9102 / 9189 node / postgres exporter
3030 / 3031 / 3032 / 3033 dwh / docs / backups / plan статик

Эх сурвалж нь deploy/docker-compose.yml-ийн ports: — энэ хүснэгт зөрвөл compose зөв.

Бүгд зөвхөн loopback дээр. Гадагшаа гарах цорын ганц зам нь хостын nginx.

Скриптийн анхдагч бүрийг шалгах хэрэгтэй. Цөмийн backup.sh-ийн анхдагч postgres контейнер нь gerege_nexus_postgres — тэр нь энэ хост дээр хөршийнх. Анхдагчийг нь солихоо мартвал скрипт хөршийн сангаас dump авч, хөршийн бүртгэлд «амжилттай» гэж бичнэ, нэг ч алдаагүйгээр. Нэг удаа яг тэр болсон.

Хаалганууд

Хувийн муж — үргэлж нээлттэй

PLATFORM_ACCESS_MODE=public. eID-ээр танигдсан иргэн бүртгэлтэй болж, өөрийн гэртээ буудаг. Миграц 00085-аас хойш шинэ данс хэний ч байгууллагад ордоггүй тул «гэр нээлттэй» гэдэг яг энэ тохиргоотой тэнцэнэ.

Байгууллага — өөрөө сонгоно

Байгууллагын тохиргоо → «Хаалга»:

  • on_request (анхдагч) — хүсэлт илгээнэ, админ шийднэ;
  • open — богино нэрийг мэдсэн хэн ч шууд гишүүн болно.

Шууд нэгдсэн хүн платформын анхдагч user ролийг авдаг бөгөөд тэр нь *.read зөвшөөрлүүдийг агуулна. Хаалга нээх ба мэдээллээ харуулах хоёр нэг зүйл. Хэн, хэзээ орсныг join_requests дээрх ACCEPTED мөр үлдээнэ; decided_by нь NULL — хүн биш, бодлого шийдсэн.

Консол

Гурван хаалга дараалж: хаягийн шалгалт (CONTROL_PLANE_HOST-оос өөр нэрээр ирсэн хүсэлт 404), нууц үг, authenticator-ийн код. Операторын бүртгэл нь хэрэглэгчийнхээс тусдаа хүснэгт, тусдаа cookie, тусдаа audit.

Хаягийн жагсаалт (IP allowlist) одоогоор тавиагүй — операторуудын хаяг тодрох өдөр nginx/admin.petronet.mn.conf доторх хоёр мөрийг сэргээнэ.

eID

Relying-party итгэмжлэл .env-д (EID_RP_UUID, EID_RP_SECRET), нэр ба бичвэр нь тусдаа (EID_RP_NAME, EID_DISPLAY_TEXT) — иргэн апп дотроо яг тэдгээрийг уншиж зөвшөөрдөг тул цөмийн анхдагч «Gerege Nexus»-ыг үлдээвэл хүнээс өөрийнх нь нээгээгүй зүйлд нэвтрэхийг зөвшөөр гэж гуйна.

eID дээр бүртгэлтэй callback нь яг https://petronet.mn/auth/eid/callback байх ёстой — платформ өөр хост, өөр замыг дуудахаасаа өмнө татгалздаг.

Нөөцлөлт

Өдөр бүр 03:15-д deploy/scripts/backup.sh. Сангийн pg_dump ба кластерын role-ууд (pg_dumpall --roles-only, нууц үггүй) хоёр файлыг авч, 14 хоногоос хуучныг цэвэрлэж, үр дүнг platform_backups-д бичнэ — консолын «Нөөцлөлт» карт тэр мөрийг уншина. Бүтэлгүйтэл ч бүртгэгдэнэ: хэмжигдээгүй нөөцлөлт нь нөөцлөлтгүйтэй бараг адил.

Хуулбар нь /var/backups/petronet/, энэ хостын дискэн дээр. Шифрлэн өөр байршил руу илгээх алхам нь BACKUP_AGE_RECIPIENT (age-ийн нийтийн түлхүүр — тавигдсан) ба BACKUP_S3_* дөрөв бүрдмэгц ажиллана. Дөрөв нь дутуу байхад алхам бүхэлдээ алгасагдаж, nexus_backup_offsite_ok 0 гэж хэмжигдэнэ — хуулбар өөр газар байхгүй гэдэг нь чимээгүй байх зүйл биш. Тохируулсан байршил руу хуулж чадаагүй бол локал файл үлдэх ч ажиллалт бүхэлдээ амжилтгүй (ok=false, exit 1). restic-ээс сэргээх алхмууд OPERATIONS.md-д.

Сэргээх нь хаягдах сан руу, ажиллаж байгаа руу биш. Эхлээд role-ууд, дараа нь сан (restore_check-ийг урьдчилан createdb-ээр үүсгэнэ):

gunzip -c /var/backups/petronet/petronet-roles-<огноо>.sql.gz \
  | docker exec -i petronet2_postgres psql -U postgres -d postgres
gunzip -c /var/backups/petronet/petronet-<огноо>.sql.gz \
  | docker exec -i petronet2_postgres psql -U postgres -d restore_check

Ажиллаж буй кластер руу role-уудыг дахин ачаалахад «already exists» алдаа гарна — хор хөнөөлгүй. Бүрэн шалгалт нь шинэ кластер дээр (OPERATIONS.md).

Ажиглалт

Prometheus гурван зорилт цуглуулна: энэ бүтээгдэхүүний API, хостын node_exporter, ба PetroNet System-ийн сангийн postgres_exporter. Grafana нь monitoring.petronet.mn дээр, хоёр самбартай.

Консолын «Платформын эрүүл мэнд» дэлгэц ижил Prometheus-аас уншина. Хөршийн стекийг хуваалцаагүй нь санаатай: консолын асуулгууд job-оор шүүдэггүй тул хоёр бүтээгдэхүүний хүсэлт нийлж тоологдох байв.

Лог ба trace (Loki, Tempo) энд байхгүй — тэдгээр нь өөрийн диск, өөрийн хадгалалтын хугацаа шаардах тусдаа шийдвэр.

Нууцууд

Нууцын сан (Infisical) дээрх petronet-gerege-nexus / prod төсөл нь эх сурвалж; хостын .env нь түүний хуулбар. Долоо: сангийн нууц үг, built-in клиентийн нууц, eID-ийн хос, Gerege Core-ийн токен, Grafana-гийн админ, ба нөөцлөлтийн age нийтийн түлхүүр.

age-ийн хувийн түлхүүр энд ч, хостод ч байхгүй — тэр нь цөмийн төсөлд, операторт. Хостод зөвхөн нийтийнх байгаа нь санаатай: эвдэрсэн платформ өөрийн илгээсэн нөөцлөлтөө уншиж чадахгүй.

Шинэчлэлт

Хост дээр гараар git pull хийхгүй. Зам нь ганцхан:

commit → PR → main → CI (ci.yml) ногоон → Deploy (deploy.yml). Deploy нь хост дээрх self-hosted runner дээр deploy/ci-deploy.sh <sha>-г дуудна: /opt/petronet-v2/src-г тэр commit руу reset --hard хийгээд ./deploy.sh-г ажиллуулна.

deploy.sh нь backend (petronet:latest) ба бүрхүүл (petronet-web:latest, core/frontend-ээс) хоёр образыг хост дээр барина — registry-ээс татахгүй. Барихаас өмнө ажиллаж буй хоёр образыг :previous гэж тэмдэглэж, эрүүл мэнд / брэнд / цөмийн хувилбарын шалгалт унавал тэдгээр рүү буцаана (миграц буцахгүй — зөвхөн урагш). Мөн docs ба plan сайтыг дахин угсарч контейнерийг нь дахин үүсгэнэ.

Цөм нь core/ дахь snapshot (core/SNAPSHOT) — backend ба бүрхүүл хоёулаа түүнээс барьдаг тул хоёр тал нэг commit-ийн нэг API гэрээн дээр байна. Шинэчлэх нь deploy/sync-core.sh / core-sync.yml.

Мартагддаг зүйлс

  • http2 on; нь тусдаа заавар байх ёстой — certbot түүнийг бичдэггүй тул шинэ vhost бүр HTTP/1.1 дээр үлддэг. Энэ хост дээр хоёр домэйн яг тэгж үлдсэн байсныг шалгалт илрүүлсэн.
  • add_header өв залгамжлахгүй: хүү блок дотор нэг ч байвал дээд түвшнийхийг бүгдийг нь хаяна.
  • Порт бүр нэг эзэнтэй. Дээрх хүснэгтийг шинэ сервис нэмэх бүрд шалгана.
curl -s -o /dev/null -w "http/%{http_version} %{http_code}\n" https://<нэр>/
curl -sI https://<нэр>/ | grep -iE 'strict-transport|x-frame|x-content'