Skip to content

Commit d4205f0

Browse files
committed
feat(infra): add MkDocs documentation service
Add containerized MkDocs docs server deployed alongside API/App/Proxy: - Dockerfile.docs: Python 3.13 + ODBC 18 (same env as API for hook imports) - docker-compose.yml: docs service on :8002 - Deploy-OpenDateaubase.ps1: SkipDocs flag, service registration, status reporting - DeployHelpers.psm1: nginx /docs/ routing with WebSocket support for livereload - EnvironmentProfiles.psm1: DocsPort per environment (8012 staging, 8002 prod) Exposed via nginx proxy at /docs/ (e.g., http://hostname/docs/).
1 parent f4e3a1c commit d4205f0

5 files changed

Lines changed: 123 additions & 1 deletion

File tree

Dockerfile.docs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# MkDocs documentation site. The build hook (docs/hooks/call_orchestrator.py)
2+
# imports api.main + scripts/ + schema_dictionary/, so this image needs the same
3+
# API/ODBC environment as Dockerfile.api — it just runs mkdocs instead of uvicorn.
4+
FROM python:3.13-slim
5+
6+
ENV PYTHONUNBUFFERED=1 \
7+
PYTHONDONTWRITEBYTECODE=1
8+
9+
WORKDIR /app
10+
11+
# OS deps + MSSQL ODBC 18 (same as the API image; the hook imports api.main).
12+
RUN apt-get update && apt-get install -y --no-install-recommends \
13+
curl ca-certificates gcc g++ \
14+
unixodbc unixodbc-dev gnupg \
15+
&& rm -rf /var/lib/apt/lists/*
16+
17+
RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
18+
| gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg \
19+
&& curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list \
20+
| tee /etc/apt/sources.list.d/microsoft-prod.list > /dev/null \
21+
&& apt-get update \
22+
&& ACCEPT_EULA=Y apt-get install -y --no-install-recommends msodbcsql18 \
23+
&& rm -rf /var/lib/apt/lists/*
24+
25+
RUN pip install --no-cache-dir uv
26+
27+
# Dependency files first for layer caching
28+
COPY pyproject.toml uv.lock ./
29+
COPY src/ ./src/
30+
COPY importer/pyproject.toml ./importer/pyproject.toml
31+
32+
# --extra api: hook imports api.main. Default dev group pulls mkdocs-swagger-ui-tag.
33+
RUN uv sync --package open-dateaubase --extra api
34+
35+
# Everything the docs build reads at runtime
36+
COPY api/ ./api/
37+
COPY scripts/ ./scripts/
38+
COPY schema_dictionary/ ./schema_dictionary/
39+
COPY docs/ ./docs/
40+
COPY mkdocs.yml ./
41+
42+
ENV PATH="/app/.venv/bin:$PATH"
43+
44+
EXPOSE 8000
45+
46+
# ponytail: dev server (live-reloads doc edits) is fine for an internal docs site.
47+
# Swap to `mkdocs build` + a static server if the file-watcher's CPU ever matters.
48+
CMD ["uv", "run", "mkdocs", "serve", "--dev-addr", "0.0.0.0:8000"]

docker-compose.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,15 @@ services:
122122
- api
123123
restart: unless-stopped
124124

125+
docs:
126+
build:
127+
context: .
128+
dockerfile: Dockerfile.docs
129+
container_name: open_dateaubase_docs
130+
logging: *default-logging
131+
ports:
132+
- "8002:8000" # no proxy in compose; docs served directly on :8002
133+
restart: unless-stopped
134+
125135
volumes:
126136
mssql_data:

scripts/deploy/Deploy-OpenDateaubase.ps1

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
.PARAMETER SkipProxy
7979
Do not deploy/update the nginx proxy service.
8080
81+
.PARAMETER SkipDocs
82+
Do not deploy/update the MkDocs documentation service (served at /docs/).
83+
8184
.PARAMETER SkipLogViewer
8285
Do not deploy/update the OpenObserve log viewer + Vector log shipper services.
8386
@@ -150,6 +153,7 @@ param(
150153
[string]$ApiPort = '',
151154
[string]$AppPort = '',
152155
[string]$ProxyPort = '',
156+
[string]$DocsPort = '',
153157
[string]$LogViewerPort = '',
154158

155159
[string]$ServiceUser = 'LocalSystem',
@@ -159,6 +163,7 @@ param(
159163
[switch]$SkipApp,
160164
[switch]$SkipImporter,
161165
[switch]$SkipProxy,
166+
[switch]$SkipDocs,
162167
[switch]$SkipLogViewer,
163168
[switch]$Uninstall
164169
)
@@ -193,6 +198,7 @@ if ([string]::IsNullOrWhiteSpace($EnvFile)) { $EnvFile = Join-Path $InstallD
193198
if ([string]::IsNullOrWhiteSpace($ApiPort)) { $ApiPort = $envProfile.ApiPort }
194199
if ([string]::IsNullOrWhiteSpace($AppPort)) { $AppPort = $envProfile.AppPort }
195200
if ([string]::IsNullOrWhiteSpace($ProxyPort)) { $ProxyPort = $envProfile.ProxyPort }
201+
if ([string]::IsNullOrWhiteSpace($DocsPort)) { $DocsPort = $envProfile.DocsPort }
196202
if ([string]::IsNullOrWhiteSpace($LogViewerPort)) { $LogViewerPort = $envProfile.LogViewerPort }
197203

198204
# The .env file is only required when deploying the API (it carries DB_*).
@@ -214,6 +220,7 @@ if (-not $Uninstall -and -not $SkipImporter) {
214220
# never collide on a shared host.
215221
$SVC_API = "OpenDateaubase-$tag-API"
216222
$SVC_APP = "OpenDateaubase-$tag-App"
223+
$SVC_DOCS = "OpenDateaubase-$tag-Docs"
217224
$SVC_PROXY = "OpenDateaubase-$tag-Proxy"
218225
$SVC_LOGVIEW = "OpenDateaubase-$tag-LogViewer"
219226
$SVC_LOGSHIP = "OpenDateaubase-$tag-LogShip"
@@ -252,6 +259,7 @@ if ($Uninstall) {
252259
Remove-NssmService -NssmExe $nssmExe -ServiceName $SVC_LOGSHIP
253260
Remove-NssmService -NssmExe $nssmExe -ServiceName $SVC_LOGVIEW
254261
Remove-NssmService -NssmExe $nssmExe -ServiceName $SVC_PROXY
262+
Remove-NssmService -NssmExe $nssmExe -ServiceName $SVC_DOCS
255263
Remove-NssmService -NssmExe $nssmExe -ServiceName $SVC_APP
256264
Remove-NssmService -NssmExe $nssmExe -ServiceName $SVC_API
257265
}
@@ -376,6 +384,30 @@ if (-not $SkipApp) {
376384
Write-Step "Streamlit app started (access via http://localhost:$AppPort once proxy is up)." -Success
377385
}
378386

387+
# ---------------------------------------------------------------------------
388+
# Step 8b: Deploy MkDocs documentation service
389+
# ---------------------------------------------------------------------------
390+
391+
if (-not $SkipDocs) {
392+
Write-Step 'Deploying docs service (mkdocs serve)...'
393+
394+
Install-NssmService `
395+
-NssmExe $nssmExe `
396+
-ServiceName $SVC_DOCS `
397+
-Application $uvExe `
398+
-AppParameters "run mkdocs serve --dev-addr 127.0.0.1:$DocsPort" `
399+
-AppDirectory $InstallDir `
400+
-DisplayName "open_datEAUbase Docs ($tag)" `
401+
-Description "MkDocs documentation site for open_datEAUbase ($Environment; localhost:$DocsPort behind nginx /docs/)" `
402+
-StdoutLog (Join-Path $LogDir 'docs\stdout.log') `
403+
-StderrLog (Join-Path $LogDir 'docs\stderr.log') `
404+
-ServiceUser $ServiceUser `
405+
-ServicePassword $ServicePassword
406+
407+
Start-ManagedService -NssmExe $nssmExe -ServiceName $SVC_DOCS
408+
Write-Step "Docs service started (access via http://localhost:$ProxyPort/docs/ once proxy is up)." -Success
409+
}
410+
379411
# ---------------------------------------------------------------------------
380412
# Step 9: Register importer Scheduled Task
381413
# ---------------------------------------------------------------------------
@@ -567,6 +599,7 @@ if (-not $SkipProxy) {
567599
-AppPort $AppPort `
568600
-ProxyPort $ProxyPort `
569601
-LogDir $LogDir `
602+
-DocsPort $(if (-not $SkipDocs) { $DocsPort } else { '' }) `
570603
-LogViewerPort $(if (-not $SkipLogViewer) { $LogViewerPort } else { '' })
571604

572605
Install-NssmService `
@@ -609,6 +642,10 @@ if (-not $SkipApp) {
609642
$s = Get-Service $SVC_APP -ErrorAction SilentlyContinue
610643
$rows += [pscustomobject]@{ Component='App'; Type='Windows Service'; Name=$SVC_APP; Status=${s}?.Status; URL="http://localhost:$AppPort/" }
611644
}
645+
if (-not $SkipDocs) {
646+
$s = Get-Service $SVC_DOCS -ErrorAction SilentlyContinue
647+
$rows += [pscustomobject]@{ Component='Docs'; Type='Windows Service'; Name=$SVC_DOCS; Status=${s}?.Status; URL="http://localhost:$ProxyPort/docs/" }
648+
}
612649
if (-not $SkipProxy) {
613650
$s = Get-Service $SVC_PROXY -ErrorAction SilentlyContinue
614651
$rows += [pscustomobject]@{ Component='Proxy'; Type='Windows Service'; Name=$SVC_PROXY; Status=${s}?.Status; URL="http://localhost:$ProxyPort/" }
@@ -635,6 +672,9 @@ if (-not $SkipLogViewer) {
635672
if (-not $SkipProxy) {
636673
Write-Host ''
637674
Write-Host " Open in browser: http://$(hostname)/" -ForegroundColor Green
675+
if (-not $SkipDocs) {
676+
Write-Host " Documentation : http://$(hostname)/docs/" -ForegroundColor Green
677+
}
638678
if (-not $SkipLogViewer) {
639679
Write-Host " Inspect logs : http://$(hostname)/logs/" -ForegroundColor Green
640680
}

scripts/deploy/DeployHelpers.psm1

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,32 @@ function Write-NginxConf {
377377
[string]$AppPort,
378378
[string]$ProxyPort,
379379
[string]$LogDir,
380-
[string]$LogViewerPort = ''
380+
[string]$LogViewerPort = '',
381+
[string]$DocsPort = ''
381382
)
382383
# nginx requires forward slashes in paths
383384
$logDirFwd = $LogDir.Replace('\', '/')
384385

386+
# Optional MkDocs site (mkdocs serve), exposed under /docs/. The trailing
387+
# slash on proxy_pass strips the /docs/ prefix because mkdocs serves at root.
388+
# WebSocket headers cover mkdocs' livereload; the site renders fine regardless.
389+
$docsBlock = ''
390+
if ($DocsPort) {
391+
$docsBlock = @"
392+
393+
# MkDocs documentation site
394+
location = /docs { return 301 /docs/; }
395+
location /docs/ {
396+
proxy_pass http://127.0.0.1:$DocsPort/;
397+
proxy_http_version 1.1;
398+
proxy_set_header Upgrade `$http_upgrade;
399+
proxy_set_header Connection "upgrade";
400+
proxy_set_header Host `$host;
401+
proxy_set_header X-Real-IP `$remote_addr;
402+
}
403+
"@
404+
}
405+
385406
# Optional OpenObserve log viewer block, exposed under /logs/ (set via
386407
# ZO_BASE_URI=/logs on the service). Uses the same WebSocket-upgrade headers
387408
# as the Streamlit block because OpenObserve's UI streams live tail over ws.
@@ -454,6 +475,7 @@ http {
454475
proxy_set_header Host `$host;
455476
proxy_set_header X-Real-IP `$remote_addr;
456477
}
478+
$docsBlock
457479
$logViewerBlock
458480
}
459481
}

scripts/deploy/EnvironmentProfiles.psm1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ $script:EnvironmentProfiles = @{
3232
ApiPort = '8010'
3333
AppPort = '8511'
3434
ProxyPort = '8080'
35+
DocsPort = '8012' # mkdocs serve, exposed via nginx /docs/
3536
LogViewerPort = '5090' # OpenObserve UI, exposed via nginx /logs/
3637
AllowSeedData = $false # dev/demo seed never loaded into staging
3738
}
@@ -42,6 +43,7 @@ $script:EnvironmentProfiles = @{
4243
ApiPort = '8000'
4344
AppPort = '8501'
4445
ProxyPort = '80'
46+
DocsPort = '8002' # mkdocs serve, exposed via nginx /docs/
4547
LogViewerPort = '5080' # OpenObserve UI, exposed via nginx /logs/
4648
AllowSeedData = $false # dev/demo seed never loaded into production
4749
}

0 commit comments

Comments
 (0)