Saltar a contenido

Runbook — дохио бүрд юу хийх вэ

Prometheus-ийн дохио бүрд: юу болсон, эхний 5 минутад юу шалгах, яаж засах, хэзээ өргөжүүлэх.

Баримт бичгийн төв рүү буцах · Ажиллагаа

Дохионы нэрс нь 2026-10-25-нд хаагдсан nexus.gerege.mn-ийн ажиглалтын стекийн Prometheus дүрмүүдийнх (тэр стек энэ репоос хасагдсан). Алхмууд нь платформын зан төлөвийг тайлбарладаг тул хүчинтэй хэвээр.

Нэр ба зам. Одоогийн цорын ганц суулгац nexus.gerege.net: core-app-02 дээр /opt/nexus хавтаст docker compose-оор ажиллана, API 127.0.0.1:8080, frontend 127.0.0.1:3000 (deploy-dev.yml). Compose файл, .env, нөөцлөлт нь proxmox-gerege-net (stack/nexus) репод. Доорх командууд /opt/nexus дотроос:

cd /opt/nexus

Ерөнхий зарчим

Хоёр түвшин. severity: page нь хэн нэгнийг сэрээх ёстой — хэрэглэгч одоо хохирч байна. severity: ticket нь ажлын өдөр засах — засахгүй бол хохирол болно. Ticket дээр 03:00 цагт хариу үйлдэл хийх нь alert fatigue-ийн хамгийн богино зам бөгөөд түүний төгсгөл нь page-ийг ч үл тоох явдал.

Эхлээд хэмжилт, дараа нь тааварлалт. Дохио бүрийн эхний алхам нь Grafana дээр аль хэсэг эвдэрсэнийг харах. Restart нь бараг үргэлж шинжтомжийг арилгаад шалтгааныг нь нуудаг.

Юу хийснээ бич. Осол бүрийн дараа энэ файлыг засах нь дараагийн хүнд өгөх хамгийн үнэ цэнэтэй зүйл.

Тайван байдал. Платформын ихэнх дохио нь өгөгдөл алдагдсан гэсэн үг биш. Өгөгдөл алдагдах ганц бодит эрсдэл нь дискний дүүрэлт бөгөөд түүнд тусдаа хоёр дохио бий.


API

NexusAPIDown

Юу болсон. Prometheus 2 минутын турш backend-ийн /metrics-ийг уншиж чадсангүй. Процесс унасан, гацсан, эсвэл сүлжээ тасарсан.

Энэ үед бусад бүх API дохио чимээгүй болно — өгөгдөл ирэхгүй тул дүрэм тооцоологдохгүй. Тиймээс энэ дохио дангаараа ирж байгаа нь "өөр юу ч болоогүй" гэсэн үг биш.

Эхний 5 минут.

docker compose ps backend
docker compose logs --tail 200 backend
curl -s 127.0.0.1:8080/health
curl -s 127.0.0.1:8080/ready
  • Контейнер огт байхгүй эсвэл Restarting бол → логоос яагаад гарсныг унш.
  • /health хариулж байгаа атлаа /metrics уншигдахгүй бол → сүлжээний асуудал, Prometheus backend-д хүрч чадаж байгаа эсэхийг шалга.
  • /health OK, /ready 503 бол → хариуны message-ийг унш: database unreachable → NexusPostgresDown; database schema is behind this release → миграц ажиллаагүй (docker compose run --rm migrate up); tenant isolation is not active yet → dbguard-ийн probe хараахан амжаагүй (өгөгдлийн сан хүрэхгүй эсвэл роль/grant буруу), логт dbguard: мөрийг хай. Схемийн шалгалт логт schema version check is off гэж байвал API-ийн роль public.goose_db_version-ийг уншиж чадахгүй байна (42501) — /ready-г блоклохгүй, гэхдээ хоцорсон схемийг барихгүй; хэрэгтэй бол SELECT эрх өг.

Засах.

docker compose up -d --no-deps backend

Дахин асаахын өмнө логийг хадгал — контейнер дахин үүсэхэд Loki-д очоогүй мөрүүд алга болно:

docker compose logs backend > "$(mktemp /tmp/backend-crash-XXXXXX.log)" 2>&1

Байнга унаж байвал: санах ой (docker stats), OOM (dmesg | tail -50), эсвэл миграц дуусаагүй байх магадлалтай.

Өргөжүүлэх. 15 минутын дотор сэргээгээгүй бол платформын хариуцсан хүнд. Өгөгдлийн сан хөндөгдсөн сэжигтэй бол шууд.


NexusAPIErrorBudgetBurningFast

Юу болсон. Сүүлийн 1 цаг ба сүүлийн 5 минутад хоёуланд нь 5xx-ийн хувь 1.44%-иас давлаа. Ийм хурдаар сарын алдааны төсөв 2 хоногт дуусна.

Хоёр цонх зэрэг давсан үед л дохио өгдөг гэдэг нь чухал: түр зуурын үсрэлт энэ дохиог өгөхгүй, харин одоо ч үргэлжилж байгаа эвдрэл өгнө.

Эхний 5 минут. Grafana → API тойм:

  1. Алдаа хамгийн их гаргаж буй замууд — нэг зам уу, бүгд үү?
  2. Нэг зам → тэр функцийн саяхны өөрчлөлт, эсвэл түүний хамаарал.
  3. Бүх зам → өгөгдлийн сан, эсвэл дөнгөж deploy хийсэн хувилбар.
  4. Хүсэлтийн урсгал — 5xx-ийн эхэлсэн мөч deploy-тэй давхцаж байна уу?
  5. Loki:
    {container=~".*backend.*"} | json | level = "ERROR"
    

Засах.

  • Deploy-ийн дараа эхэлсэн бол → буцаа. Оношлохоос өмнө буцаах нь зөв дараалал. Өмнөх хувилбарын image GHCR-д commit-ийн SHA-гаар байгаа тул deploy-dev.yml-ийг тэр SHA-гаар гараар ажиллуул:
    gh workflow run deploy-dev.yml --ref main -f tag=<өмнөх commit-ийн SHA>
    
    Схем нь шинэ хэвээр үлдэнэ — миграц нэг хувилбарын турш хойш нийцтэй байх дүрэм (Ажиллагаа) яг үүний төлөө. Дараа нь засвараа main руу түлхэхэд CI ногоон болмогц deploy шинэ image-ийг гаргана.
  • Гадаад системээс шалтгаалсан бол → NexusExternalSystemFailing.
  • Өгөгдлийн сангаас → NexusDatabasePoolExhausted.

Өргөжүүлэх. 30 минутын дотор алдааны хувь буурахгүй бол.


NexusAPIErrorBudgetBurning

Юу болсон. 6 цаг ба 30 минутын цонхонд 0.6%-иас дээш. Огцом уналт биш, тогтвортой эвдрэлтэй төлөв — ихэвчлэн нэг зам, нэг гадаад систем, эсвэл зөвхөн тодорхой нөхцөлд гардаг алдаа.

Эхний 5 минут. Дээрхтэй адил, гэхдээ 6 цагийн хугацаанд: Алдаа хамгийн их гаргаж буй замууд панелийн хугацааг 6 цаг болго. Тогтмол нэг зам харагдвал тэр нь шалтгаан.

Засах. Ихэвчлэн код засвар шаардана. Хэрэглэгчид үргэлжлүүлэн хохирч байгаа тул засвар нь дараагийн ээлжийн ажил биш, өнөөдрийнх.

Өргөжүүлэх. Ажлын цагт хариуцсан багт. Шөнө сэрээх шаардлагагүй — энэ нь page боловч 15 минутын for-той бөгөөд дэлбэрэлт биш.


NexusAPIErrorBudgetSlowBurn

Юу болсон. 3 хоног ба 6 цагийн аль алинд нь SLO-гоос давсан. Хэн ч сэрэх шаардлагагүй, гэхдээ энэ сарын 99.9% биелэхгүй болох замдаа явж байна.

Эхний 5 минут. Үлдсэн алдааны төсөв панелийг хар. Сөрөг бол зорилт аль хэдийн биелэхгүй болсон.

Засах. Тасалдсан хэдэн зам, эсвэл нэг тогтмол алдаа гаргадаг үйлдлийг ол. Ихэвчлэн: боловсруулагдаагүй nil, timeout-гүй гадаад дуудлага, эсвэл тодорхой өгөгдөл дээр л унадаг handler.

Өргөжүүлэх. Шаардлагагүй. Ажлын тасалбар үүсгэ.


NexusAPILatencyHigh

Юу болсон. Урт poll-оос бусад бүх замын p95 хариу 10 минутын турш 2 секундээс давлаа.

Эхний 5 минут.

  1. Grafana → Тэсвэрлэлт → Өгөгдлийн сангийн pool. pgxpool_acquired_conns таазанд ойрхон, empty_acquire өсөж байвал → шалтгаан нь pool.
  2. Гадаад системүүд → p95. Аль нэг систем удааширсан бол handler-ууд түүнийг хүлээж байна.
  3. Инфраструктур → CPU, iowait. iowait өндөр бол диск.

Засах.

  • Pool дүүрсэн → удаан query-г ол:
    SELECT pid, now() - query_start AS duration, state, left(query, 120)
      FROM pg_stat_activity
     WHERE state <> 'idle' AND now() - query_start > interval '5 seconds'
     ORDER BY duration DESC;
    
  • Гадаад систем удаан → тухайн rail-ыг түр хаах боломжтой эсэхийг хар (жишээ нь eSign HSM-ийг гарын үсгийн бодлогоос).
  • Диск удаан → NexusDiskFillingUp шалга, дүүрсэн диск удаашралын түгээмэл шалтгаан.

Өргөжүүлэх. Хэрэглэгчид "систем ажиллахгүй байна" гэж мэдэгдэж эхэлбэл page шиг хандах.


NexusLoadShedding

Юу болсон. Зэрэг үйлчлэх хүсэлтийн тааз (1000)-д хүрч, шинэ хүсэлтүүд 503-аар татгалзагдаж байна. Хамгаалалт ажиллаж байгаа хэрэг — гэхдээ хэрэглэгч алдаа хараад байна.

Эхний 5 минут.

  1. Тэсвэрлэлт → Зэрэг үйлчилж буй хүсэлт. Огцом үсэрсэн үү, аажим өссөн үү?
  2. Огцом → урсгалын оргил эсвэл халдлага. API тойм → rps.
  3. Аажим → хүсэлтүүд дуусахгүй хуримтлагдаж байна: удаан гадаад дуудлага эсвэл өгөгдлийн сангийн түгжээ.
  4. Эх сурвалж нэг IP мөн эсэхийг nginx-ийн логоос:
    awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
    

Засах.

  • Халдлага бол → nginx дээр хязгаарла (тэнд аль хэдийн limit_req бүсүүд бий).
  • Бодит ачаалал бол → яагаад хүсэлтүүд удаж байгааг ол; таазыг өсгөх нь сүүлийн арга бөгөөд ихэвчлэн буруу арга — 1000 зэрэг хүсэлт нь 25 холболттой pool-той машины хувьд аль хэдийн их.
  • Урт poll-ууд (eID) хуримтлагдсан бол энэ нь хэвийн: тэдгээр нь секундүүдээр барих зориулалттай.

Өргөжүүлэх. 15 минутаас удаан үргэлжилбэл.


Гадаад системүүд

NexusExternalSystemFailing

Юу болсон. {{ system }} рүү хийсэн дуудлагын тэн хагасаас олон нь 10 минутын турш амжилтгүй. Энэ нь ихэвчлэн энэ платформын доголдол биш — гэхдээ хэрэглэгчид нэвтэрч эсвэл гарын үсэг зурж чадахгүй байна.

Аль систем ямар үйлчилгээг зогсоох вэ:

Систем Зогсох зүйл
eid Gerege ID-гаар нэвтрэх, баталгаажсан гарын үсэг
dan ДАН-аар нэвтрэх
xyp Иргэн/байгууллагын лавлагаа
esign HSM-ийн гарын үсэг (хуучин зам)
gemini AI туслах, орчуулга, яриа таних
emailverify Хаяг баталгаажуулах и-мэйл

Эхний 5 минут.

  1. Гадаад системүүд dashboard → p95 — үйлдлээр. Бүх үйлдэл унасан уу, нэг нь уу?
  2. Гараар шалга — жишээ нь eID:
    docker compose exec backend wget -qO- --timeout=10 https://rp.eidmongolia.mn/ >/dev/null && echo reachable
    
  3. Loki:
    {container=~".*backend.*"} | json | level = "ERROR" |= "eid"
    
  4. Итгэмжлэл дууссан эсэхийг шалга — 401/403 нь тэдний уналт биш, бидний тохиргооны асуудал.

Засах.

  • Тэдний тал бол: тухайн үйлчилгээ үзүүлэгчид мэдэгд. Хэмжилтээ хамт өг — external_request_duration_seconds нь яг хэдэн цагт, ямар хувьтай гэдгийг хэлнэ.
  • Бидний тал бол (итгэмжлэл, тохиргоо): .env-ийг шалга, backend-ыг дахин асаа.
  • Хэрэглэгчдэд: өөр нэвтрэх зам байгаа эсэхийг мэдэгд (eID унасан ч нууц үг, Google ажиллаж байгаа бол).

Өргөжүүлэх. Нэвтрэлтийн бүх зам (eid, dan, google, sso) зэрэг унасан бол шууд — тэр үед хэн ч орж чадахгүй байна.


NexusExternalSystemDegraded

Юу болсон. 30 минутын турш алдааны хувь 15%-иас дээш. Зургаа тутмын нэг хүсэлт унана — хэрэглэгчийн хувьд "заримдаа ажилладаг", энэ нь бүрэн уналтаас удаан анзаарагддаг тул илүү их гомдол авчирдаг.

Эхний 5 минут. Дээрхтэй адил. Нэмж: алдаа тодорхой хэв маягтай юу — тодорхой регистрийн дугаар, тодорхой хэмжээний файл?

Засах. Ихэвчлэн хүлээх нь зөв — эсвэл давтан оролдох логик нэмэх. Дуудлага бүр өөрийн timeout-той эсэхийг шалга.

Өргөжүүлэх. Шаардлагагүй. Ажлын тасалбар.


NexusExternalSystemSlow

Юу болсон. {{ system }}-ийн p95 хариу 10 секундээс удаан. Удаан гадаад дуудлага нь хүсэлтийн goroutine болон pool-ын холболтыг барьж байдаг тул тухайн систем рүү огт хандаагүй хүсэлтүүд ч удаашрана.

Эхний 5 минут. Pool-ын байдлыг шалга (Тэсвэрлэлт dashboard). resilience_in_flight_requests өсөж байвал энэ нь удахгүй load shedding болно.

Засах. Тухайн rail-ыг түр хаах, эсвэл timeout-ыг богиносгох. Хамгийн муу төлөв нь удаан дуудлага + урт timeout: платформ бүхэлдээ нэг гадаад системийн хурдаар ажиллана.

Өргөжүүлэх. Load shedding эхэлбэл.


Инфраструктур

NexusDiskFillingUp

Юу болсон. {{ mountpoint }} дээр сул зай 15%-иас бага. 93% давбал NexusDiskAlmostFull нь page болж ирнэ.

Энэ бол өгөгдөл алдагдах бодит эрсдэл бүхий цөөхөн дохионы нэг. 100% хүрэхэд Postgres бичихээ болино, WAL бичигдэхгүй, платформ бүхэлдээ зогсоно.

Эхний 5 минут.

df -h
du -xh --max-depth=1 / 2>/dev/null | sort -rh | head -20
docker system df

Хамгийн түгээмэл гурван шалтгаан:

  1. Docker-ийн хуучин image, build cache — deploy бүр шинэ image татдаг.
  2. Prometheus/Loki-ийн өгөгдөл — 20 GB / 31 хоногийн таазтай ч эхний тохиргооны алдаа таазыг хэтрүүлж болно.
  3. Postgres — үнэхээр өссөн, эсвэл vacuum ажиллаагүй.

Засах.

# 1 — build cache. Хамгийн аюулгүй, ихэвчлэн хамгийн том.
docker builder prune -af

# 2 — ашиглагдаагүй image-ууд. deploy-dev.yml амжилттай rollout бүрийн дараа
#     үүнийг өөрөө ажиллуулдаг ч диск дүүрсэн бол гараар шууд.
docker image prune -f

# Postgres-ийн хамгийн том хүснэгтүүд
docker compose exec -T postgres psql -U postgres -d platform_db -c "
  SELECT relname, pg_size_pretty(pg_total_relation_size(c.oid)) AS size
    FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
   WHERE n.nspname = 'public' AND c.relkind = 'r'
   ORDER BY pg_total_relation_size(c.oid) DESC LIMIT 15;"

Устгаж болохгүй зүйлс: Postgres-ийн өгөгдлийн volume, шифрлэлтийн түлхүүр агуулсан .env файлууд, гарын үсэгтэй PDF-үүд.

Хамгийн сүүлд гарсан: 2026-08-30, nexus.gerege.mn — 81%. Хэмжсэн зүйл: 294 image / 42.7 GB, build cache 11.9 GB. Ихэнх нь өдөр бүрийн deploy-ийн хуучин tag байсан — backend 129, frontend 130. Build cache ба хуучин tag-уудыг цэвэрлэхэд 61 GB → 22 GB (81% → 29%). Зогссон контейнерууд хуучин tag дээр суугаагүйг эхлээд шалгасан.

2026-10-12, nexus.gerege.mn — 100%. Энэ cron суугаагүй байсан (дээрх loop хуучин private-gerege-nexus repo нэрийг заасан тул юу ч устгахгүй байв): 166 tag / repo, containerd 58 GB. private_nexus_postgres recovery-ийн checkpoint дээр No space left on device PANIC-аар давтаж, eID нэвтрэлт «session could not be started» гэж хэдэн цаг хариулсан — receiver хоосон тул хэн ч мэдээгүй. Цэвэрлэсний дараа 44%; nexus_js_postgres restart backoff-д гацсан тул docker restart хэрэгтэй болсон.

deploy бүр шинэ image нэмдэг ба ghcr-retention.yml нь зөвхөн GHCR талыг цэвэрлэдэг, хостын дискийг биш — одоо deploy-dev.yml rollout бүрийн дараа docker image prune -f ажиллуулна.

Өргөжүүлэх. 95% давбал шууд — цаг хэдхэн байна.


NexusDiskAlmostFull

93%. Дээрхтэй ижил, гэхдээ page. Эхлээд docker builder prune -af ба docker image prune -f.


NexusHostMemoryPressure

Юу болсон. Сул санах ой 10%-иас бага. Дараагийн алхам нь OOM killer бөгөөд тэр нь ихэвчлэн хамгийн том процессыг — Postgres-ийг — алдаг.

Эхний 5 минут.

free -h
docker stats --no-stream
dmesg | grep -i -E 'oom|killed process' | tail -20

Инфраструктур dashboard → Контейнер бүрийн санах ой. Аль контейнер өссөн бэ, тогтмол өсөж байна уу (leak) эсвэл нэг үсрэлт үү?

Засах.

  • Мониторингийн стек өөрөө хэт их идэж байвал → Prometheus-ийн retention багасга, эсвэл Loki-г түр унтраа. Платформ энэ хоёрын алийг нь ч шаарддаггүй.
  • Backend leak → дахин асаа, дараа нь go_goroutines графикийг хар (API тойм). Тогтмол өсөж байгаа goroutine нь дуусаагүй урт poll.
  • Postgres → shared_buffers хэт өндөр тохируулсан эсэхийг шалга.

Өргөжүүлэх. OOM killer аль хэдийн ажилласан бол шууд.


NexusPostgresDown

Юу болсон. postgres_exporter өгөгдлийн санд холбогдож чадахгүй байна.

Эхлээд ялга: үнэхээр Postgres унасан уу, эсвэл зөвхөн exporter-ийн нэвтрэлт эвдэрсэн үү? Хоёр дахь нь платформд огт нөлөөгүй.

# Платформ өөрөө өгөгдлийн санд хүрч байна уу
curl -s 127.0.0.1:8080/ready

docker compose ps postgres
docker compose logs --tail 100 postgres

/ready нь {"status":"ready"} гэж хариулж байвал → Postgres хэвийн, асуудал нь exporter-ийн нууц үг. backend/db/migrations/00044_monitoring_role.sql доторх GRANT-ыг дахин ажиллуул.

Postgres үнэхээр унасан бол.

docker compose up -d postgres
docker compose logs -f postgres

Диск дүүрсэн эсэхийг заавал шалга — Postgres-ийн зогсох хамгийн түгээмэл шалтгаан бол сул зай дуусах.

Өргөжүүлэх. Postgres үнэхээр унасан бол шууд. Өгөгдөл эвдэрсэн шинж тэмдэг (PANIC, corrupted) харагдвал дахин асаахаа боль — эхлээд backup-аа шалга.


NexusPostgresConnectionsHigh

Юу болсон. Зөвшөөрөгдсөн холболтын 80% эзлэгдсэн. Дүүрэхэд шинэ холболт бүр татгалзагдана.

Платформын pool нь replica тутамд 25-аар хязгаарлагдсан тул энэ нь ихэвчлэн pool-оос гадуурх ямар нэг зүйл холбогдож байгааг илтгэнэ.

Эхний 5 минут.

SELECT usename, application_name, state, count(*)
  FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;
  • idle in transaction олон → commit хийгээгүй гүйлгээ. Түгжээ барьж, vacuum-ыг зогсооно.
  • Танихгүй application_name → өөр стек, нээлттэй үлдсэн psql, эсвэл дуусаагүй миграц.

Засах.

-- 10 минутаас удаан idle in transaction байгаа холболтуудыг таслах
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
   AND now() - state_change > interval '10 minutes';

max_connections-ыг өсгөх нь сүүлийн арга: холболт бүр санах ой иддэг, өсгөх нь ихэвчлэн асуудлыг хойшлуулна.

Өргөжүүлэх. 95% давбал.


NexusDatabasePoolExhausted

Юу болсон. Платформын өөрийн pool-ын 90%-иас дээш нь 10 минутын турш эзлэгдсэн. Хүсэлтүүд холболт хүлээж эхэлнэ — энэ нь удаашралыг тайлбарлах хамгийн эхний газар.

Postgres чөлөөтэй атлаа энэ дүүрсэн байх нь бүрэн боломжтой: асуудал нь холболтын тоо биш, тэднийг барьж буй хугацаа.

Эхний 5 минут.

  1. Тэсвэрлэлт → Хүлээлт. empty_acquire тэгээс салсан мөч нь эхлэл.
  2. Удаан query:
    SELECT pid, now() - query_start AS duration, left(query, 200)
      FROM pg_stat_activity
     WHERE state = 'active' AND now() - query_start > interval '2 seconds'
     ORDER BY duration DESC;
    
  3. Түгжээ:
    SELECT blocked.pid AS blocked_pid, blocking.pid AS blocking_pid,
           left(blocked.query, 80) AS blocked_query
      FROM pg_stat_activity blocked
      JOIN pg_stat_activity blocking
        ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));
    

Засах. Удаан query-г ол, индекс нэм. Түр арга нь тухайн query-г таслах (pg_cancel_backend). Pool-ын хэмжээг өсгөх нь Postgres руу асуудлыг шилжүүлнэ.

Өргөжүүлэх. p95 2 секундээс давбал.


NexusRedisDown

Юу болсон. Redis хариу өгөхгүй байна.

Платформ ажиллана. Redis нь энд кэш хүчингүй болгох мессеж болон deployment-ийн хэмжээний хурдны хязгаарын тоолуурыг барьдаг. Унасан үед хязгаар нь процесс тус бүрийнх болж, эрх цуцлах нь бусад replica дээр кэшийн TTL-ийн хугацаагаар хоцрогдоно. Өгөгдөл алдагдахгүй — Redis-д хадгалагдах ёстой юу ч байхгүй.

Эхний 5 минут.

docker compose ps redis
docker compose logs --tail 50 redis
docker compose exec redis redis-cli ping

Засах.

docker compose up -d redis

Санах ойн таазанд хүрсэн бол энэ нь хэвийн — allkeys-lru бодлого хуучин түлхүүрүүдийг хаяна.

Өргөжүүлэх. Шаардлагагүй.


NexusContainerRestarting

Юу болсон. {{ name }} сүүлийн 30 минутад 3-аас олон удаа дахин эхэллээ.

Docker-ийн restart: unless-stopped нь эвдрэлийг нуух гол арга: үйлчилгээ "ажиллаж байгаа" харагдана, гэхдээ хэсэг хугацаанд хариу өгөхгүй бөгөөд лог нь контейнер бүрийн хамт алга болно.

Эхний 5 минут.

docker compose ps -a
docker inspect <контейнер> --format '{{.State.ExitCode}} {{.State.Error}}'
docker compose logs --tail 300 <үйлчилгээ>
  • ExitCode 137 → OOM. NexusHostMemoryPressure.
  • ExitCode 1 + тохиргооны алдаа → .env.
  • Healthcheck унаж байгаа → шалтгаан нь тэр контейнер биш, түүний хамаарал.

Засах. Шалтгааныг зас. Дахин асаах нь мөчлөгийг л үргэлжлүүлнэ.

Өргөжүүлэх. backend эсвэл postgres бол шууд.


NexusTLSCertificateExpiringSoon

Юу болсон. {{ domain }}-ийн сертификат 14 хоногт дуусна — өөрөөр хэлбэл certbot-ын автомат сунгалт ажиллаагүй байна.

Дуусахад бүх браузер сэрэмжлүүлэг үзүүлнэ, native клиентүүд огт холбогдохоо болино (тэд сертификатын алдааг алгасах товчгүй).

Эхний 5 минут.

sudo certbot certificates
sudo systemctl status certbot.timer
sudo journalctl -u certbot --since '7 days ago' | tail -50

Засах.

sudo certbot renew --dry-run   # эхлээд туршилт
sudo certbot renew
sudo nginx -t && sudo systemctl reload nginx

Түгээмэл шалтгаан: .well-known/acme-challenge/ руу очих зам nginx-д хаагдсан, DNS өөрчлөгдсөн, эсвэл 80 порт хаалттай.

Өргөжүүлэх. 3 хоног үлдсэн бол шууд.


Мониторинг өөрөө ба нөөцлөлт

Ажиглалтын стекийн өөрийнх нь болон нөөцлөлтийн дохионууд (NexusMonitoring*, NexusAlertmanager*, NexusPrometheus*, NexusLoki*, NexusBackup*, NexusTLSExpiryUnknown) хаагдсан хостын скрипт, стекийнх байсан. nexus.gerege.net-ийн нөөцлөлт proxmox-gerege-net (stack/nexus) репод.

Операторын консол

Консолын дохионууд бусдаасаа өөр асуулт тавьдаг: "систем эрүүл үү" биш, "хэн нэгэн орох гэж оролдож байна уу". Консол нь хаягийн жагсаалтын (CONTROL_PLANE_ALLOWED_CIDRS) ард байдаг тул эдгээр оролдлого нь зөвшөөрөгдсөн сүлжээнээс ирж байна — гаднаас ирсэн бол энэ хүртэл хүрэхгүй байсан.

NexusControlPlaneLoginFailures

Юу болсон. 15 минутын дотор консол руу 5-аас олон удаа амжилтгүй нэвтрэх оролдлого болов.

Эхний 5 минут.

  1. Шалтгааныг ялга — Prometheus дээр:
    sum by (result) (increase(cp_login_attempts_total[1h]))
    
  2. bad_password давамгайлж байвал → нууц үг тааж байна.
  3. bad_code давамгайлж байвал → нууц үг нь зөв байж болзошгүй, зөвхөн хоёр дахь хүчин зүйл нь зогсоож байна. Энэ нь илүү яаралтай.
  4. unknown давамгайлж байвал → байхгүй и-мэйл хаягуудыг оролдож байна: жагсаалт таамаглаж байгаа хэрэг.
  5. no_second_factor → bootstrap тасалдсан бүртгэл байна. §3.4-ийг үз.
  6. Аль хаягнаас ирснийг:
    grep '/api/platform/v1/session' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
    
  7. Тэр хаяг хэний вэ? CONTROL_PLANE_ALLOWED_CIDRS дотор хэн байгааг хар.

Засах.

  • Оператор өөрөө андуурсан бол → түгжээ 15 минутын дараа өөрөө тайлагдана. Яаралтай бол DB-ээс:
    UPDATE operator_accounts SET failed_login_attempts = 0, locked_until = NULL
     WHERE lower(email) = lower('...');
    
  • Танихгүй хаяг бол → тэр хаягийг CONTROL_PLANE_ALLOWED_CIDRS-аас хасаад backend-ыг дахин асаа. Дараа нь тухайн сүлжээнд юу болж байгааг шалга: жагсаалтад байгаа хаягнаас оролдлого ирсэн нь тэр машин эсвэл VPN-ийн бүртгэл алдагдсан гэсэн үг.
  • bad_password олон боловч дараа нь success гарсан бол → тэр session-ыг operator_audit-аас шалга: хэн, хаанаас, юу хийсэн.

Өргөжүүлэх. bad_code олноор гарсан, эсвэл амжилтгүйн дараа амжилттай нэвтрэлт болсон бол — тухайн операторт утсаар холбогдож, өөрөө мөн эсэхийг асуу. Тийм биш бол бүх session-ыг цуцалж, нууц үг ба TOTP-ийг дахин үүсгэ.

NexusControlPlaneLockout

Юу болсон. Аль хэдийн түгжигдсэн бүртгэл рүү оролдлого үргэлжилж байна. Хүн андуурсан бол зогсдог; үргэлжилж байгаа нь автоматжуулсан оролдлого.

Эхний 5 минут.

  1. Дээрхтэй ижил — аль бүртгэл, аль хаягнаас.
  2. operator_audit-д тэр бүртгэлээр амжилттай юу болсныг шалга:
    SELECT created_at, action, ip FROM operator_audit
     WHERE operator_email = '...' ORDER BY created_at DESC LIMIT 50;
    

Засах.

  1. Хаягийг CONTROL_PLANE_ALLOWED_CIDRS-аас шууд хас, docker compose up -d backend.
  2. Бүртгэлийг идэвхгүй болго:
    UPDATE operator_accounts SET disabled_at = NOW() WHERE lower(email) = lower('...');
    UPDATE operator_sessions SET revoked_at = NOW()
     WHERE operator_id = (SELECT id FROM operator_accounts WHERE lower(email) = lower('...'))
       AND revoked_at IS NULL;
    
  3. Бусад операторуудад мэдэгд.

Өргөжүүлэх. Шууд. Энэ бол хамгийн эрхтэй интерфэйс рүү чиглэсэн үргэлжилсэн оролдлого бөгөөд аль хэдийн дотоод сүлжээнд байгаа хэн нэгнээс ирж байна.

NexusBreakGlassUsed

Юу болсон. Онцгой байдлын (break-glass) операторын бүртгэлээр консол руу нэвтэрлээ. Энэ бүртгэлийн нууц үг офлайн сейфэнд байдаг бөгөөд ердийн үед хэзээ ч хэрэглэгддэггүй.

Эхний 5 минут.

  1. Хэн нээв? Сейфийг хариуцагчаас утсаар асуу — мессежээр биш.
  2. Хаанаас:
    docker compose logs backend 2>&1 | grep "BREAK GLASS"
    
    Мөр нь и-мэйл ба IP-г агуулна.
  3. Тэр session юу хийснийг:
    SELECT created_at, action, target_type, target_id, reason, ip
      FROM operator_audit
     WHERE operator_email = '<break-glass email>'
     ORDER BY created_at DESC LIMIT 100;
    

Засах.

  • Төлөвлөгөөт бол (бүх superadmin-ий TOTP алдагдсан г.м.): ажил дууссаны дараа нууц үгийг нь ЗААВАЛ солиж, сейфэнд шинээр хийнэ. Дараа нь энгийн superadmin бүртгэлүүдийг сэргээ.
  • Төлөвлөгөөт биш бол — зөвшөөрөлгүй хандалт гэж үз:
    UPDATE operator_accounts SET disabled_at = NOW() WHERE break_glass;
    UPDATE operator_sessions SET revoked_at = NOW() WHERE revoked_at IS NULL;
    
    Дараа нь CONTROL_PLANE_ALLOWED_CIDRS-аас танихгүй хаягийг хасаж, backend-ыг дахин асааж, operator_audit-ыг бүхэлд нь шалга.

Өргөжүүлэх. Шууд, хэн нэгнийг сэрээж. Энэ бол платформын хамгийн эрхтэй хаалга бөгөөд дуут дохио нь зориудаар чанга.

NexusControlPlaneUnrecordedWrite

Юу болсон. Консолын хүсэлт 500 буцаалаа. Хамгийн магадлалтай шалтгаан нь RequireAudit — Service.Do-г тойрч бичсэн handler-ийн хариуг барьсан.

Эхний 5 минут.

docker compose logs backend 2>&1 | grep -i "without an audit record"
docker compose logs backend 2>&1 | grep -i "control plane:" | tail -30
  • "answered successfully without an audit record" → кодын алдаа. Тухайн handler Do-г ашиглах ёстой. Мөрөнд бичигдсэн path нь хаанаас хайхыг хэлнэ.
  • "is not available (run the migrations up to 00049_control_plane)" → миграц ажиллаагүй. docker compose run --rm migrate up.
  • "permission denied for table ..." → operator role-д тухайн хүснэгтийн эрх олгогдоогүй. Энэ нь зөв ажиллаж буй хамгаалалт: query нь тэр хүснэгтийг унших ёстой эсэхийг эхлээд шийд, дараа нь миграцаар тодорхой нэмнэ.

Засах. Кодын алдаа тул засвар нь commit. Түр зуурын тойрох арга байхгүй — audit-гүй бичилтийг зөвшөөрөх нь энэ давхаргын утгыг бүхэлд нь алдагдуулна.

Өргөжүүлэх. Ажлын өдөр. Гэхдээ өгөгдлийн санд ул мөргүй өөрчлөлт орсон байж болзошгүй тул тухайн үйлдлийг гараар шалга.

eID нэвтрэлт

eID sign-in smoke

Юу. .github/workflows/eid-smoke.yml (2 цаг тутам) scripts/eid-smoke.sh-ээр POST /api/v1/auth/eid/start-ийг ширээний хөтөч шиг (callback-гүй QR session) дуудаж, 200 ба device_link_url (legacy https://…/dl?sessionId=…&vc=… эсвэл v3 …/dl?deviceLinkType=QR&elapsedSeconds=…&authCode=…) ирэхийг шаардана. Callback илгээхгүй: CA callback-тай session-ийг App2App гэж тоолдог тул батлагдахгүй smoke нь CA-ийн статистикийг App2App timeout-оор дүүргэж байв (2026-09-30 хүртэл 30 мин тутам). Унасан хуваарьт ажлыг GitHub имэйлээр мэдэгдэнэ (Alertmanager-т receiver тохируулаагүй).

Шалгах.

bash scripts/eid-smoke.sh https://nexus.gerege.net
docker compose logs backend 2>&1 | grep -i "eid" | tail -20
docker compose exec backend printenv EID_BASE_URL
  • 502 «session could not be started» → CA хариулсангүй. EID_BASE_URL-ийн зам буруу эсэхийг хар: rp.eidmongolia.mn нь root дээр хариулдаг, /v3 дор 404 (2026-09-29). ca-eid-ийн nginx логт энэ хостын хүсэлт ямар замаар, ямар статустай очсоныг тулга.
  • 401/403 CA-аас → RP secret эсвэл mTLS cert (EID_CLIENT_CERT_FILE) хүчингүй эсвэл хугацаа дууссан; CA-ийн RP-ийн IP allowlist.
  • 429 → энэ хостоос start-ийн хязгаар (30/мин) дүүрсэн; дахин ажиллуул.

Засах. Тохиргоо бол .env-ийг proxmox-gerege-net (stack/nexus)-ээр засаж backend-ыг дахин асаа. CA талын өөрчлөлт бол eID-ийн багт мэдэгд.

Хамраагүй. CA callback-ийг хүлээн авсан эсэхийг (callback_hosts) энэ шалгалт харахгүй — жинхэнэ зөвшөөрлийн signature.flowType (App2App) л үүнийг харуулна.