Docusaurus — Documentation Website
Tổng quan (Overview)
RAC sử dụng Docusaurus v3 để chuyển đổi toàn bộ tài liệu Markdown trong repo thành một static website có thể tìm kiếm, điều hướng và sử dụng offline. Website được tự động build và deploy lên Cloudflare Pages mỗi khi có commit vào main.
irp/ services/ templates/ tools/
↓ symlinks
website/docs/
↓ Docusaurus build
website/build/
↓ Wrangler (GitLab CI)
Cloudflare Pages → docs.nexa.mobi
Cấu trúc thư mục (Directory Structure)
website/ ← Docusaurus project root
├── docusaurus.config.js ← Cấu hình chính: plugins, themes, navbar
├── sidebars.js ← Định nghĩa sidebar cho 4 section
├── package.json ← Dependencies (Node.js)
├── docs/ ← Symlinks trỏ về nội dung thật
│ ├── irp → ../../irp/
│ ├── services → ../../services/
│ ├── templates→ ../../templates/
│ ├── tools → ../../tools/
│ └── resilience_as_code_architecture_plan.md → ../../resilience_as_code_architecture_plan.md
├── static/
│ └── img/runbooks/ ← Static assets (SVG diagrams, images)
└── src/
├── pages/index.js ← Landing page
└── css/custom.css ← Custom styles
Tại sao dùng symlinks?
Docusaurus cần đọc file từ một thư mục nằm bên trong project (website/docs/). Thay vì copy tài liệu (tạo ra hai source of truth) hoặc trỏ path: '../' về toàn bộ repo root (gây lỗi EMFILE — quá nhiều file descriptors để watch), RAC dùng symlinks trong website/docs/ trỏ chính xác về các thư mục nội dung.
Kèm theo cấu hình webpack resolve.symlinks: false để webpack không resolve symlink về đường dẫn thật (tránh lỗi metadata undefined khi render page).
Plugins đã cài đặt
1. @easyops-cn/docusaurus-search-local — Full-text Search
Loại: Theme
Cấu hình: themes array trong docusaurus.config.js
Build search index tại compile time — không cần external service, hoạt động offline. Hỗ trợ cả tiếng Anh và tiếng Việt.
// docusaurus.config.js
themes: [
['@easyops-cn/docusaurus-search-local', {
hashed: true,
language: ['en', 'vi'],
docsRouteBasePath: '/',
indexBlog: false,
}],
],
Lưu ý: Search không hoạt động trong
npm run start(dev server). Phải chạynpm run build && npm run serveđể test search locally.
2. @docusaurus/theme-mermaid — Diagram-as-Code
Loại: Theme
Cài đặt: npm install @docusaurus/theme-mermaid
Render Mermaid diagrams trực tiếp trong Markdown — không cần export ảnh. Hỗ trợ: sequenceDiagram, flowchart, erDiagram, gantt, gitGraph, v.v.
// docusaurus.config.js
markdown: { mermaid: true },
themes: ['@docusaurus/theme-mermaid'],
Cách dùng trong .md / .mdx:
```mermaid
sequenceDiagram
participant SRE
participant DB as PostgreSQL
SRE->>DB: systemctl stop postgresql
DB-->>SRE: Service stopped ✓
```
Khi nào dùng Mermaid:
- Sequence diagrams (tương tác giữa các service/người)
- Flowcharts đơn giản
- Git branching diagrams
- ER diagrams cho data model
3. docusaurus-plugin-drawio — Interactive Draw.io Diagrams
Loại: Plugin
Cài đặt: npm install docusaurus-plugin-drawio
Render file .drawio trực tiếp trong page — interactive zoom, pan, layer switching. Yêu cầu file có extension .mdx (cần JSX import syntax).
// docusaurus.config.js
plugins: [['drawio', {}]],
Cách dùng trong .mdx:
import Drawio from '@theme/Drawio';
import myDiagram from '!!raw-loader!./my-diagram.drawio';
<Drawio content={myDiagram} toolbar="zoom layers" />
Lưu ý quan trọng:
- File
.drawiophải nằm cùng thư mục với file.mdxđể import relative path hoạt động qua symlinks. - Chỉ dùng
.mdxextension (không phải.md) khi cần embed Drawio hoặc bất kỳ JSX component nào. - File
.drawiolà XML — có thể mở trực tiếp tại app.diagrams.net để chỉnh sửa.
Khi nào dùng Drawio vs Mermaid:
| Tiêu chí | Mermaid | Drawio |
|---|---|---|
| Cú pháp | Code (text-based) | GUI (drag & drop) |
| Phù hợp cho | Sequence, flowchart đơn giản | Architecture complex, custom layout |
| Git diff | Dễ review | XML khó đọc |
Cần .mdx? | Không | Có |
4. docusaurus-plugin-copy-page-button — Copy Page as Markdown
Loại: Plugin
Cài đặt: npm install docusaurus-plugin-copy-page-button
Thêm nút "Copy page" vào sidebar TOC — copy toàn bộ nội dung page thành Markdown để paste vào AI tool (ChatGPT, Claude, v.v.).
plugins: [
['docusaurus-plugin-copy-page-button', {
// Chỉ bật copy và view — ẩn link AI tools (site nội bộ, không public)
enabledActions: ['copy', 'view'],
}],
],
5. docusaurus-plugin-llms — LLM/AI Consumption Files
Loại: Plugin
Cài đặt: npm install docusaurus-plugin-llms
Tự động sinh /llms.txt (index) và /llms-full.txt (toàn bộ nội dung) tại build time theo chuẩn llmstxt.org. AI agent và LLM tools có thể truy cập toàn bộ RAC documentation qua hai file này.
plugins: [
['docusaurus-plugin-llms', {
generateLLMsTxt: true,
generateLLMsFullTxt: true,
docsDir: 'docs',
title: 'Resilience as Code — RAC Documentation',
excludeImports: true,
removeDuplicateHeadings: true,
}],
],
URL sau khi deploy:
https://docs.nexa.mobi/llms.txt— Index với linkshttps://docs.nexa.mobi/llms-full.txt— Toàn bộ content (dùng cho RAG)
6. docusaurus-plugin-image-zoom — Image Zoom
Loại: Plugin
Cài đặt: npm install docusaurus-plugin-image-zoom
Click vào bất kỳ ảnh nào trong .markdown để zoom full-screen (dùng medium-zoom).
// Thêm vào plugins[]
'docusaurus-plugin-image-zoom',
// Thêm vào themeConfig
zoom: {
selector: '.markdown img',
background: { light: 'rgb(255,255,255)', dark: 'rgb(30,30,30)' },
},
7. docusaurus-graph — Knowledge Graph
Loại: Plugin
Cài đặt: npm install docusaurus-graph
Render một trang /graph interactive cho phép xem toàn bộ mối quan hệ giữa các tài liệu RAC dưới dạng node-edge graph. Mỗi document là một node; field calls trong frontmatter tạo directed edge từ document đó đến document được gọi.
// docusaurus.config.js
plugins: [
['docusaurus-graph', {
docsDir: 'docs',
buildDir: 'build',
referencesTag: 'calls', // tên field trong frontmatter
sourcesTag: 'called_by', // reverse-link field (không bắt buộc phải điền)
}],
],
// themeConfig.navbar.items
{ to: '/graph', label: 'Graph', position: 'right' },
Cách thêm relationship cho một document mới:
---
type: Runbook
title: "My New Runbook"
# ... frontmatter khác ...
calls:
- payment-gateway
- escalation-policy
---
Định dạng node ID — docusaurus-graph dùng basename của file (không có extension, không có đường dẫn) làm node ID:
| File thật | Node ID trong calls |
|---|---|
irp/runbooks/restart-database.mdx | restart-database |
services/payment-gateway.md | payment-gateway |
irp/policies/escalation-policy.md | escalation-policy |
Lưu ý quan trọng:
docusaurus-graphdùng basename làm ID, không phải đường dẫn đầy đủ. Điều này có nghĩa là hai file cùng tên trong các thư mục khác nhau sẽ được coi là một node duy nhất (collision). Vì vậy, khi tạo runbook/playbook mới cho production, hãy đặt tên file unique (ví dụ:restart-payment-gateway-pods.mdthay vìexample.md). Các file tênexample.mdvàindex.mdkhông nên được dùng trongcalls.
Relationship hierarchy hiện tại của RAC:
example (playbook)
→ restart-database (runbook) → payment-gateway (service)
→ escalation-policy (policy)
→ payment-gateway (service)
Plugin tự inject button: docusaurus-graph tự thêm nút "Graph view" vào góc phải trên của navbar — không cần thêm navbar item thủ công.
Quy ước bảo trì:
- Chỉ cần khai báo
callstrên document gọi (caller) — không cầncalled_bytrên document được gọi. docusaurus-graphtự suy ra chiều ngược lại khi render graph.- Validator (
validate.py) không kiểm tra giá trị trongcalls— là basename, không phải markdown path.
TODO — Auto-extract graph edges từ inline markdown links: Hiện tại
callsđược khai báo thủ công trong frontmatter, nhưng các file.mdđã có đầy đủ relationship thông qua inline links[text](path.md)trong prose. Implement một trong hai hướng:
- Option A (
validate.pypost-pass): Thêm một bước vàovalidate.pyđể parse tất cả[text](path.md)links trong body, suy ra basename của target file, và tự động ghi vào fieldcallstrong frontmatter — đồng bộcallsvới thực tế của prose.- Option B (custom Docusaurus plugin): Viết plugin riêng thay thế
docusaurus-graph— đọc markdown body của mỗi doc trong quá trình build, extract links bằng regex, xây dựng graph data mà không cần frontmattercalls. Zero maintenance overhead phía content.Option B được ưu tiên vì không tạo ra hai source of truth (frontmatter + prose). Tham khảo
website/docusaurus.config.jscomment cho context đầy đủ.
Chạy locally (Local Development)
cd website
# Dev server (hot reload, search không hoạt động)
npm run start
# Production build + local preview (search hoạt động đầy đủ)
npm run build && npm run serve
# Xóa cache khi gặp lỗi lạ
npm run clear
Dev server và EMFILE error trên macOS: Nếu gặp lỗi
EMFILE: too many open files, càiwatchman:brew install watchman
CI/CD — Cloudflare Pages (GitLab CI)
Mỗi push lên bất kỳ branch nào sẽ trigger job deploy-docs trong .gitlab-ci.yml.
Luồng deploy
git push → GitLab CI trigger
→ node:22-alpine container
→ cd website && npm ci
→ npm run build (Docusaurus → website/build/)
→ npx wrangler pages deploy build
--project-name=$CF_PAGES_PROJECT_NAME
--branch=$CI_COMMIT_REF_SLUG
| Branch | Loại deployment | URL |
|---|---|---|
main | Production | docs.nexa.mobi |
| Bất kỳ branch khác | Preview | <branch-slug>.rac-docs.pages.dev |
CI/CD Variables (GitLab Settings → CI/CD → Variables)
| Variable | Mô tả | Lưu ý |
|---|---|---|
CLOUDFLARE_API_TOKEN | API token với quyền "Cloudflare Pages: Edit" | Bỏ chọn Protected để dùng trên feature branches |
CLOUDFLARE_ACCOUNT_ID | Account ID từ Cloudflare dashboard | Bỏ chọn Protected |
CF_PAGES_PROJECT_NAME | Tên project trên Cloudflare Pages (vd: rac-docs) | Bỏ chọn Protected |
Tạo Cloudflare Pages project (lần đầu)
cd website
npx wrangler pages project create rac-docs
# Production branch: main
Thêm nội dung mới (Adding Content)
Thêm tài liệu thông thường (.md)
Tạo file trong irp/, services/, templates/, hoặc tools/ — Docusaurus tự động pick up qua symlinks. Không cần cấu hình thêm.
Nếu file nằm trong thư mục đã có trong autogenerated sidebar → tự động xuất hiện.
Nếu không → thêm vào website/sidebars.js thủ công.
Thêm tài liệu có JSX/Mermaid/Drawio (.mdx)
Dùng extension .mdx khi file cần:
- Import JSX component (
<Drawio>, custom component) - Sử dụng Mermaid (thực ra
.mdcũng hoạt động với Mermaid)
---
type: Runbook
title: "My Runbook"
# ... frontmatter như bình thường
---
import Drawio from '@theme/Drawio';
import diagram from '!!raw-loader!./my-diagram.drawio';
## Architecture
<Drawio content={diagram} toolbar="zoom layers" />
## Sequence
```mermaid
sequenceDiagram
A->>B: Hello
### Thêm ảnh tĩnh
Đặt ảnh vào `website/static/img/` và reference bằng absolute path:
```markdown

Ảnh trong
website/static/được serve tại root URL — không đi qua symlinks.
Lưu ý kỹ thuật (Technical Notes)
Validator và static assets
tools/validate/validate.py chỉ kiểm tra links trỏ đến file .md/.mdx. Links đến ảnh (/img/...) hoặc file khác không bị validate — đây là behavior có chủ đích vì static assets không phải RAC documents.
resolve.symlinks: false
Docusaurus đăng ký doc file theo symlink path (website/docs/irp/index.md). Webpack mặc định resolve symlink về real path (irp/index.md) — dẫn đến mismatch làm props.content.metadata trả về undefined trên mọi page. Plugin nội bộ fix-symlink-resolution set resolve: { symlinks: false } để giữ path nhất quán.
Broken link warnings
Build luôn sinh [WARNING] Docusaurus found broken links cho:
- Template placeholder links (
/services/tên-service.md) — intentional - Links đến Python source files (
.py) trong tools docs — không được serve - Đây là warnings, không phải errors — build vẫn
[SUCCESS]