docs: Dockerfile + nginx + CI publish для deploy на ordinis.k8s.264.nstart.cloud/docs/

Multi-stage Dockerfile: yfm build (Node 20 alpine) → nginx 1.27 alpine
serving static HTML. Nginx config:
- /healthz endpoint для k8s probes
- /docs/ alias → /usr/share/nginx/html/ (Diplodoc делает relative links,
  alias корректно работает без URL rewriting)
- Hash-named assets (_bundle/) — cache 1y immutable
- HTML pages — cache 5min must-revalidate
- /docs (без trailing slash) → 301 → /docs/
- Security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy)

CI:
- build-docs (sanity check) — теперь только на non-main branches и MR
  (для preview без push). Production build идёт через docker-docs.
- docker-docs (NEW) — multi-stage Dockerfile build + push в repo.nstart.cloud
  через standard .docker_base + .publish_script pattern. Path-filtered на
  docs/**, manual через web UI.
- resolve-tag: emit RESOLVED_DOCS_TAG (= IMAGE_TAG, тот же что для backend).
- trigger-deploy-{staging,prod}: pass DOCS_IMAGE_TAG в downstream pipeline.

Local verify:
  cd docs && docker build -t ordinis-docs:test .
  docker run --rm -p 18088:8080 ordinis-docs:test
  curl localhost:18088/healthz       → 200
  curl -I localhost:18088/docs       → 301 → /docs/
  curl localhost:18088/docs/         → 200 index.html
  curl localhost:18088/docs/integration/api/auth.html → 200
  curl localhost:18088/docs/missing.html → 404

Соответствующие infra-side изменения (Helm chart docs deployment + Ingress
path /docs/, values, deploy script) — commit в ordinis-infra repo.
This commit is contained in:
Zimin A.N.
2026-05-07 23:44:03 +03:00
parent bcfb07f547
commit cfe26a07a7
3 changed files with 111 additions and 13 deletions
+32 -13
View File
@@ -118,16 +118,10 @@ maven-package:
- ordinis-migrations/src/main/resources/db/changelog/** - ordinis-migrations/src/main/resources/db/changelog/**
expire_in: 1 day expire_in: 1 day
# ─── BUILD: Integrator Guide (Diplodoc) ──────── # ─── BUILD: Integrator Guide (sanity check) ────
# Build static HTML documentation для consumers через @diplodoc/cli (yfm). # Sanity-check build для PRs и non-main branches. Полный image build
# Source — docs/ (Markdown + toc.yaml). Output — docs/_dist/ HTML. # идёт в publish stage (docker-docs). Здесь только verify что yfm
# # не выдаёт errors на изменения docs/**.
# Triggers:
# - auto при изменении docs/**
# - manual через web UI (для force-rebuild без code changes)
#
# Artifact `docs/_dist/` сохраняется неделю — pages-deploy job (TBD,
# когда host решён — GitLab Pages либо k8s ingress) подхватит из него.
build-docs: build-docs:
stage: build stage: build
image: repo.nstart.cloud/library/node:20-alpine image: repo.nstart.cloud/library/node:20-alpine
@@ -146,9 +140,10 @@ build-docs:
- docs/_dist - docs/_dist
expire_in: 1 week expire_in: 1 week
rules: rules:
- changes: *docs-changes - if: '$CI_COMMIT_BRANCH != "main"'
- if: '$CI_PIPELINE_SOURCE == "web"' changes: *docs-changes
when: manual - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes: *docs-changes
# ─── PUBLISH (Docker images) ─────────────────── # ─── PUBLISH (Docker images) ───────────────────
.docker_base: .docker_base:
@@ -228,6 +223,24 @@ docker-admin-ui:
# frontend нет — резолвили общий тег и helm пытался катить admin-ui:<commit> # frontend нет — резолвили общий тег и helm пытался катить admin-ui:<commit>
# которого не существовало. Layer-cache делает повторную сборку ~30 sec. # которого не существовало. Layer-cache делает повторную сборку ~30 sec.
# Integrator Guide (Diplodoc) — multi-stage Dockerfile (yfm build → nginx serving).
# Path-filtered: rebuild только при изменении docs/. Если docs не менялся,
# helm берёт previous tag через --reset-then-reuse-values.
docker-docs:
extends: .docker_base
needs: []
variables:
SVC: ordinis-docs
DOCKERFILE: docs/Dockerfile
BUILD_CONTEXT: docs/
script:
- *publish_script
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
changes: *docs-changes
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual
# ─── TRIGGER DEPLOY ─────────────────────────── # ─── TRIGGER DEPLOY ───────────────────────────
# Single resolve-tag (always runs) → один UPSTREAM_IMAGE_TAG. Path-filter # Single resolve-tag (always runs) → один UPSTREAM_IMAGE_TAG. Path-filter
# работает на уровне docker-* jobs (backend skip если бэк не менялся, # работает на уровне docker-* jobs (backend skip если бэк не менялся,
@@ -239,6 +252,10 @@ resolve-tag:
image: repo.nstart.cloud/library/alpine:3.20 image: repo.nstart.cloud/library/alpine:3.20
script: script:
- echo "RESOLVED_IMAGE_TAG=$IMAGE_TAG" > resolved.env - echo "RESOLVED_IMAGE_TAG=$IMAGE_TAG" > resolved.env
# DOCS_IMAGE_TAG может быть пустым если docker-docs не пересобирался
# (path-filter no-match). Helm в infra оставит старый tag через
# --reset-then-reuse-values если переменная пустая.
- echo "RESOLVED_DOCS_TAG=$IMAGE_TAG" >> resolved.env
- cat resolved.env - cat resolved.env
artifacts: artifacts:
reports: reports:
@@ -255,6 +272,7 @@ trigger-deploy-staging:
strategy: depend strategy: depend
variables: variables:
UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG
DOCS_IMAGE_TAG: $RESOLVED_DOCS_TAG
DEPLOY_ENV: staging DEPLOY_ENV: staging
rules: rules:
- if: '$CI_COMMIT_BRANCH == "main"' - if: '$CI_COMMIT_BRANCH == "main"'
@@ -268,6 +286,7 @@ trigger-deploy-prod:
strategy: depend strategy: depend
variables: variables:
UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG UPSTREAM_IMAGE_TAG: $RESOLVED_IMAGE_TAG
DOCS_IMAGE_TAG: $RESOLVED_DOCS_TAG
DEPLOY_ENV: prod DEPLOY_ENV: prod
rules: rules:
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
+33
View File
@@ -0,0 +1,33 @@
# Multi-stage build для Ordinis Integrator Guide.
# Stage 1: build HTML через @diplodoc/cli (yfm).
# Stage 2: nginx serving static files на /docs/ path.
ARG NODE_IMAGE=repo.nstart.cloud/library/node:20-alpine
ARG NGINX_IMAGE=repo.nstart.cloud/library/nginx:1.27-alpine
# ─── Stage 1: Build HTML ─────────────────────
FROM ${NODE_IMAGE} AS builder
WORKDIR /docs
# Layered cache: package.json + lockfile меняются реже чем content.
COPY package.json pnpm-lock.yaml ./
RUN corepack enable \
&& corepack prepare pnpm@9.0.0 --activate \
&& pnpm install --frozen-lockfile
# Copy остальное (toc, .yfm, markdown).
COPY .yfm toc.yaml index.md ./
COPY integration ./integration
RUN pnpm build
# ─── Stage 2: nginx ──────────────────────────
FROM ${NGINX_IMAGE}
COPY --from=builder /docs/_dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Healthcheck для k8s readiness/liveness.
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
CMD wget -q -O - http://localhost:8080/healthz || exit 1
EXPOSE 8080
+46
View File
@@ -0,0 +1,46 @@
# Ordinis Integrator Guide — nginx serving static HTML на /docs/.
# Diplodoc генерирует relative links — alias корректно работает без rewrite.
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
access_log /dev/stdout;
error_log /dev/stderr;
# K8s probe.
location = /healthz {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
# Documentation root.
location /docs/ {
alias /usr/share/nginx/html/;
index index.html;
try_files $uri $uri/ $uri.html =404;
# HTML — short cache (5 min). Новые deploy'ы быстро видны.
expires 5m;
add_header Cache-Control "public, must-revalidate";
# Hash-named assets — long cache. Diplodoc делает content-hashing
# в _bundle/ что safe для immutable cache.
location ~* /_bundle/.*\.(css|js|woff2?|ttf|eot|svg|png|jpg|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# Redirect /docs (без trailing slash) → /docs/.
location = /docs {
return 301 /docs/;
}
# Security headers.
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}