<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Agile Robin]]></title><description><![CDATA[living an Awesome Life]]></description><link>https://blog.robinjiang.com</link><image><url>https://cdn.hashnode.com/uploads/logos/658e5a4f93ea7e7ce37b2a6c/0fd4985d-ac43-4407-a1cb-2456793c135e.png</url><title>Agile Robin</title><link>https://blog.robinjiang.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 09 May 2026 13:38:31 GMT</lastBuildDate><atom:link href="https://blog.robinjiang.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[GitHub 开源项目仓库汇总（2026-04-25 更新）]]></title><description><![CDATA[GitHub 开源项目仓库汇总（2026-04-25 更新）

数据来源：IMA 知识库 GitHub 相关内容整理 | 每周自动更新
本期新增 15 个项目，总计收录 44 个优质开源项目

📊 本周更新亮点
本周新增 15 个项目，重点聚焦 AI Coding 与 MLOps 基础设施：

🤖 AI Coding 全景图：OpenClaw（356K ⭐）、Claude Code（113K ⭐）、opencode（145K ⭐）等领跑赛道
🧠 记忆与持久化：claude-mem（59K ...]]></description><link>https://blog.robinjiang.com/github-open-source-projects-summary-2026-04-25</link><guid isPermaLink="true">https://blog.robinjiang.com/github-open-source-projects-summary-2026-04-25</guid><category><![CDATA[AI]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[open source]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Sat, 25 Apr 2026 09:40:34 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-github-2026-04-25">GitHub 开源项目仓库汇总（2026-04-25 更新）</h1>
<blockquote>
<p>数据来源：IMA 知识库 GitHub 相关内容整理 | 每周自动更新
本期新增 15 个项目，总计收录 44 个优质开源项目</p>
</blockquote>
<h2 id="heading-8jtiidmnkzlkajmm7tmlrdkuq7ngrk">📊 本周更新亮点</h2>
<p>本周新增 <strong>15 个项目</strong>，重点聚焦 AI Coding 与 MLOps 基础设施：</p>
<ul>
<li>🤖 <strong>AI Coding 全景图</strong>：OpenClaw（356K ⭐）、Claude Code（113K ⭐）、opencode（145K ⭐）等领跑赛道</li>
<li>🧠 <strong>记忆与持久化</strong>：claude-mem（59K ⭐）、MemPalace（43.4K ⭐）解决 AI 跨会话记忆难题  </li>
<li>⚡ <strong>推理引擎加速</strong>：vLLM v0.19.1（76K ⭐）、Ollama（169K ⭐）、Unsloth（61K ⭐）覆盖从本地到生产的全栈推理需求</li>
<li>🛡️ <strong>AI SRE 元年</strong>：OpenSRE 开启 AI 驱动运维新时代，与 OpenTelemetry 深度集成</li>
<li>🌐 <strong>多 Agent 框架</strong>：Google ADK（8.2K ⭐）、Meta Llama Stack（6.4K ⭐）、OpenAI Agents（22K ⭐）三大厂商同台竞技</li>
</ul>
<hr />
<h2 id="heading-5paw5ake6ag555uu6ycf6kei">新增项目速览</h2>
<h3 id="heading-ai-coding-amp-agent-7">🤖 AI Coding &amp; Agent 工具（新增 7 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>OpenClaw</strong></td><td><a target="_blank" href="https://github.com/openclaw/openclaw">github.com/openclaw/openclaw</a></td><td>356K ⭐</td><td>个人AI助手框架，定位为"智能中枢"。衍生出NanoClaw轻量版及ACP协议生态，持续领跑AI Agent领域。</td></tr>
<tr>
<td><strong>opencode</strong></td><td><a target="_blank" href="https://github.com/anomalyco/opencode">github.com/anomalyco/opencode</a></td><td>145K ⭐</td><td>开源AI编码助手，功能强大，社区关注度迅速攀升。</td></tr>
<tr>
<td><strong>claude-mem</strong></td><td><a target="_blank" href="https://github.com/thedotmack/claude-mem">github.com/thedotmack/claude-mem</a></td><td>59K ⭐</td><td>Claude持久记忆层工具，彻底解决Claude无法跨会话记忆的问题。</td></tr>
<tr>
<td><strong>andrej-karpathy-skills</strong></td><td><a target="_blank" href="https://github.com/forrestchang/andrej-karpathy-skills">github.com/forrestchang/andrej-karpathy-skills</a></td><td>47.9K ⭐</td><td>基于Andrej Karpathy AI理念整理的精选技能库。</td></tr>
<tr>
<td><strong>openai-agents-python</strong></td><td><a target="_blank" href="https://github.com/openai/openai-agents-python">github.com/openai/openai-agents-python</a></td><td>22K ⭐</td><td>OpenAI官方Python版代理框架，本周新增3526 stars，热度持续攀升。</td></tr>
<tr>
<td><strong>GenericAgent</strong></td><td><a target="_blank" href="https://github.com/lsdefine/GenericAgent">github.com/lsdefine/GenericAgent</a></td><td>-</td><td>通用AI代理框架，Token消耗是同类工具的1/6，高效低耗是核心竞争力。</td></tr>
<tr>
<td><strong>Hermes Agent</strong></td><td><a target="_blank" href="https://github.com/NousResearch/hermes-agent">github.com/NousResearch/hermes-agent</a></td><td>80K+ ⭐</td><td>NousResearch发布的开源AI代理框架v0.10.0，社区活跃度高。</td></tr>
</tbody>
</table>
</div><h3 id="heading-5">⚡ 机器学习框架与推理引擎（新增 5 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>vLLM</strong></td><td><a target="_blank" href="https://github.com/vllm-project/vllm">github.com/vllm-project/vllm</a></td><td>76K ⭐</td><td>高吞吐量LLM推理引擎，本周发布v0.19.1rc0，PyTorch 2.10升级。生产级LLM推理的事实标准。</td></tr>
<tr>
<td><strong>Ollama</strong></td><td><a target="_blank" href="https://github.com/ollama/ollama">github.com/ollama/ollama</a></td><td>169K ⭐</td><td>一键运行LLM的本地推理平台，支持海量开源模型，与LangChain、Dify等框架深度集成。</td></tr>
<tr>
<td><strong>Unsloth</strong></td><td><a target="_blank" href="https://github.com/unslothai/unsloth">github.com/unslothai/unsloth</a></td><td>61K ⭐</td><td>微调速度提升2倍、内存减少70%的轻量微调框架，已支持Llama 4全系列。</td></tr>
<tr>
<td><strong>Google ADK Python</strong></td><td><a target="_blank" href="https://github.com/google/adk-python">github.com/google/adk-python</a></td><td>8.2K+ ⭐</td><td>Google Agent Development Kit，构建多智能体系统的官方框架，两周内暴增8200 stars。</td></tr>
<tr>
<td><strong>Meta Llama Stack</strong></td><td><a target="_blank" href="https://github.com/meta-llama/llama-stack">github.com/meta-llama/llama-stack</a></td><td>6.4K+ ⭐</td><td>Llama 4系列的统一部署栈，提供推理、微调、SFT、RLHF全链路工具链。</td></tr>
</tbody>
</table>
</div><h3 id="heading-3">🛠️ 基础设施与运维（新增 3 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>OpenSRE</strong></td><td><a target="_blank" href="https://github.com/Tracer-Cloud/opensre">github.com/Tracer-Cloud/opensre</a></td><td>~1.5K ⭐</td><td>面向AI时代的开源SRE工具包，允许开发者构建自己的AI SRE代理，与OpenTelemetry生态深度集成。</td></tr>
<tr>
<td><strong>LangChain</strong></td><td><a target="_blank" href="https://github.com/langchain-ai/langchain">github.com/langchain-ai/langchain</a></td><td>133K ⭐</td><td>Agent工程平台，与LangGraph（29K ⭐）配合，提供图形化Agent构建能力，覆盖RAG、工具调用全链路。</td></tr>
<tr>
<td><strong>MCP Servers</strong></td><td><a target="_blank" href="https://github.com/modelcontextprotocol/servers">github.com/modelcontextprotocol/servers</a></td><td>84K ⭐</td><td>MCP协议服务器集合，为LLM提供标准化工具调用能力，正成为Agent工具互操作的行业标准。</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-44">完整项目清单（44 项）</h2>
<h3 id="heading-4">一、数据平台与可视化（4 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>DataCap</strong></td><td><a target="_blank" href="https://github.com/devlive-community/datacap">github.com/devlive-community/datacap</a></td><td>开源数据中台软件，支持多数据源管理与监控，实现数据转换、集成、可视化等功能。Java/JavaScript开发，遵循Apache 2.0协议。</td></tr>
<tr>
<td><strong>Chartbrew</strong></td><td><a target="_blank" href="https://github.com/chartbrew/chartbrew">github.com/chartbrew/chartbrew</a></td><td>开源报表平台，用于从API、MySQL、PostgreSQL、MongoDB等数据源创建实时报表看板。</td></tr>
<tr>
<td><strong>Shaper</strong></td><td><a target="_blank" href="https://github.com/taleshape-com/shaper">github.com/taleshape-com/shaper</a></td><td>免费开源、基于SQL（DuckDB）的数据可视化工具，用于创建交互式数据仪表盘。后端Go，前端React，遵循MPL-2.0协议。</td></tr>
<tr>
<td><strong>Data Formulator</strong></td><td><a target="_blank" href="https://github.com/microsoft/data-formulator">github.com/microsoft/data-formulator</a></td><td>微软研究院开发的AI驱动数据可视化工具，融合拖拽UI与自然语言输入，无需复杂编码即可完成数据转换和可视化。</td></tr>
</tbody>
</table>
</div><h3 id="heading-ai-16">二、AI 编程与代理（16 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>BMAD-METHOD</strong></td><td><a target="_blank" href="https://github.com/bmad-code-org/BMAD-METHOD">github.com/bmad-code-org/BMAD-METHOD</a></td><td>37k+</td><td>现象级开源项目，将大厂敏捷开发流程引入AI世界，构建含多角色（PM、架构师、开发者）虚拟开发团队。</td></tr>
<tr>
<td><strong>GitNexus</strong></td><td><a target="_blank" href="https://github.com/abhigyanpatwari/GitNexus">github.com/abhigyanpatwari/GitNexus</a></td><td>-</td><td>将代码结构转化为可查询知识图谱，解决大型项目因结构知识仅存于少数人脑而导致的协作瓶颈。</td></tr>
<tr>
<td><strong>code-review-graph</strong></td><td><a target="_blank" href="https://github.com/tirth8205/code-review-graph">github.com/tirth8205/code-review-graph</a></td><td>-</td><td>专为Claude Code等AI编码助手设计的本地知识图谱工具，利用Tree-sitter构建代码结构地图，减少6.8倍审查Token。</td></tr>
<tr>
<td><strong>Maestro</strong></td><td><a target="_blank" href="https://github.com/pedramamini/Maestro">github.com/pedramamini/Maestro</a></td><td>-</td><td>跨平台桌面应用，编排管理多个AI代理与项目，支持Git Worktrees并行开发、Auto Run自动化。</td></tr>
<tr>
<td><strong>Acontext</strong></td><td><a target="_blank" href="https://github.com/memodb-io/Acontext">github.com/memodb-io/Acontext</a></td><td>-</td><td>面向自学智能体的上下文数据平台，统一存储会话上下文、任务记录与产出，将经验沉淀为长期记忆。</td></tr>
<tr>
<td><strong>Scrapling</strong></td><td><a target="_blank" href="https://github.com/ccheshirecat/scrapling">github.com/ccheshirecat/scrapling</a></td><td>-</td><td>面向本地AI Agent生态的高性能Python爬虫库，具备绕过Cloudflare Turnstile等反爬、网页结构自适应能力。</td></tr>
<tr>
<td><strong>multica</strong></td><td><a target="_blank" href="https://github.com/multica-ai/multica">github.com/multica-ai/multica</a></td><td>9.3k+</td><td>多Coding Agent协调平台，将Claude Code、Codex等汇成「舰队」，可认领任务、报告进度、被阻塞时自动建Issue。</td></tr>
<tr>
<td><strong>MemPalace</strong></td><td><a target="_blank" href="https://github.com/MemPalace/mempalace">github.com/MemPalace/mempalace</a></td><td>43.4k+</td><td>AI记忆系统框架，"逐字存储+向量搜索"架构，ChromaDB+SQLite本地检索，零API费用。LongMemEval评分最高。</td></tr>
<tr>
<td><strong>Archon</strong></td><td><a target="_blank" href="https://github.com/coleam00/Archon">github.com/coleam00/Archon</a></td><td>17k+</td><td>AI编码Harness构建器，通过YAML声明式定义AI编码工作流（计划→实现→验证→审查→PR），确保Agent确定性和可重复。</td></tr>
<tr>
<td><strong>OpenClaw</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/openclaw/openclaw">github.com/openclaw/openclaw</a></td><td>356k</td><td>个人AI助手框架，定位为"智能中枢"，Star突破356K，持续领跑AI Agent领域。</td></tr>
<tr>
<td><strong>opencode</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/anomalyco/opencode">github.com/anomalyco/opencode</a></td><td>145k</td><td>开源AI编码助手，功能强大，社区关注度迅速攀升。</td></tr>
<tr>
<td><strong>claude-mem</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/thedotmack/claude-mem">github.com/thedotmack/claude-mem</a></td><td>59k</td><td>Claude持久记忆层工具，解决Claude无法跨会话记忆的问题。</td></tr>
<tr>
<td><strong>andrej-karpathy-skills</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/forrestchang/andrej-karpathy-skills">github.com/forrestchang/andrej-karpathy-skills</a></td><td>47.9k</td><td>基于Andrej Karpathy AI理念整理的精选技能库。</td></tr>
<tr>
<td><strong>openai-agents-python</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/openai/openai-agents-python">github.com/openai/openai-agents-python</a></td><td>22k</td><td>OpenAI官方Python版代理框架，持续高热度增长。</td></tr>
<tr>
<td><strong>GenericAgent</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/lsdefine/GenericAgent">github.com/lsdefine/GenericAgent</a></td><td>-</td><td>通用AI代理框架，Token消耗是同类工具的1/6。</td></tr>
<tr>
<td><strong>Hermes Agent</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/NousResearch/hermes-agent">github.com/NousResearch/hermes-agent</a></td><td>80k+</td><td>NousResearch发布的开源AI代理框架v0.10.0。</td></tr>
</tbody>
</table>
</div><h3 id="heading-3-1">三、文档与知识处理（3 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Docs2KG</strong></td><td><a target="_blank" href="https://github.com/abhigyanpatwari/Docs2KG">github.com/abhigyanpatwari/Docs2KG</a></td><td>将PDF、邮件、Excel等多种异构非结构化文档统一转换为可查、可推理、可追溯的多模态知识图谱。</td></tr>
<tr>
<td><strong>Markwhen</strong></td><td><a target="_blank" href="https://github.com/mark-when/markwhen">github.com/mark-when/markwhen</a></td><td>将类Markdown文本快速转化为交互式、可缩放和筛选的层叠时间线。</td></tr>
<tr>
<td><strong>Scriban</strong></td><td><a target="_blank" href="https://github.com/scriban/scriban">github.com/scriban/scriban</a></td><td>适用于.NET的快速、安全且轻量级脚本语言与文本模板引擎，语法简洁，性能高，无第三方依赖。</td></tr>
</tbody>
</table>
</div><h3 id="heading-5-1">四、开发工具与库（5 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>chaser-oxide</strong></td><td><a target="_blank" href="https://github.com/ccheshirecat/chaser-oxide">github.com/ccheshirecat/chaser-oxide</a></td><td>基于Rust的库，通过修改Chromium CDP通信协议实现反爬隐藏，从协议层消除自动化痕迹。</td></tr>
<tr>
<td><strong>Pinchtab</strong></td><td><a target="_blank" href="https://github.com/pinchtab/pinchtab">github.com/pinchtab/pinchtab</a></td><td>12MB的Go二进制工具，将Chrome浏览器转为LLM可直接控制的接口，轻量、稳定、防检测。</td></tr>
<tr>
<td><strong>Amphi-ETL</strong></td><td><a target="_blank" href="https://github.com/amphi-ai/amphi-etl">github.com/amphi-ai/amphi-etl</a></td><td>开源低代码数据管道生成器，实时生成基于pandas/DuckDB的标准Python代码，支持与AI模型协同。</td></tr>
<tr>
<td><strong>chart-visualization-skills</strong></td><td><a target="_blank" href="https://github.com/antvis/chart-visualization-skills">github.com/antvis/chart-visualization-skills</a></td><td>蚂蚁AntV团队打造适配Claude Code的AI原生可视化Skill，覆盖图表、信息图、地图等六大模块。</td></tr>
<tr>
<td><strong>excalidraw-diagram-generator</strong></td><td><a target="_blank" href="https://github.com/mark-when/excalidraw-diagram-generator">github.com/mark-when/excalidraw-diagram-generator</a></td><td>让AI直接生成Excalidraw JSON文件绘制流程图、架构图等9类图形。</td></tr>
</tbody>
</table>
</div><h3 id="heading-3-2">五、学习资源与课程（3 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>cs249r_book</strong></td><td><a target="_blank" href="https://github.com/harvard-edge/cs249r_book">github.com/harvard-edge/cs249r_book</a></td><td>哈佛团队打造的开源教材《Machine Learning Systems》，教授如何在现实环境中让AI模型稳定、可靠运行。</td></tr>
<tr>
<td><strong>Generative AI for Beginners</strong></td><td><a target="_blank" href="https://github.com/Microsoft/generative-ai-for-beginners">github.com/Microsoft/generative-ai-for-beginners</a></td><td>微软开源AI课程，针对文科生、产品经理及零基础跨行者，侧重应用技能。</td></tr>
<tr>
<td><strong>AI-For-Beginners</strong></td><td><a target="_blank" href="https://github.com/Microsoft/AI-For-Beginners">github.com/Microsoft/AI-For-Beginners</a></td><td>微软开源AI课程，针对有计算机背景的学习者，侧重底层原理与框架训练。</td></tr>
</tbody>
</table>
</div><h3 id="heading-6">六、基础设施与运维（6 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>OpenObserve</strong></td><td><a target="_blank" href="https://github.com/openobserve/openobserve">github.com/openobserve/openobserve</a></td><td>-</td><td>开源云原生可观测性平台，存储成本较Elasticsearch低约140倍，支持PB级数据处理。</td></tr>
<tr>
<td><strong>Consul</strong></td><td>HashiCorp官方仓库</td><td>-</td><td>HashiCorp的服务发现、健康检查、服务网格与配置管理工具。</td></tr>
<tr>
<td><strong>OpenSRE</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/Tracer-Cloud/opensre">github.com/Tracer-Cloud/opensre</a></td><td>~1.5K</td><td>面向AI时代的开源SRE工具包，构建AI SRE代理，能自主监控、诊断、解决基础设施问题。</td></tr>
<tr>
<td><strong>LangChain</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/langchain-ai/langchain">github.com/langchain-ai/langchain</a></td><td>133K</td><td>Agent工程平台，配合LangGraph提供图形化Agent构建能力，覆盖RAG、工具调用全链路。</td></tr>
<tr>
<td><strong>MCP Servers</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/modelcontextprotocol/servers">github.com/modelcontextprotocol/servers</a></td><td>84K</td><td>MCP协议服务器集合，正成为Agent工具互操作的行业标准。</td></tr>
<tr>
<td><strong>awesome-llm-apps</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/Shubhamsaboo/awesome-llm-apps">github.com/Shubhamsaboo/awesome-llm-apps</a></td><td>106K</td><td>LLM应用案例精选集，覆盖各类AI应用开发场景的优质学习资源。</td></tr>
</tbody>
</table>
</div><h3 id="heading-5-2">七、机器学习框架与推理引擎（5 项）⭐本周新增分类</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>vLLM</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/vllm-project/vllm">github.com/vllm-project/vllm</a></td><td>76K</td><td>高吞吐量LLM推理引擎，生产级LLM推理的事实标准。最新v0.19.1rc0支持PyTorch 2.10。</td></tr>
<tr>
<td><strong>Ollama</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/ollama/ollama">github.com/ollama/ollama</a></td><td>169K</td><td>一键运行LLM的本地推理平台，本地LLM运行的入门首选，与主流框架深度集成。</td></tr>
<tr>
<td><strong>Unsloth</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/unslothai/unsloth">github.com/unslothai/unsloth</a></td><td>61K</td><td>微调速度提升2倍、内存减少70%，已支持Llama 4全系列。中小团队微调的最优解。</td></tr>
<tr>
<td><strong>Google ADK Python</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/google/adk-python">github.com/google/adk-python</a></td><td>8.2K+</td><td>Google官方多智能体开发框架，两周内暴增8200 stars，是2026年Multi-Agent开发的重要框架。</td></tr>
<tr>
<td><strong>Meta Llama Stack</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/meta-llama/llama-stack">github.com/meta-llama/llama-stack</a></td><td>6.4K+</td><td>Llama 4系列的统一部署栈，MoE架构（Scout/Maverick）生产部署必备框架。</td></tr>
</tbody>
</table>
</div><h3 id="heading-2">八、具身机器人与数据集（2 项）</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>链接</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>AGIBOT WORLD 2026</strong></td><td><a target="_blank" href="https://github.com/OpenDriveLab/Agibot-World">github.com/OpenDriveLab/Agibot-World</a></td><td>-</td><td>全场景具身机器人开源数据集（100%真实世界），G2机器人采集，覆盖工厂/家庭场景。IROS 2025 Best Paper。</td></tr>
<tr>
<td><strong>Gemini Robotics-ER 1.6</strong></td><td><a target="_blank" href="https://deepmind.google/blog/gemini-robotics-er-1-6/">DeepMind Blog</a></td><td>-</td><td>DeepMind发布的具身推理模型1.6版，仪表读取精度93%，感知专家ER + 执行者VLA分离架构。</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-8jticdpobnnm67nu5orqhmsyfmgls">📈 项目统计汇总</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>分类</td><td>上周项目数</td><td>本周新增</td><td>合计</td></tr>
</thead>
<tbody>
<tr>
<td>数据平台与可视化</td><td>4</td><td>0</td><td>4</td></tr>
<tr>
<td>AI 编程与代理</td><td>9</td><td>7</td><td>16</td></tr>
<tr>
<td>文档与知识处理</td><td>3</td><td>0</td><td>3</td></tr>
<tr>
<td>开发工具与库</td><td>5</td><td>0</td><td>5</td></tr>
<tr>
<td>学习资源与课程</td><td>3</td><td>0</td><td>3</td></tr>
<tr>
<td>基础设施与运维</td><td>2</td><td>4</td><td>6</td></tr>
<tr>
<td>机器学习框架与推理引擎</td><td>0</td><td>5</td><td>5</td></tr>
<tr>
<td>具身机器人与数据集</td><td>2</td><td>0</td><td>2</td></tr>
<tr>
<td><strong>合计</strong></td><td><strong>29</strong></td><td><strong>+15</strong></td><td><strong>44</strong></td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-8jtjcdmnkzlkajotovlirmtj7lr58">📌 本周趋势洞察</h2>
<ol>
<li><strong>AI Coding 赛道成熟化</strong>：Cursor 估值 50 亿美元标志着市场从探索期进入成熟期；多 Agent 协作（multica、Google ADK）正在取代单 Agent 模式</li>
<li><strong>推理引擎格局清晰</strong>：vLLM（生产）、Ollama（本地）、Unsloth（微调）三足鼎立，各有侧重</li>
<li><strong>MoE 架构主流化</strong>：Llama 4、Qwen 3.6、DeepSeek V3 均采用 MoE，"70B 级"智能在 13B 级硬件上运行成为可能</li>
<li><strong>AI SRE 元年</strong>：OpenSRE 等工具标志着 AI 驱动运维从概念走向开源落地，与 OpenTelemetry 的深度集成值得关注</li>
<li><strong>记忆持久化成标配</strong>：claude-mem、MemPalace、Acontext 等工具井喷，AI 持久记忆从研究走向工程实践</li>
</ol>
<hr />
<blockquote>
<p>来源：Agile Robin 技术笔记 | 每周由 WorkBuddy 自动整理
Tags: GitHub, 开源项目, AI编程, 大模型, 推理引擎, DevOps, 具身机器人, 开发工具, 2026</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Grafana Loki 版本变更与存储系统升级完全指南（2.x → 3.x）]]></title><description><![CDATA[前言
Grafana Loki 作为云原生日志聚合的事实标准，在过去几年经历了从 2.0 到 3.5 的巨大演进。其中，存储系统的重构是最核心的变化——从 BoltDB 本地存储到 TSDB Single Store，从大索引文件到 Bloom Filter 加速查询。
本文系统梳理 Loki 各主要版本的变更要点，着重分析存储系统的演进脉络，并提供阿里云 OSS 作为存储后端的完整配置指南。

一、Loki 版本发布概览




版本发布日期生命周期状态关键里程碑



2.02021-03E...]]></description><link>https://blog.robinjiang.com/grafana-loki-2x-3x</link><guid isPermaLink="true">https://blog.robinjiang.com/grafana-loki-2x-3x</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 24 Apr 2026 14:18:26 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-5ymn6kia">前言</h2>
<p>Grafana Loki 作为云原生日志聚合的事实标准，在过去几年经历了从 2.0 到 3.5 的巨大演进。其中，<strong>存储系统的重构</strong>是最核心的变化——从 BoltDB 本地存储到 TSDB Single Store，从大索引文件到 Bloom Filter 加速查询。</p>
<p>本文系统梳理 Loki 各主要版本的变更要点，<strong>着重分析存储系统的演进脉络</strong>，并提供阿里云 OSS 作为存储后端的完整配置指南。</p>
<hr />
<h2 id="heading-loki">一、Loki 版本发布概览</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>版本</td><td>发布日期</td><td>生命周期状态</td><td>关键里程碑</td></tr>
</thead>
<tbody>
<tr>
<td><strong>2.0</strong></td><td>2021-03</td><td>EOL</td><td>引入 Helm Chart、Ingester 分片</td></tr>
<tr>
<td><strong>2.8</strong></td><td>2023-07</td><td>EOL</td><td><strong>TSDB 索引正式推荐</strong></td></tr>
<tr>
<td><strong>2.9</strong></td><td>2023-09</td><td>EOL</td><td>TSDB 成熟、Bloom Compactor（实验）</td></tr>
<tr>
<td><strong>3.0</strong></td><td>2024-04</td><td>维护中</td><td><strong>默认 v13 Schema + TSDB</strong>，弃用旧存储</td></tr>
<tr>
<td><strong>3.1</strong></td><td>2024-07</td><td>维护中</td><td>性能优化、Bug 修复</td></tr>
<tr>
<td><strong>3.2</strong></td><td>2024-09</td><td>维护中</td><td>OTLP 改进、查询增强</td></tr>
<tr>
<td><strong>3.3</strong></td><td>2024-11</td><td>维护中</td><td><strong>Bloom Filter V3 Block</strong>，结构化元数据索引</td></tr>
<tr>
<td><strong>3.4</strong></td><td>2025-02</td><td>维护中</td><td><strong>标准化对象存储</strong>、Thanos 支持、Sizing 指南</td></tr>
<tr>
<td><strong>3.5</strong></td><td>2025-04</td><td>最新</td><td>区域感知 Ingestor、性能改进</td></tr>
</tbody>
</table>
</div><p>Loki 采用<strong>月度小版本 + 季度大版本</strong>的发布策略，每个大版本约 3-4 个月的活跃维护期。</p>
<hr />
<h2 id="heading-5lqm44cb5a2y5yko57o757uf5ryu6lb5rex5bqm5yig5p6q">二、存储系统演进深度分析</h2>
<h3 id="heading-5a2y5yko5p625p6e5oc76kei">存储架构总览</h3>
<p>Loki 的存储分为两个核心部分：</p>
<ul>
<li><strong>Index（索引）</strong>：存储标签（Label），支持快速查询过滤</li>
<li><strong>Chunks（日志块）</strong>：存储实际日志内容，压缩后存储到对象存储</li>
<li><strong>Schema Config</strong>：决定不同时间段的存储方式和索引格式</li>
</ul>
<h3 id="heading-57si5byv5a2y5yko55qe5zub5liq6zi25q61">索引存储的四个阶段</h3>
<h4 id="heading-loki-1x">阶段一：单体索引（Loki 1.x，已完全移除）</h4>
<div class="hn-table">
<table>
<thead>
<tr>
<td>存储后端</td><td>说明</td><td>移除时间</td></tr>
</thead>
<tbody>
<tr>
<td><strong>BoltDB（本地）</strong></td><td>单机本地存储，不支持多副本</td><td>2.x 中弃用，3.0 移除</td></tr>
<tr>
<td><strong>Cassandra</strong></td><td>分布式索引，运维复杂</td><td>3.0 弃用，计划 4.0 移除</td></tr>
<tr>
<td><strong>DynamoDB</strong></td><td>AWS 原生存储，成本高</td><td>3.0 弃用，计划 4.0 移除</td></tr>
<tr>
<td><strong>BigTable</strong></td><td>GCP 原生存储</td><td>3.0 弃用</td></tr>
</tbody>
</table>
</div><h4 id="heading-boltdb-shipperloki-15-27">阶段二：BoltDB-Shipper（Loki 1.5 - 2.7）</h4>
<p>BoltDB-Shipper 将索引文件从本地同步到对象存储，解决了单点问题，但索引文件大、查询需下载整个文件、扩展性仍然有限。</p>
<h4 id="heading-tsdb-loki-28">阶段三：TSDB 索引（Loki 2.8+，推荐）</h4>
<p>这是 Loki 存储架构的<strong>根本性变革</strong>，借鉴 Prometheus TSDB：</p>
<ul>
<li>索引和 Chunks 统一存储到对象存储（<strong>Single Store</strong>）</li>
<li>按时间分片生成 Block，支持高效的 Head Compaction</li>
<li>原生支持 <strong>Bloom Filter</strong> 加速查询</li>
<li>支持结构化元数据（<strong>Structured Metadata</strong>）</li>
</ul>
<h4 id="heading-single-store-tsdb-bloom-filter-v3loki-33">阶段四：Single Store TSDB + Bloom Filter V3（Loki 3.3+）</h4>
<ul>
<li><strong>Block Schema V3</strong>：重新设计 Block 结构，集成 Structured Metadata 的 Bloom 索引</li>
<li><strong>Bloom Compactor</strong>：独立的 Bloom Filter 压缩组件</li>
<li><strong>动态查询分片</strong>：根据数据分布自动分片查询</li>
</ul>
<h3 id="heading-schema">Schema 版本演进</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Schema</td><td>引入版本</td><td>索引类型</td><td>说明</td></tr>
</thead>
<tbody>
<tr>
<td>v1-v10</td><td>1.x</td><td>旧式索引</td><td>BoltDB/Cassandra 等</td></tr>
<tr>
<td>v11</td><td>2.0+</td><td>BoltDB-Shipper</td><td>改进分片</td></tr>
<tr>
<td>v12</td><td>2.8</td><td>TSDB</td><td>TSDB 索引首次可用</td></tr>
<tr>
<td><strong>v13</strong></td><td>3.0</td><td>TSDB</td><td><strong>3.0 默认</strong>，支持 Structured Metadata</td></tr>
</tbody>
</table>
</div><h3 id="heading-5zce54mi5pys5a2y5yko6yen54k55yy5pu0">各版本存储重点变更</h3>
<h4 id="heading-loki-302024-04">Loki 3.0（2024-04）—— 重大里程碑</h4>
<p>这是 Loki 最重要的版本之一：</p>
<ul>
<li>默认 Schema 切换为 <strong>v13</strong></li>
<li>弃用所有旧存储后端（BoltDB、Cassandra、DynamoDB、BigTable）</li>
<li><strong>Structured Metadata 默认启用</strong></li>
<li>TSDB Single Store：索引和 Chunks 统一存储</li>
</ul>
<p>升级到 3.0 的 schema_config 示例：</p>
<pre><code class="lang-yaml"><span class="hljs-attr">schema_config:</span>
  <span class="hljs-attr">configs:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">from:</span> <span class="hljs-string">"2024-01-01"</span>
      <span class="hljs-attr">store:</span> <span class="hljs-string">tsdb</span>
      <span class="hljs-attr">object_store:</span> <span class="hljs-string">s3</span>
      <span class="hljs-attr">schema:</span> <span class="hljs-string">v13</span>
      <span class="hljs-attr">index:</span>
        <span class="hljs-attr">prefix:</span> <span class="hljs-string">loki_index_</span>
        <span class="hljs-attr">period:</span> <span class="hljs-string">24h</span>
</code></pre>
<h4 id="heading-loki-332024-11-bloom-filter">Loki 3.3（2024-11）—— Bloom Filter 重大升级</h4>
<ul>
<li>引入 <strong>Block Schema V3</strong></li>
<li>Bloom Filter 索引结构化元数据</li>
<li>查询延迟显著降低（大标签集场景）</li>
</ul>
<h4 id="heading-loki-342025-02">Loki 3.4（2025-02）—— 标准化与易用性</h4>
<ul>
<li><strong>标准化对象存储配置</strong>（统一 S3/GCS/Azure/OSS 接口）</li>
<li><strong>Thanos 远程存储支持</strong></li>
<li><strong>Sizing 指南</strong>：官方首次提供详细的容量规划指南</li>
</ul>
<p>Sizing 参考：</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>日志量/天</td><td>推荐架构</td><td>最小资源</td></tr>
</thead>
<tbody>
<tr>
<td>&lt; 20GB</td><td>Monolithic</td><td>2C4G</td></tr>
<tr>
<td>20-200GB</td><td>Simple Scalable</td><td>4C8G x 2</td></tr>
<tr>
<td>&gt; 200GB</td><td>Microservice</td><td>独立部署各组件</td></tr>
</tbody>
</table>
</div><h4 id="heading-loki-352025-04">Loki 3.5（2025-04）—— 最新版本</h4>
<ul>
<li><strong>区域感知 Ingestor</strong>（Zone-Aware Ingester）</li>
<li>Helm Chart 改进（1x.pico size 支持）</li>
<li>改进 Bloom Compactor</li>
</ul>
<h3 id="heading-5a2y5yko5zco56uv5byd55so5pe26ze057q">存储后端弃用时间线</h3>
<pre><code class="lang-text">2.8      2.9       3.0        3.3       3.5       4.0(?)
TSDB推荐  TSDB默认  旧存储弃用  Bloom V3  区域感知   旧存储移除

         BoltDB   已弃用（3.0）
         Cassandra 已弃用（3.0），计划 4.0 移除
         DynamoDB  已弃用（3.0），计划 4.0 移除
</code></pre>
<hr />
<h2 id="heading-oss">三、阿里云 OSS 存储配置实战</h2>
<p>Loki 原生使用 S3 SDK，阿里云 OSS 完全兼容 S3 API，这是<strong>最稳定且官方推荐</strong>的方式。</p>
<h3 id="heading-yaml">完整 YAML 配置</h3>
<pre><code class="lang-yaml"><span class="hljs-comment"># loki-config.yaml — 阿里云 OSS 存储配置（S3 兼容模式）</span>
<span class="hljs-comment"># 适用于 Loki 3.x</span>

<span class="hljs-attr">schema_config:</span>
  <span class="hljs-attr">configs:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">from:</span> <span class="hljs-string">"2024-01-01"</span>
      <span class="hljs-attr">store:</span> <span class="hljs-string">tsdb</span>
      <span class="hljs-attr">object_store:</span> <span class="hljs-string">s3</span>
      <span class="hljs-attr">schema:</span> <span class="hljs-string">v13</span>
      <span class="hljs-attr">index:</span>
        <span class="hljs-attr">prefix:</span> <span class="hljs-string">loki_index_</span>
        <span class="hljs-attr">period:</span> <span class="hljs-string">24h</span>

<span class="hljs-attr">storage_config:</span>
  <span class="hljs-attr">aws:</span>
    <span class="hljs-attr">s3:</span> <span class="hljs-string">s3_storage</span>
    <span class="hljs-attr">s3forcepathstyle:</span> <span class="hljs-literal">true</span>      <span class="hljs-comment"># OSS 必须启用 path-style</span>

  <span class="hljs-attr">s3_storage:</span>
    <span class="hljs-comment"># OSS Endpoint（K8s Pod 通过 VPC 内网访问）</span>
    <span class="hljs-attr">endpoint:</span> <span class="hljs-string">https://oss-cn-beijing-internal.aliyuncs.com</span>
    <span class="hljs-attr">region:</span> <span class="hljs-string">cn-beijing</span>

    <span class="hljs-attr">bucketNames:</span>
      <span class="hljs-attr">chunks:</span> <span class="hljs-string">loki-chunks</span>       <span class="hljs-comment"># 日志块存储桶</span>
      <span class="hljs-attr">ruler:</span> <span class="hljs-string">loki-ruler</span>         <span class="hljs-comment"># 告警规则存储桶</span>
      <span class="hljs-attr">admin:</span> <span class="hljs-string">loki-admin</span>         <span class="hljs-comment"># 管理数据存储桶</span>

    <span class="hljs-comment"># 访问凭证（生产环境建议用 RAM Role）</span>
    <span class="hljs-attr">access_key_id:</span> <span class="hljs-string">${OSS_ACCESS_KEY_ID}</span>
    <span class="hljs-attr">secret_access_key:</span> <span class="hljs-string">${OSS_SECRET_ACCESS_KEY}</span>

    <span class="hljs-comment"># 连接参数</span>
    <span class="hljs-attr">insecure:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">http_config:</span>
      <span class="hljs-attr">idle_conn_timeout:</span> <span class="hljs-string">90s</span>
      <span class="hljs-attr">response_header_timeout:</span> <span class="hljs-string">0s</span>
      <span class="hljs-attr">insecure_skip_verify:</span> <span class="hljs-literal">false</span>

    <span class="hljs-comment"># 重试策略</span>
    <span class="hljs-attr">max_retries:</span> <span class="hljs-number">5</span>
    <span class="hljs-attr">backoff_config:</span>
      <span class="hljs-attr">min_period:</span> <span class="hljs-string">100ms</span>
      <span class="hljs-attr">max_period:</span> <span class="hljs-string">5s</span>
      <span class="hljs-attr">max_retries:</span> <span class="hljs-number">10</span>

  <span class="hljs-attr">tsdb_shipper:</span>
    <span class="hljs-attr">active_index_directory:</span> <span class="hljs-string">/loki/tsdb-index</span>
    <span class="hljs-attr">cache_location:</span> <span class="hljs-string">/loki/tsdb-cache</span>

<span class="hljs-attr">compactor:</span>
  <span class="hljs-attr">working_directory:</span> <span class="hljs-string">/loki/compactor</span>
  <span class="hljs-attr">compaction_interval:</span> <span class="hljs-string">10m</span>
  <span class="hljs-attr">retention_enabled:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">delete_request_store:</span> <span class="hljs-string">s3</span>

<span class="hljs-attr">limits_config:</span>
  <span class="hljs-attr">retention_period:</span> <span class="hljs-string">30d</span>
  <span class="hljs-attr">retention_stream:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">'{namespace="prod"}'</span>
      <span class="hljs-attr">priority:</span> <span class="hljs-number">1</span>
      <span class="hljs-attr">period:</span> <span class="hljs-string">90d</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">'{namespace="dev"}'</span>
      <span class="hljs-attr">priority:</span> <span class="hljs-number">2</span>
      <span class="hljs-attr">period:</span> <span class="hljs-string">7d</span>
</code></pre>
<h3 id="heading-oss-endpoint">OSS Endpoint 参考</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>区域</td><td>外网 Endpoint</td><td>内网 Endpoint</td></tr>
</thead>
<tbody>
<tr>
<td>华北 2（北京）</td><td>oss-cn-beijing.aliyuncs.com</td><td>oss-cn-beijing-internal.aliyuncs.com</td></tr>
<tr>
<td>华东 1（杭州）</td><td>oss-cn-hangzhou.aliyuncs.com</td><td>oss-cn-hangzhou-internal.aliyuncs.com</td></tr>
<tr>
<td>华东 2（上海）</td><td>oss-cn-shanghai.aliyuncs.com</td><td>oss-cn-shanghai-internal.aliyuncs.com</td></tr>
<tr>
<td>华南 1（深圳）</td><td>oss-cn-shenzhen.aliyuncs.com</td><td>oss-cn-shenzhen-internal.aliyuncs.com</td></tr>
</tbody>
</table>
</div><h3 id="heading-55sf5lqn546v5akd5pya5l2z5a6e6le1">生产环境最佳实践</h3>
<p><strong>凭证管理</strong>：推荐使用 K8s RAM Role（阿里云 ack-pod-identity-webhook），而非硬编码 AccessKey。</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Pod annotation 方式自动注入</span>
<span class="hljs-comment"># pod-identity.alibabacloud.com/inject: "true"</span>
<span class="hljs-comment"># pod-identity.alibabacloud.com/role-arn: acs:ram::<span class="hljs-doctag">xxx:</span>role/loki-oss-role</span>
</code></pre>
<p><strong>OSS Bucket 策略</strong>：Loki 需要以下权限：<code>oss:PutObject</code>、<code>oss:GetObject</code>、<code>oss:DeleteObject</code>、<code>oss:ListBucket</code>，分别对应 chunks、ruler、admin 三个 Bucket。</p>
<hr />
<h2 id="heading-5zub44cb5y2h57qn6lev57q5zu5bu66k6u">四、升级路线图建议</h2>
<h3 id="heading-29-3x">从 2.9 升级到 3.x</h3>
<pre><code class="lang-text">2.9.x (TSDB + v12)
  |
  +-- Step 1: 升级到 2.9.x 最新补丁，确保 TSDB 稳定
  |
  +-- Step 2: 添加 v13 Schema period_config
  |
  +-- Step 3: 升级到 3.0.x，验证弃用警告
  |
  +-- Step 4: 逐步到 3.4+，使用标准化存储配置
  |
  +-- Step 5: 考虑 3.5（区域感知 Ingestor）
</code></pre>
<h3 id="heading-boltdb-shipper-tsdb">从 BoltDB-Shipper 迁移到 TSDB</h3>
<pre><code class="lang-yaml"><span class="hljs-comment"># 迁移过渡期配置：双 Schema 并存</span>
<span class="hljs-attr">schema_config:</span>
  <span class="hljs-attr">configs:</span>
    <span class="hljs-comment"># 旧数据保持 BoltDB-Shipper</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">from:</span> <span class="hljs-string">"2020-01-01"</span>
      <span class="hljs-attr">store:</span> <span class="hljs-string">boltdb-shipper</span>
      <span class="hljs-attr">object_store:</span> <span class="hljs-string">s3</span>
      <span class="hljs-attr">schema:</span> <span class="hljs-string">v12</span>
      <span class="hljs-attr">index:</span>
        <span class="hljs-attr">prefix:</span> <span class="hljs-string">loki_boltdb_</span>
        <span class="hljs-attr">period:</span> <span class="hljs-string">24h</span>
    <span class="hljs-comment"># 新数据切换到 TSDB</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">from:</span> <span class="hljs-string">"2024-01-01"</span>
      <span class="hljs-attr">store:</span> <span class="hljs-string">tsdb</span>
      <span class="hljs-attr">object_store:</span> <span class="hljs-string">s3</span>
      <span class="hljs-attr">schema:</span> <span class="hljs-string">v13</span>
      <span class="hljs-attr">index:</span>
        <span class="hljs-attr">prefix:</span> <span class="hljs-string">loki_tsdb_</span>
        <span class="hljs-attr">period:</span> <span class="hljs-string">24h</span>
</code></pre>
<h3 id="heading-5o6o6i2q55uu5qch54mi5pys">推荐目标版本</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>场景</td><td>推荐版本</td><td>理由</td></tr>
</thead>
<tbody>
<tr>
<td><strong>新部署</strong></td><td><strong>3.5.x</strong></td><td>最新稳定版，包含所有优化</td></tr>
<tr>
<td><strong>生产升级</strong></td><td><strong>3.4.x</strong></td><td>标准化存储 + Sizing 指南成熟</td></tr>
<tr>
<td><strong>仍用 BoltDB</strong></td><td><strong>尽快迁至 TSDB</strong></td><td>旧后端即将在 4.0 移除</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-5lqu44cb5a2y5yko6ycj5z6l5a55qu">五、存储选型对比</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>维度</td><td>BoltDB-Shipper</td><td>TSDB (Single Store)</td></tr>
</thead>
<tbody>
<tr>
<td><strong>索引存储</strong></td><td>对象存储（大文件）</td><td>TSDB Block（小文件、按时间分片）</td></tr>
<tr>
<td><strong>查询性能</strong></td><td>需下载整个索引文件</td><td>按需加载 Block，支持 Bloom 加速</td></tr>
<tr>
<td><strong>运维复杂度</strong></td><td>低</td><td>中（需配置 Compactor）</td></tr>
<tr>
<td><strong>扩展性</strong></td><td>一般</td><td>优秀</td></tr>
<tr>
<td><strong>Bloom Filter</strong></td><td>不支持</td><td>原生支持（3.0+）</td></tr>
<tr>
<td><strong>结构化元数据</strong></td><td>不支持</td><td>支持（3.0+）</td></tr>
<tr>
<td><strong>社区状态</strong></td><td>弃用</td><td>活跃开发</td></tr>
<tr>
<td><strong>阿里云 OSS</strong></td><td>支持</td><td>支持（推荐）</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-5oc757ut">总结</h2>
<p>Loki 的存储系统已经完成了从「能用」到「好用」的跨越。TSDB Single Store 的引入统一了索引和数据的存储路径，Bloom Filter 大幅降低了查询延迟，标准化存储配置也让多云部署变得更加容易。</p>
<p>对于新部署，直接使用 <strong>Loki 3.5 + TSDB + OSS</strong> 是最佳选择。对于现有集群，建议尽快规划从 BoltDB-Shipper 到 TSDB 的迁移——旧后端将在 4.0 版本中被正式移除。</p>
<hr />
<p><em>参考资料：<a target="_blank" href="https://grafana.com/docs/loki/latest/">Grafana Loki 官方文档</a> | <a target="_blank" href="https://grafana.com/docs/loki/latest/release-notes/">Loki Release Notes</a> | <a target="_blank" href="https://grafana.com/docs/loki/latest/configure/storage/">Loki 存储配置</a> | <a target="_blank" href="https://endoflife.date/grafana-loki">Loki End-of-Life</a></em></p>
]]></content:encoded></item><item><title><![CDATA[Migrating Rundeck from H2 to PostgreSQL: A Complete Guide]]></title><description><![CDATA[If you've been running Rundeck with its default H2 embedded database, you've probably already hit the wall: slow queries as job history grows, no proper connection pooling, and the constant anxiety of]]></description><link>https://blog.robinjiang.com/migrating-rundeck-from-h2-to-postgresql-a-complete-guide</link><guid isPermaLink="true">https://blog.robinjiang.com/migrating-rundeck-from-h2-to-postgresql-a-complete-guide</guid><category><![CDATA[database]]></category><category><![CDATA[Devops]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[rundeck]]></category><category><![CDATA[SRE]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Tue, 21 Apr 2026 00:15:40 GMT</pubDate><content:encoded><![CDATA[<p>If you've been running Rundeck with its default H2 embedded database, you've probably already hit the wall: slow queries as job history grows, no proper connection pooling, and the constant anxiety of a file-based database in production. PostgreSQL fixes all of that.</p>
<p>This guide walks you through two migration paths — pick the one that fits your situation.</p>
<hr />
<h2>Why Migrate Away from H2?</h2>
<p>Rundeck ships with H2 for a reason: zero-config, works out of the box. But H2 has real limitations at scale:</p>
<ul>
<li><p><strong>No concurrent writes</strong> — H2 struggles under parallel job execution</p>
</li>
<li><p><strong>File corruption risk</strong> — a hard shutdown can corrupt the <code>.mv.db</code> file</p>
</li>
<li><p><strong>No external tooling</strong> — can't use pg_dump, replication, or your existing DB monitoring</p>
</li>
<li><p><strong>Memory pressure</strong> — H2 loads data into the JVM heap</p>
</li>
</ul>
<p>PostgreSQL gives you connection pooling, proper ACID guarantees, <code>pg_dump</code> for backups, and the ability to run Standby replicas. For any team running more than a handful of jobs, it's the right call.</p>
<hr />
<h2>Before You Start: Understand What Lives in the Database</h2>
<p>Rundeck stores the following in its database:</p>
<table>
<thead>
<tr>
<th>Data Type</th>
<th>Migration Notes</th>
</tr>
</thead>
<tbody><tr>
<td>Project definitions</td>
<td>Exportable via API archive</td>
</tr>
<tr>
<td>Job definitions</td>
<td>Included in project export</td>
</tr>
<tr>
<td>Execution history &amp; logs</td>
<td>Log files stay on disk; metadata in DB</td>
</tr>
<tr>
<td>Users &amp; ACL tokens</td>
<td>Recreate manually or via config</td>
</tr>
<tr>
<td>Scheduled jobs</td>
<td>Preserved in job definitions</td>
</tr>
</tbody></table>
<p>Execution log <em>content</em> lives in <code>/var/lib/rundeck/logs/</code>, not the database — so you don't need to migrate that.</p>
<hr />
<h2>Two Migration Strategies</h2>
<h3>Strategy A: Clean Start + Project Import (Recommended)</h3>
<p>Best for: Most teams. You keep all your job definitions and project configs. You lose execution history records in the DB (but not the actual log files on disk).</p>
<p><strong>Effort:</strong> Low | <strong>Risk:</strong> Low | <strong>Downtime:</strong> ~30 minutes</p>
<h3>Strategy B: Full H2 → PostgreSQL Data Migration</h3>
<p>Best for: Teams that need to preserve execution history records in the DB for audit/compliance.</p>
<p><strong>Effort:</strong> High | <strong>Risk:</strong> Medium | <strong>Downtime:</strong> 1–3 hours</p>
<hr />
<h2>Strategy A: Clean Start + Project Import</h2>
<h3>Step 1 — Back Up Everything</h3>
<pre><code class="language-bash"># Stop Rundeck first
systemctl stop rundeckd

# Back up config files
cp /etc/rundeck/rundeck-config.properties /backup/
cp /etc/rundeck/framework.properties /backup/

# Back up the H2 database files (just in case)
cp -r /var/lib/rundeck/data /backup/rundeck-data-h2/

# Back up project files on disk
cp -r /var/lib/rundeck/projects /backup/rundeck-projects/
cp -r /var/lib/rundeck/logs /backup/rundeck-logs/
</code></pre>
<p>Export each project via the Rundeck API (Rundeck must be running for this):</p>
<pre><code class="language-bash"># Start Rundeck temporarily to export
systemctl start rundeckd

# Export all projects via API
TOKEN="your-api-token"
RUNDECK_URL="http://localhost:4440"

for project in \((curl -s -H "X-Rundeck-Auth-Token: \)TOKEN" \
  "$RUNDECK_URL/api/41/projects" | jq -r '.[].name'); do
  echo "Exporting: $project"
  curl -s -H "X-Rundeck-Auth-Token: $TOKEN" \
    "\(RUNDECK_URL/api/41/project/\)project/export" \
    -o "/backup/project-${project}.zip"
done

# Stop again before DB switch
systemctl stop rundeckd
</code></pre>
<hr />
<h3>Step 2 — Install and Configure PostgreSQL</h3>
<pre><code class="language-bash"># Ubuntu/Debian
apt-get install -y postgresql postgresql-contrib

# CentOS/RHEL 8+
dnf install -y postgresql-server postgresql-contrib
postgresql-setup --initdb
systemctl enable --now postgresql
</code></pre>
<p>Create the Rundeck database and user:</p>
<pre><code class="language-bash">su - postgres
psql &lt;&lt; 'EOF'
CREATE DATABASE rundeck ENCODING 'UTF8';
CREATE USER rundeckuser WITH PASSWORD 'your_strong_password_here';
GRANT ALL PRIVILEGES ON DATABASE rundeck TO rundeckuser;
\c rundeck
GRANT ALL PRIVILEGES ON SCHEMA public TO rundeckuser;
EOF
</code></pre>
<hr />
<h3>Step 3 — Configure pg_hba.conf</h3>
<p>Edit <code>/etc/postgresql/&lt;version&gt;/main/pg_hba.conf</code> (Debian/Ubuntu) or <code>/var/lib/pgsql/data/pg_hba.conf</code> (RHEL):</p>
<pre><code class="language-plaintext"># Allow rundeckuser from localhost
host    rundeck         rundeckuser     127.0.0.1/32    scram-sha-256
host    rundeck         rundeckuser     ::1/128         scram-sha-256
</code></pre>
<p>Reload PostgreSQL:</p>
<pre><code class="language-bash">systemctl reload postgresql
</code></pre>
<p>Verify connectivity:</p>
<pre><code class="language-bash">psql -U rundeckuser -h 127.0.0.1 -d rundeck -c "SELECT version();"
</code></pre>
<hr />
<h3>Step 4 — Update rundeck-config.properties</h3>
<p>Open <code>/etc/rundeck/rundeck-config.properties</code> and replace the H2 datasource block:</p>
<pre><code class="language-properties"># === REMOVE or comment out the H2 config ===
# dataSource.url = jdbc:h2:file:/var/lib/rundeck/data/rundeckdb;...

# === ADD PostgreSQL config ===
dataSource.driverClassName = org.postgresql.Driver
dataSource.url = jdbc:postgresql://127.0.0.1:5432/rundeck
dataSource.username = rundeckuser
dataSource.password = your_strong_password_here
dataSource.dialect = org.hibernate.dialect.PostgreSQLDialect

# Connection pool tuning (recommended)
dataSource.properties.maxActive = 50
dataSource.properties.maxIdle = 25
dataSource.properties.minIdle = 5
dataSource.properties.initialSize = 5
dataSource.properties.validationQuery = SELECT 1
dataSource.properties.testOnBorrow = true
</code></pre>
<blockquote>
<p><strong>Note:</strong> Rundeck 4.x+ bundles the PostgreSQL JDBC driver — no manual download needed. For older versions, download <code>postgresql-&lt;version&gt;.jar</code> and place it in <code>/var/lib/rundeck/lib/</code>.</p>
</blockquote>
<hr />
<h3>Step 5 — Start Rundeck and Let It Initialize</h3>
<pre><code class="language-bash">systemctl start rundeckd

# Watch the logs for successful schema creation
tail -f /var/log/rundeck/service.log
</code></pre>
<p>Look for:</p>
<pre><code class="language-plaintext">Hibernate: create table rduser ...
Grails application running at http://localhost:4440/
</code></pre>
<p>If you see <code>Connection refused</code> or <code>FATAL: password authentication failed</code>, double-check <code>pg_hba.conf</code> and your credentials.</p>
<hr />
<h3>Step 6 — Import Projects Back</h3>
<pre><code class="language-bash">TOKEN="your-new-api-token"
RUNDECK_URL="http://localhost:4440"

for zipfile in /backup/project-*.zip; do
  project=\((basename "\)zipfile" .zip | sed 's/project-//')
  echo "Importing: $project"

  # Create project first if it doesn't exist
  curl -s -X POST \
    -H "X-Rundeck-Auth-Token: $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"$project\"}" \
    "$RUNDECK_URL/api/41/projects"

  # Import archive
  curl -s -X POST \
    -H "X-Rundeck-Auth-Token: $TOKEN" \
    -H "Content-Type: application/zip" \
    --data-binary "@$zipfile" \
    "\(RUNDECK_URL/api/41/project/\)project/import?importExecutions=true"
done
</code></pre>
<hr />
<h2>Strategy B: Full Data Migration from H2</h2>
<blockquote>
<p>Use this only if you need historical execution records preserved in the database.</p>
</blockquote>
<h3>The Core Challenge</h3>
<p>H2 and PostgreSQL have different SQL dialects. H2's <code>SCRIPT TO</code> command exports valid H2 SQL, but that SQL won't run cleanly in PostgreSQL without transformation.</p>
<h3>Step 1 — Export H2 with the H2 Console</h3>
<p>Identify the H2 version Rundeck is using:</p>
<pre><code class="language-bash">find /var/lib/rundeck -name "h2-*.jar" 2&gt;/dev/null
# or
ls /var/lib/rundeck/bootstrap/
</code></pre>
<p>Run the H2 Script tool with the matching version:</p>
<pre><code class="language-bash">systemctl stop rundeckd

java -cp /path/to/h2-&lt;version&gt;.jar org.h2.tools.Script \
  -url "jdbc:h2:/var/lib/rundeck/data/rundeckdb" \
  -user sa \
  -password "" \
  -script /tmp/rundeck_h2_export.sql
</code></pre>
<h3>Step 2 — Transform the SQL</h3>
<pre><code class="language-bash">cp /tmp/rundeck_h2_export.sql /tmp/rundeck_pg_import.sql

# Remove H2-specific statements
sed -i '/^SET /d' /tmp/rundeck_pg_import.sql
sed -i '/^CREATE USER IF NOT EXISTS/d' /tmp/rundeck_pg_import.sql
sed -i '/^ALTER TABLE.*ADD CONSTRAINT.*CHECK/d' /tmp/rundeck_pg_import.sql

# Fix identity columns
sed -i 's/BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH [0-9]* INCREMENT BY 1)/BIGSERIAL/g' \
  /tmp/rundeck_pg_import.sql

# Fix boolean literals
sed -i "s/\bTRUE\b/true/g; s/\bFALSE\b/false/g" /tmp/rundeck_pg_import.sql
</code></pre>
<blockquote>
<p><strong>Tip:</strong> H2 exports may include vendor-specific syntax that varies by version. Always test imports in a staging environment first and fix errors iteratively.</p>
</blockquote>
<h3>Step 3 — Import to PostgreSQL</h3>
<pre><code class="language-bash">psql -U rundeckuser -d rundeck -f /tmp/rundeck_pg_import.sql 2&gt;&amp;1 | tee /tmp/import_log.txt

# Check for errors
grep -i "error\|fatal" /tmp/import_log.txt
</code></pre>
<p>Fix any errors, then update <code>rundeck-config.properties</code> (same as Strategy A, Step 4) and restart.</p>
<hr />
<h2>Post-Migration Verification</h2>
<p>Run this checklist after switching to PostgreSQL:</p>
<pre><code class="language-bash"># 1. Service status
systemctl status rundeckd

# 2. Database tables created
psql -U rundeckuser -d rundeck -c "\dt" | wc -l
# Should show 30+ tables

# 3. Check for connection errors in logs
grep -i "HikariPool\|Cannot acquire connection\|ORA-\|PSQLException" \
  /var/log/rundeck/service.log | tail -20

# 4. Test a job execution end-to-end
# 5. Verify ACL tokens still work
# 6. Confirm scheduled jobs are firing
</code></pre>
<hr />
<h2>Performance Tuning Tips</h2>
<p>Once you're on PostgreSQL, a few quick wins:</p>
<p><strong>Connection pooling</strong> — Rundeck uses HikariCP internally. The defaults are conservative; bump them for busy instances:</p>
<pre><code class="language-properties">dataSource.properties.maximumPoolSize = 50
dataSource.properties.minimumIdle = 10
dataSource.properties.connectionTimeout = 30000
dataSource.properties.idleTimeout = 600000
</code></pre>
<p><strong>PostgreSQL autovacuum</strong> — Execution history tables grow fast. Make sure autovacuum is running and <code>work_mem</code> is set appropriately in <code>postgresql.conf</code>:</p>
<pre><code class="language-plaintext">work_mem = 64MB
maintenance_work_mem = 256MB
autovacuum = on
</code></pre>
<p><strong>Indexes</strong> — If execution history queries are slow, check that the <code>execution</code> table indexes on <code>project</code>, <code>date_started</code>, and <code>status</code> exist.</p>
<hr />
<h2>Common Errors and Fixes</h2>
<table>
<thead>
<tr>
<th>Error</th>
<th>Cause</th>
<th>Fix</th>
</tr>
</thead>
<tbody><tr>
<td><code>FATAL: password authentication failed</code></td>
<td>Wrong password or pg_hba auth method</td>
<td>Check credentials + pg_hba.conf</td>
</tr>
<tr>
<td><code>Connection refused (127.0.0.1:5432)</code></td>
<td>PostgreSQL not running</td>
<td><code>systemctl start postgresql</code></td>
</tr>
<tr>
<td><code>Permission denied for schema public</code></td>
<td>PostgreSQL 15+ changed defaults</td>
<td>Run <code>GRANT ALL ON SCHEMA public TO rundeckuser</code></td>
</tr>
<tr>
<td><code>No suitable driver found</code></td>
<td>Missing JDBC jar (old Rundeck)</td>
<td>Add <code>postgresql.jar</code> to <code>/var/lib/rundeck/lib/</code></td>
</tr>
<tr>
<td><code>Table already exists</code></td>
<td>Partial init ran before</td>
<td>Drop and recreate the database, restart clean</td>
</tr>
<tr>
<td><code>UnsupportedOperationException: dialect</code></td>
<td>Missing dialect config</td>
<td>Add <code>dataSource.dialect = org.hibernate.dialect.PostgreSQLDialect</code></td>
</tr>
</tbody></table>
<hr />
<h2>Wrapping Up</h2>
<p>Migrating Rundeck to PostgreSQL is a one-time investment that pays off immediately in stability, performance, and operational visibility. Strategy A (clean start + project import) is the pragmatic path for most teams and can be done in under an hour.</p>
<p>If you're running Rundeck on Kubernetes (KubernetesExecutor or similar), the same <code>rundeck-config.properties</code> approach applies — just mount the config as a Secret and point the JDBC URL at your PostgreSQL service.</p>
]]></content:encoded></item><item><title><![CDATA[GitHub 开源项目仓库汇总（2026-04-18 更新 | 新增 6 项）]]></title><description><![CDATA[GitHub 开源项目仓库汇总（2026-04-18 更新）

数据来源：IMA 知识库 GitHub 相关内容整理 | 每周自动更新
本期新增 6 个项目，总计收录 29 个优质开源项目

📊 本周更新亮点
本周新增 6 个项目，包括：

AI Coding 多 Agent 协调平台 multica（本周 +5,362 stars）
AI 持久记忆框架 MemPalace（43k+ stars）
AI 编码工作流编排器 Archon（17k+ stars）
全场景具身机器人数据集 AGIBO...]]></description><link>https://blog.robinjiang.com/github-2026-04-18-6</link><guid isPermaLink="true">https://blog.robinjiang.com/github-2026-04-18-6</guid><category><![CDATA[AI]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[open source]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Sat, 18 Apr 2026 14:55:58 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-github-2026-04-18">GitHub 开源项目仓库汇总（2026-04-18 更新）</h1>
<blockquote>
<p>数据来源：IMA 知识库 GitHub 相关内容整理 | 每周自动更新
本期新增 6 个项目，总计收录 29 个优质开源项目</p>
</blockquote>
<h2 id="heading-8jtiidmnkzlkajmm7tmlrdkuq7ngrk">📊 本周更新亮点</h2>
<p>本周新增 <strong>6 个项目</strong>，包括：</p>
<ul>
<li>AI Coding 多 Agent 协调平台 <strong>multica</strong>（本周 +5,362 stars）</li>
<li>AI 持久记忆框架 <strong>MemPalace</strong>（43k+ stars）</li>
<li>AI 编码工作流编排器 <strong>Archon</strong>（17k+ stars）</li>
<li>全场景具身机器人数据集 <strong>AGIBOT WORLD 2026</strong></li>
<li>DeepMind <strong>Gemini Robotics-ER 1.6</strong> 机器人模型</li>
</ul>
<hr />
<h2 id="heading-5lia44cb5pww5o2u5bmz5yw5lio5yv6keg5yyw6ag555uu">一、数据平台与可视化项目</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>DataCap</strong></td><td><a target="_blank" href="https://github.com/devlive-community/datacap">github.com/devlive-community/datacap</a></td><td>开源数据中台软件，支持多数据源管理与监控，实现数据转换、集成、可视化等功能。采用Java/JavaScript开发，遵循Apache 2.0协议。</td></tr>
<tr>
<td><strong>Chartbrew</strong></td><td><a target="_blank" href="https://github.com/chartbrew/chartbrew">github.com/chartbrew/chartbrew</a></td><td>开源报表平台，用于从API、MySQL、PostgreSQL、MongoDB等数据源创建实时报表看板。</td></tr>
<tr>
<td><strong>Shaper</strong></td><td><a target="_blank" href="https://github.com/taleshape-com/shaper">github.com/taleshape-com/shaper</a></td><td>免费开源、基于SQL（DuckDB）的数据可视化工具，用于创建交互式数据仪表盘。后端Go，前端React，遵循MPL-2.0协议。</td></tr>
<tr>
<td><strong>Data Formulator</strong></td><td><a target="_blank" href="https://github.com/microsoft/data-formulator">github.com/microsoft/data-formulator</a></td><td>微软研究院开发的AI驱动数据可视化工具，融合拖拽UI与自然语言输入。</td></tr>
</tbody>
</table>
</div><h2 id="heading-ai">二、AI 编程与代理项目</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>Stars</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>BMAD-METHOD</strong></td><td><a target="_blank" href="https://github.com/bmad-code-org/BMAD-METHOD">github.com/bmad-code-org/BMAD-METHOD</a></td><td>37k+</td><td>将大厂敏捷开发流程引入AI世界，构建含多角色虚拟开发团队。</td></tr>
<tr>
<td><strong>GitNexus</strong></td><td><a target="_blank" href="https://github.com/abhigyanpatwari/GitNexus">github.com/abhigyanpatwari/GitNexus</a></td><td>-</td><td>将代码结构转化为可查询知识图谱，解决大型项目协作瓶颈。</td></tr>
<tr>
<td><strong>code-review-graph</strong></td><td><a target="_blank" href="https://github.com/tirth8205/code-review-graph">github.com/tirth8205/code-review-graph</a></td><td>-</td><td>专为AI编码助手设计的本地知识图谱工具，利用Tree-sitter构建代码结构地图。</td></tr>
<tr>
<td><strong>Maestro</strong></td><td><a target="_blank" href="https://github.com/pedramamini/Maestro">github.com/pedramamini/Maestro</a></td><td>-</td><td>跨平台桌面应用，编排管理多个AI代理与项目，支持Git Worktrees并行开发。</td></tr>
<tr>
<td><strong>Acontext</strong></td><td><a target="_blank" href="https://github.com/memodb-io/Acontext">github.com/memodb-io/Acontext</a></td><td>-</td><td>面向自学智能体的上下文数据平台，统一存储会话上下文、任务记录与产出。</td></tr>
<tr>
<td><strong>Scrapling</strong></td><td><a target="_blank" href="https://github.com/ccheshirecat/scrapling">github.com/ccheshirecat/scrapling</a></td><td>-</td><td>面向本地AI Agent生态的高性能Python爬虫库，具备绕过Cloudflare Turnstile等反爬能力。</td></tr>
<tr>
<td><strong>multica</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/multica-ai/multica">github.com/multica-ai/multica</a></td><td>9.3k+</td><td>多Coding Agent协调平台，将Claude Code、Codex等汇成「舰队」协作，本周 +5,362 stars。</td></tr>
<tr>
<td><strong>MemPalace</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/MemPalace/mempalace">github.com/MemPalace/mempalace</a></td><td>43.4k+</td><td>AI记忆系统框架，ChromaDB+SQLite本地检索，零API费用，LongMemEval基准评分显著提升。</td></tr>
<tr>
<td><strong>Archon</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/coleam00/Archon">github.com/coleam00/Archon</a></td><td>17k+</td><td>AI编码Harness构建器，通过YAML声明式定义AI编码工作流（计划→实现→验证→审查→PR）。</td></tr>
</tbody>
</table>
</div><h2 id="heading-5lij44cb5pah5qgj5lio55l6kg5ase55cg6ag555uu">三、文档与知识处理项目</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Docs2KG</strong></td><td><a target="_blank" href="https://github.com/abhigyanpatwari/Docs2KG">github.com/abhigyanpatwari/Docs2KG</a></td><td>将PDF、邮件、Excel等异构文档统一转换为多模态知识图谱。</td></tr>
<tr>
<td><strong>Markwhen</strong></td><td><a target="_blank" href="https://github.com/mark-when/markwhen">github.com/mark-when/markwhen</a></td><td>将类Markdown文本快速转化为交互式层叠时间线。</td></tr>
<tr>
<td><strong>Scriban</strong></td><td><a target="_blank" href="https://github.com/scriban/scriban">github.com/scriban/scriban</a></td><td>适用于.NET的快速、安全且轻量级的脚本语言与文本模板引擎。</td></tr>
</tbody>
</table>
</div><h2 id="heading-5zub44cb5bya5yr5bel5yw35lio5bqt6ag555uu">四、开发工具与库项目</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>chaser-oxide</strong></td><td><a target="_blank" href="https://github.com/ccheshirecat/chaser-oxide">github.com/ccheshirecat/chaser-oxide</a></td><td>基于Rust的库，通过修改Chromium CDP通信协议实现反爬隐藏。</td></tr>
<tr>
<td><strong>Pinchtab</strong></td><td><a target="_blank" href="https://github.com/pinchtab/pinchtab">github.com/pinchtab/pinchtab</a></td><td>12MB的Go二进制工具，将Chrome浏览器转为LLM可直接控制的接口。</td></tr>
<tr>
<td><strong>Amphi-ETL</strong></td><td><a target="_blank" href="https://github.com/amphi-ai/amphi-etl">github.com/amphi-ai/amphi-etl</a></td><td>开源低代码数据管道生成器，实时生成基于pandas/DuckDB的Python代码。</td></tr>
<tr>
<td><strong>chart-visualization-skills</strong></td><td><a target="_blank" href="https://github.com/antvis/chart-visualization-skills">github.com/antvis/chart-visualization-skills</a></td><td>蚂蚁AntV团队打造适合Claude Code的AI原生可视化Skill。</td></tr>
<tr>
<td><strong>excalidraw-diagram-generator</strong></td><td><a target="_blank" href="https://github.com/mark-when/excalidraw-diagram-generator">github.com/mark-when/excalidraw-diagram-generator</a></td><td>让AI直接生成Excalidraw JSON文件来绘制流程图、架构图等9类图形。</td></tr>
</tbody>
</table>
</div><h2 id="heading-5lqu44cb5a2m5lmg6lwe5rqq5lio6k56il6ag555uu">五、学习资源与课程项目</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>cs249r_book</strong></td><td><a target="_blank" href="https://github.com/harvard-edge/cs249r_book">github.com/harvard-edge/cs249r_book</a></td><td>哈佛团队开源教材《Machine Learning Systems》，教授现实环境中让AI模型稳定运行。</td></tr>
<tr>
<td><strong>Generative AI for Beginners</strong></td><td><a target="_blank" href="https://github.com/Microsoft/generative-ai-for-beginners">github.com/Microsoft/generative-ai-for-beginners</a></td><td>微软开源AI课程，针对文科生、产品经理及零基础跨行者。</td></tr>
<tr>
<td><strong>AI-For-Beginners</strong></td><td><a target="_blank" href="https://github.com/Microsoft/AI-For-Beginners">github.com/Microsoft/AI-For-Beginners</a></td><td>微软开源AI课程，针对有计算机背景的学习者，侧重底层原理与框架训练。</td></tr>
</tbody>
</table>
</div><h2 id="heading-5ywt44cb5z656ga6k65pa95lio6lq57u06ag555uu">六、基础设施与运维项目</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>GitHub 地址</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>OpenObserve</strong></td><td><a target="_blank" href="https://github.com/openobserve/openobserve">github.com/openobserve/openobserve</a></td><td>开源云原生可观测性平台，存储成本较Elasticsearch低约140倍，支持PB级数据处理。</td></tr>
<tr>
<td><strong>Consul</strong></td><td>HashiCorp官方仓库</td><td>HashiCorp开发的服务发现、健康检查、服务网格与配置管理工具。</td></tr>
</tbody>
</table>
</div><h2 id="heading-5lid44cb5yw36lqr5py65zmo5lq65lio5pww5o2u6zugioktkoacrowrqoawsowinuwihuexuw">七、具身机器人与数据集 ⭐本周新增分类</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>项目名称</td><td>链接</td><td>简介</td></tr>
</thead>
<tbody>
<tr>
<td><strong>AGIBOT WORLD 2026</strong> ⭐新增</td><td><a target="_blank" href="https://github.com/OpenDriveLab/Agibot-World">github.com/OpenDriveLab/Agibot-World</a></td><td>全场景具身机器人开源数据集（100%真实世界），G2机器人采集，荣获IROS 2025 Best Paper。2026-04-10发布。</td></tr>
<tr>
<td><strong>Gemini Robotics-ER 1.6</strong> ⭐新增</td><td><a target="_blank" href="https://deepmind.google/blog/gemini-robotics-er-1-6/">DeepMind Blog</a></td><td>DeepMind发布的具身机器人模型1.6版，仪表读取精度93%，感知专家ER + 执行器VLA分离架构。</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-8jticdpobnnm67nu5orqhmsyfmgls">📈 项目统计汇总</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>分类</td><td>项目数</td></tr>
</thead>
<tbody>
<tr>
<td>数据平台与可视化</td><td>4</td></tr>
<tr>
<td>AI 编程与代理</td><td>10</td></tr>
<tr>
<td>文档与知识处理</td><td>3</td></tr>
<tr>
<td>开发工具与库</td><td>5</td></tr>
<tr>
<td>学习资源与课程</td><td>3</td></tr>
<tr>
<td>基础设施与运维</td><td>2</td></tr>
<tr>
<td>具身机器人与数据集</td><td>2</td></tr>
<tr>
<td><strong>合计</strong></td><td><strong>29</strong></td></tr>
</tbody>
</table>
</div><hr />
<blockquote>
<p>来源：Agile Robin 技术笔记 | 每周由 WorkBuddy 自动整理
Tags: GitHub, 开源项目, AI编程, 数据可视化, 具身机器人, 开发工具</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Python 工具生态全景图：10+ 必备库与 AI 代理工具汇总（2026）]]></title><description><![CDATA[本文整理自个人 IMA 知识库，汇总了近期收藏的 Python 工具、库与相关插件，作为个人 Python 工具生态的中央索引。

一、Python 核心工具与库
通用增强
boltons — 200+ 个高质量实用工具，覆盖迭代器（iterutils）、字典（dictutils）、文件、JSON、调试等，弥补标准库功能缺口。适合日常开发的"万能工具箱"，如分块（chunked）、有序多值字典（Omd）、LRU 缓存等。
AI/LLM 开发
Marvin (PrefectHQ) — 以类型安全、...]]></description><link>https://blog.robinjiang.com/python-10-ai-2026</link><guid isPermaLink="true">https://blog.robinjiang.com/python-10-ai-2026</guid><category><![CDATA[AI]]></category><category><![CDATA[Python]]></category><category><![CDATA[tools]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Sat, 18 Apr 2026 14:54:39 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>本文整理自个人 IMA 知识库，汇总了近期收藏的 Python 工具、库与相关插件，作为个人 Python 工具生态的中央索引。</p>
</blockquote>
<h2 id="heading-python">一、Python 核心工具与库</h2>
<h3 id="heading-6yca55so5ake5by6">通用增强</h3>
<p><strong>boltons</strong> — 200+ 个高质量实用工具，覆盖迭代器（iterutils）、字典（dictutils）、文件、JSON、调试等，弥补标准库功能缺口。适合日常开发的"万能工具箱"，如分块（chunked）、有序多值字典（Omd）、LRU 缓存等。</p>
<h3 id="heading-aillm">AI/LLM 开发</h3>
<p><strong>Marvin (PrefectHQ)</strong> — 以类型安全、结构化方式调用 LLM。提供 <code>extract</code>（提取）、<code>cast</code>（转换）、<code>classify</code>（分类）等简洁 API，以及完整的 Agentic 工作流框架。将 AI 能力封装为 Python 对象，避免繁琐的 prompt engineering。</p>
<h3 id="heading-5pww5o2u5yv6keg5yyw">数据可视化</h3>
<p><strong>lux</strong> — 自动化的探索性数据分析库。在 Jupyter 中显示 DataFrame 时，自动推荐相关性、分布、时间趋势等多种可视化方案。一键式数据探索，快速发现数据洞察。</p>
<h3 id="heading-5pww5o2u5pw05zci">数据整合</h3>
<p><strong>wove</strong> — 声明式的数据编织库，用于将多个数据源（数据库、API、文件）智能地交织、关联和合并。简化复杂的数据集成任务，提供直观的 API 处理数据关联与转换。</p>
<h3 id="heading-http">HTTP 客户端</h3>
<p><strong>Niquests</strong> — 高性能的 Python HTTP 客户端，API 与 requests 高度兼容，支持无缝替换。原生支持异步操作，性能优于 requests。适合需要从 requests 无缝迁移到高性能、支持异步场景。</p>
<h3 id="heading-json">JSON 处理</h3>
<p><strong>orjson</strong> — 基于 Rust 的超高速 JSON 序列化/反序列化库。速度大幅超越标准 json 模块，支持 datetime、UUID 等类型。适合高并发、对 JSON 处理性能有极致要求的场景。</p>
<h3 id="heading-url">URL 处理</h3>
<p><strong>furl</strong> — 直观的 URL 操作库。将 URL 作为对象处理，可优雅地构造、解析、修改查询参数，自动处理编码。替代手写字符串拼接，特别适合需要复杂查询参数操作的爬虫或 API 开发。</p>
<h3 id="heading-6kt6kia6kg5yir">语言识别</h3>
<p><strong>LangExtract</strong> — 轻量级文本语言识别库，能快速检测文本语言并提取语言学特征。适用于多语言应用开发、内容管理系统中的自动语言分类。</p>
<h3 id="heading-5pww5o2u566h6ygt">数据管道</h3>
<p><strong>Amphi-ETL</strong> — 低代码数据管道生成器。通过可视化拖拽操作，实时生成基于 pandas/DuckDB 的标准 Python 代码，支持与 AI 协同。快速构建可复用、可部署的数据清洗和转换管道。</p>
<h3 id="heading-54is6jmr">爬虫</h3>
<p><strong>Scrapling</strong> — 面向本地 AI Agent 生态的高性能爬虫库。能绕过 Cloudflare Turnstile 等硬反爬，网站结构自适应，并通过 MCP Server 与 Claw 项目深度集成。为 AI 代理提供工业级爬虫能力。</p>
<hr />
<h2 id="heading-5lqm44cb5pah5qgjlawhs7tuinoaekos4juwkhoeqhuw3pewftw">二、文档/文件解析与处理工具</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>工具</td><td>核心功能</td><td>技术特点</td></tr>
</thead>
<tbody>
<tr>
<td><strong>MonkeyOCR</strong></td><td>PDF 转 Markdown，格式转 LaTeX，OCR 识别扫描版 PDF</td><td>基于 PyMuPDF+Tesseract，支持 GPU 加速（Surya 引擎）</td></tr>
<tr>
<td><strong>Agentic-Doc (LandingAI)</strong></td><td>从 PDF/图像中提取结构化、视觉化数据</td><td>Agentic 文档提取（ADE）驱动，理解视觉布局，返回结构化 JSON/Markdown</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-ai">三、自动化与 AI 代理工具</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>工具</td><td>核心功能</td><td>亮点</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Pinchtab</strong></td><td>将 Chrome 浏览器转为 LLM 可直接操控的接口（REST API + 可访问性树）</td><td>轻量 12MB Go 二进制，Token 消耗极低，防检测</td></tr>
<tr>
<td><strong>Maestro</strong></td><td>编排和管理多个 AI 代理与项目的跨平台桌面应用</td><td>支持 Git Worktrees 并行开发、Auto Run、Group Chat 多 AI 协作</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-obsidian-ai">四、与 Obsidian 集成的 AI 插件方案</h2>
<h3 id="heading-api">API 方案</h3>
<p><strong>Copilot（或 DeepSeek Copilot）</strong> — 使用硅基流动（SiliconFlow）托管的 DeepSeek-V3 模型。需配置 API Key 和 Base URL (<code>https://api.siliconflow.cn/v1</code>)。模型智能，可实现智能问答、文档总结。</p>
<h3 id="heading-55l6kg5bqt5ake5by65pa55qgi">知识库增强方案</h3>
<p><strong>Copilot + Vault QA</strong> — 需额外配置嵌入模型（如 BAAI/bge-m3），用于向量化笔记内容，实现 RAG。让 AI 基于整个笔记库回答问题，打造专属知识库助手。</p>
<hr />
<h2 id="heading-5lqu44cb5yw25luw5a6e55so5luj56cb54mh5q61">五、其他实用代码片段</h2>
<p><strong>提取文本中的 URL</strong> — 使用 Python + re（正则表达式），提供从给定文本中提取所有 URL 链接的示例函数 <code>extract_urls(text)</code>。</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> re

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">extract_urls</span>(<span class="hljs-params">text: str</span>) -&gt; list[str]:</span>
    url_pattern = re.compile(
        <span class="hljs-string">r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&amp;+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'</span>
    )
    <span class="hljs-keyword">return</span> url_pattern.findall(text)
</code></pre>
<hr />
<h2 id="heading-5br6ycf6ycj5z6l5oyh5y2x">快速选型指南</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>场景</td><td>推荐工具</td></tr>
</thead>
<tbody>
<tr>
<td>需要 HTTP 客户端，追求高性能</td><td>Niquests</td></tr>
<tr>
<td>数据探索与可视化</td><td>lux</td></tr>
<tr>
<td>JSON 序列化性能瓶颈</td><td>orjson</td></tr>
<tr>
<td>构建 ETL 数据管道</td><td>Amphi-ETL</td></tr>
<tr>
<td>AI 驱动的网络爬虫</td><td>Scrapling</td></tr>
<tr>
<td>操作 URL 参数</td><td>furl</td></tr>
<tr>
<td>多语言文本识别</td><td>LangExtract</td></tr>
<tr>
<td>从 PDF 中提取结构化数据</td><td>Agentic-Doc</td></tr>
<tr>
<td>本地 AI 笔记助手</td><td>Obsidian + Copilot + DeepSeek</td></tr>
</tbody>
</table>
</div><hr />
<p><em>整理时间：2026-04-18 | 数据来源：IMA 个人知识库</em></p>
]]></content:encoded></item><item><title><![CDATA[📦 Python Tools 知识库汇总 - 从 IMA 知识库梳理的实用工具]]></title><description><![CDATA[📦 Python Tools 知识库汇总

本文是从 IMA 个人知识库中梳理出的 Python 相关工具和库，涵盖网络请求、数据处理、可视化等多个领域。

概述
定期整理知识库是保持技术敏感度的重要习惯。本文汇总了从 IMA 知识库中发现的 7 个实用 Python 工具，按功能分类整理，方便大家根据需求快速查找。

网络请求与爬虫
1. Niquests
定位: 全新的 Python HTTP 客户端项目
特点:

高性能实现
API 与 requests 高度兼容
适合从 request...]]></description><link>https://blog.robinjiang.com/python-tools-ima</link><guid isPermaLink="true">https://blog.robinjiang.com/python-tools-ima</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 17 Apr 2026 06:22:56 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-python-tools">📦 Python Tools 知识库汇总</h1>
<blockquote>
<p>本文是从 IMA 个人知识库中梳理出的 Python 相关工具和库，涵盖网络请求、数据处理、可视化等多个领域。</p>
</blockquote>
<h2 id="heading-5qac6lw">概述</h2>
<p>定期整理知识库是保持技术敏感度的重要习惯。本文汇总了从 IMA 知识库中发现的 <strong>7 个实用 Python 工具</strong>，按功能分类整理，方便大家根据需求快速查找。</p>
<hr />
<h2 id="heading-572r57uc6k35rgc5lio54is6jmr">网络请求与爬虫</h2>
<h3 id="heading-1-niquests">1. Niquests</h3>
<p><strong>定位</strong>: 全新的 Python HTTP 客户端项目</p>
<p><strong>特点</strong>:</p>
<ul>
<li>高性能实现</li>
<li>API 与 requests 高度兼容</li>
<li>适合从 requests 迁移到更现代、高性能的 HTTP 库</li>
</ul>
<p><strong>适用场景</strong>: 需要高性能 HTTP 请求处理的 Web 应用、API 客户端开发</p>
<hr />
<h3 id="heading-2-scrapling">2. Scrapling</h3>
<p><strong>定位</strong>: Python 爬虫终结者</p>
<p><strong>特点</strong>:</p>
<ul>
<li>能绕过 Cloudflare Turnstile 等反爬机制</li>
<li>可直接与 Claw Agent 集成</li>
<li>为 AI 驱动的数据爬取提供工业级能力</li>
</ul>
<p><strong>适用场景</strong>: 复杂网站的数据爬取、需要绕过反爬机制的爬虫项目</p>
<hr />
<h2 id="heading-5pww5o2u5ase55cg5lio5yv6keg5yyw">数据处理与可视化</h2>
<h3 id="heading-3-lux">3. lux</h3>
<p><strong>定位</strong>: 直观高效的 Python 数据可视化库</p>
<p><strong>特点</strong>:</p>
<ul>
<li>自动推荐可视化方案</li>
<li>在 Jupyter 中展示 DataFrame 时自动分析相关性、分布、时间趋势等</li>
<li>一键导出可视化代码</li>
</ul>
<p><strong>适用场景</strong>: 探索性数据分析 (EDA)、快速数据可视化原型</p>
<hr />
<h3 id="heading-4-wove">4. wove</h3>
<p><strong>定位</strong>: 智能精简的 Python 数据编织库</p>
<p><strong>特点</strong>:</p>
<ul>
<li>用于整合多个数据源（数据库、API、文件）</li>
<li>声明式数据转换</li>
<li>简化复杂的数据管道任务</li>
</ul>
<p><strong>适用场景</strong>: 多数据源整合、ETL 流程、数据管道构建</p>
<hr />
<h3 id="heading-5-pandas-ai">5. Pandas + AI</h3>
<p><strong>定位</strong>: 用可视化 + AI 做完整数据清洗</p>
<p><strong>特点</strong>:</p>
<ul>
<li>可视化界面操作数据清洗</li>
<li>AI 辅助数据分析和转换</li>
<li>一键导出 Python 代码</li>
</ul>
<p><strong>适用场景</strong>: 数据清洗、数据预处理、非程序员友好的数据处理</p>
<hr />
<h2 id="heading-url">数据序列化与 URL 处理</h2>
<h3 id="heading-6-orjson">6. orjson</h3>
<p><strong>定位</strong>: 极速 JSON 库</p>
<p><strong>特点</strong>:</p>
<ul>
<li>基于 Rust 实现</li>
<li>性能大幅超越标准库 json 模块</li>
<li>支持 datetime、UUID 等类型</li>
</ul>
<p><strong>适用场景</strong>: 高并发、对 JSON 处理性能有极致要求的 Web 应用和数据处理</p>
<hr />
<h3 id="heading-7-furl">7. furl</h3>
<p><strong>定位</strong>: 直观的 Python URL 处理库</p>
<p><strong>特点</strong>:</p>
<ul>
<li>将 URL 作为对象处理</li>
<li>可链式调用添加、修改、删除查询参数</li>
<li>自动处理编码</li>
</ul>
<p><strong>适用场景</strong>: 需要频繁操作 URL 查询参数的 Web 开发、爬虫项目</p>
<hr />
<h2 id="heading-5oc757ut">总结</h2>
<p>以上 7 个工具覆盖了 Python 开发中常见的几个领域：</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>类别</td><td>工具</td></tr>
</thead>
<tbody>
<tr>
<td>HTTP 客户端</td><td>Niquests</td></tr>
<tr>
<td>网络爬虫</td><td>Scrapling</td></tr>
<tr>
<td>数据可视化</td><td>lux</td></tr>
<tr>
<td>数据编织</td><td>wove</td></tr>
<tr>
<td>数据处理</td><td>Pandas + AI</td></tr>
<tr>
<td>JSON 处理</td><td>orjson</td></tr>
<tr>
<td>URL 处理</td><td>furl</td></tr>
</tbody>
</table>
</div><p>这些工具都经过了实际项目的检验，可以根据具体需求选择合适的工具来提升开发效率。</p>
<hr />
<h2 id="heading-5yc6icd">参考</h2>
<ul>
<li>原始笔记: IMA 个人知识库</li>
<li>整理时间: 2026-04-17</li>
<li>Asana 任务: clawLog 项目下的 Python Tools 汇总</li>
</ul>
<hr />
<p><em>本文是 IMA 知识库自动化梳理系列的一部分，定期整理知识库中的有价值内容。</em></p>
]]></content:encoded></item><item><title><![CDATA[The Full Landscape of Observability: History, Present, and Future]]></title><description><![CDATA[Preface: Why "Monitoring" Is No Longer Enough
In 2016, Charity Majors — then an engineer at Parse, later co-founder of Honeycomb — wrote something that has since been quoted countless times:

"Monitor]]></description><link>https://blog.robinjiang.com/the-full-landscape-of-observability-history-present-and-future</link><guid isPermaLink="true">https://blog.robinjiang.com/the-full-landscape-of-observability-history-present-and-future</guid><category><![CDATA[cloud native]]></category><category><![CDATA[Devops]]></category><category><![CDATA[observability]]></category><category><![CDATA[OpenTelemetry]]></category><category><![CDATA[SRE]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Thu, 16 Apr 2026 05:14:39 GMT</pubDate><content:encoded><![CDATA[<h2>Preface: Why "Monitoring" Is No Longer Enough</h2>
<p>In 2016, Charity Majors — then an engineer at Parse, later co-founder of Honeycomb — wrote something that has since been quoted countless times:</p>
<blockquote>
<p><em>"Monitoring tells you whether something is wrong. Observability tells you <strong>why</strong>."</em></p>
</blockquote>
<p>For most of the past two decades, "monitoring" was the word we used. Its underlying model was simple: pre-plant probes at known failure points, configure thresholds, wait for alerts. It worked well when systems were relatively simple and failures were predictable. But in a world of microservices, containers, and serverless functions, that model is quietly falling apart.</p>
<p>A single user request today might traverse dozens of services, span multiple availability zones, and trigger hundreds of downstream calls. When it fails, you often don't know <em>where</em> it failed, <em>why</em> it failed, or even <em>that</em> it failed in a way you've seen before. No pre-configured alert rule would have caught it — because you never imagined that failure mode existed.</p>
<p>This is exactly what <strong>observability</strong> tries to solve: making system behavior transparent to engineers, <em>even for failure modes they've never anticipated</em>.</p>
<p>This article traces the full arc — where the concept came from, the pivotal milestones along the way, where things stand today, and where they're headed.</p>
<hr />
<h2>I. Origins: From Control Theory to Software Engineering</h2>
<p>"Observability" didn't originate in software. The term was formalized in <strong>1960 by Rudolf E. Kálmán</strong>, a Hungarian-American engineer working in control theory. His definition was precise:</p>
<blockquote>
<p>A system is observable if, from its external outputs, you can fully reconstruct its internal state.</p>
</blockquote>
<p>Software engineering borrowed this concept almost verbatim. When we say a distributed system is <em>observable</em>, we mean: <strong>given the data the system emits — logs, metrics, traces — can engineers answer any question about its behavior without needing to modify the code or redeploy?</strong></p>
<p>That last clause — "without modifying the code" — is the key distinction. Monitoring is reactive and pre-defined; observability is proactive and exploratory. You can ask questions you didn't know to ask in advance.</p>
<hr />
<h2>II. Four Stages of Evolution</h2>
<h3>Stage 1 — Monitoring 1.0 (Pre-2010): "Is It Alive?"</h3>
<p>The demands of this era were elemental: is the server up? Is the process running? Can I reach the network?</p>
<p>Tools were script-based: cron jobs running <code>ping</code>, shell checks on process lists, emails for alerts. <strong>Nagios</strong> (1999) was the benchmark — it codified the check→alert→notify cycle and became the default operations toolbox for the next decade.</p>
<p>The fundamental limitation: it could tell you the system was <em>dead</em>, but not <em>why</em>, and offered no performance visibility whatsoever.</p>
<h3>Stage 2 — Monitoring 2.0 (2010–2016): "Is It Slow?"</h3>
<p>As web-scale systems grew, "alive" stopped being sufficient. Performance became the metric that mattered.</p>
<p>This era was defined by the rise of <strong>time-series databases and metrics ecosystems</strong>. Graphite (2006) brought time-series storage into practical engineering. The bigger shift came in 2012 when SoundCloud engineers Matt T. Proud and Julius Volz built <strong>Prometheus</strong> — introducing a Pull-based collection model, a multi-dimensional label system, and the PromQL query language. All three were genuinely novel at the time.</p>
<p><strong>Grafana</strong> followed in 2014 as a visualization layer, and the Prometheus + Grafana pairing quickly became the de facto standard for metrics monitoring — a position it still holds today.</p>
<h3>Stage 3 — Observability 1.0 (2016–2020): "The Three Pillars and the Standards War"</h3>
<p>Around 2016, "observability" began appearing frequently in engineering conversations. The era's primary intellectual contribution was establishing the <strong>three-pillar framework</strong>:</p>
<table>
<thead>
<tr>
<th>Pillar</th>
<th>What it answers</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Metrics</strong></td>
<td>How is the system performing overall?</td>
</tr>
<tr>
<td><strong>Logs</strong></td>
<td>What exactly happened?</td>
</tr>
<tr>
<td><strong>Traces</strong></td>
<td>What did this specific request go through?</td>
</tr>
</tbody></table>
<p>The framework was clean and resonated immediately. But alongside it came a standards war that would drag on for years:</p>
<ul>
<li><strong>OpenTracing</strong> (2015, CNCF) — a vendor-neutral Tracing API spec, but Traces only, no Metrics support.</li>
<li><strong>OpenCensus</strong> (2017, Google) — covered both Metrics and Traces, with a more complete API, but raised vendor-neutrality concerns given its Google provenance.</li>
</ul>
<p>The two were incompatible. Users couldn't choose. SDK authors had to maintain parallel implementations. This fragmentation was the direct motivation for what came next.</p>
<h3>Stage 4 — Observability 2.0 (2020–Present): "Unified, Integrated, Intelligent"</h3>
<p>The turning point was 2019. OpenTracing and OpenCensus announced they would merge to form <strong>OpenTelemetry</strong> — ending four years of standards fragmentation. A genuinely neutral, unified observability standard began to take shape.</p>
<p>What followed: the three pillars reached GA status one by one, AI and eBPF entered the mainstream, and Profiling emerged as a potential "fourth signal." Observability evolved from <em>collecting data</em> to <em>understanding system behavior</em>.</p>
<hr />
<h2>III. A Timeline of Key Systems and Events (2010–2025)</h2>
<h3>The Foundational Years (2010–2014)</h3>
<p><strong>2010</strong>
Google publishes the <strong>Dapper paper</strong> (<em>Dapper, a Large-Scale Distributed Systems Tracing Infrastructure</em>). The paper describes Google's internal approach to tracking cross-service requests and introduces the foundational concepts of Span, Trace, and Annotation. Nearly every distributed tracing system that followed — Zipkin, Jaeger, SkyWalking — drew directly from it.</p>
<p><strong>2012</strong></p>
<ul>
<li><strong>Prometheus</strong> is created at SoundCloud. The Pull model, multi-dimensional labels, and PromQL were each revolutionary. Together, they redefined how metrics collection worked.</li>
<li><strong>Zipkin</strong> is open-sourced by Twitter — the first publicly available Dapper-inspired tracing system, making distributed tracing engineering-practical for the first time.</li>
</ul>
<p><strong>2013</strong>
<strong>Docker</strong> launches. Container-based deployment's short lifecycle and high density made host-based monitoring models obsolete almost immediately.</p>
<p><strong>2014</strong></p>
<ul>
<li><strong>Grafana 0.x</strong> is released, initially as a visualization front-end for Graphite and InfluxDB, eventually growing into a full observability platform.</li>
<li><strong>Kubernetes</strong> is open-sourced by Google. Cloud-native infrastructure enters the mainstream. System complexity increases by an order of magnitude; observability requirements follow.</li>
</ul>
<hr />
<h3>The Standards Exploration Years (2015–2018)</h3>
<p><strong>2015</strong></p>
<ul>
<li><strong>Jaeger</strong> is open-sourced by Uber, purpose-built for microservices with Cassandra/Elasticsearch backends and better scalability than Zipkin.</li>
<li><strong>OpenTracing</strong> is accepted into the CNCF (becoming its third project), proposing "one API, many implementations" for distributed tracing.</li>
</ul>
<p><strong>2016</strong></p>
<ul>
<li><strong>Prometheus</strong> joins the CNCF, becoming its second project after Kubernetes — formally anchoring metrics monitoring in the cloud-native core.</li>
<li>The three-pillar theory enters widespread engineering discourse. Honeycomb and other startups begin building high-cardinality observability platforms.</li>
</ul>
<p><strong>2017</strong></p>
<ul>
<li><strong>OpenCensus</strong> is released by Google with support for both Metrics and Traces and multi-language SDKs.</li>
<li><strong>Apache SkyWalking</strong> is created by Wu Sheng in China and enters Apache incubation — it would go on to become the dominant APM platform in Chinese Java ecosystems.</li>
</ul>
<p><strong>2018</strong></p>
<ul>
<li><strong>Grafana Loki</strong> development begins (open-sourced in 2019). Its core insight: apply Prometheus's label-based indexing philosophy to logs, dramatically cutting storage costs versus Elasticsearch.</li>
<li><strong>eBPF</strong> starts entering observability discussions. Cilium is the first project to use eBPF for Kubernetes network observation, demonstrating the potential of zero-instrumentation data collection.</li>
</ul>
<hr />
<h3>The Unification and Maturity Years (2019–2023)</h3>
<p><strong>2019 — The Pivotal Year</strong></p>
<p>More happened in this single year than in the previous five combined:</p>
<ul>
<li><strong>OpenTelemetry is born</strong>: OpenTracing and OpenCensus communities announce their merger at KubeCon NA 2019. The fragmentation problem is solved at the source. OTel enters CNCF incubation and within 18 months becomes CNCF's second most active project (behind only Kubernetes).</li>
<li><strong>Jaeger graduates from CNCF</strong> at the highest (graduated) status.</li>
<li><strong>Grafana Loki 1.0</strong> is officially open-sourced.</li>
<li><strong>Apache SkyWalking</strong> graduates to Apache top-level project status.</li>
</ul>
<p><strong>2020</strong></p>
<ul>
<li>OTel enters active development across all major language SDKs.</li>
<li>COVID-19 accelerates cloud-native adoption globally — companies that had been gradually migrating are forced to move quickly, driving a surge in observability demand.</li>
<li><strong>Thanos</strong> and <strong>Cortex</strong> become the primary solutions for Prometheus long-term storage and high availability. Cortex later evolves into <strong>Grafana Mimir</strong>.</li>
</ul>
<p><strong>2021</strong></p>
<ul>
<li><strong>OTel Tracing SDK</strong> reaches v1.0 stable status in Go, Java, Python, and .NET — production-grade distributed tracing via an open standard becomes reality.</li>
<li><strong>Grafana Tempo</strong> launches as the tracing backend for Grafana Stack, completing a coherent Grafana observability trio: Tempo (traces) + Loki (logs) + Prometheus/Mimir (metrics).</li>
<li><strong>Datadog</strong> exceeds a $40B market cap, validating the commercial scale of the observability market.</li>
</ul>
<p><strong>2022</strong></p>
<ul>
<li><strong>OTel Metrics reaches GA</strong> in major language SDKs.</li>
<li><strong>OpenTracing is officially archived</strong> by the CNCF, having been superseded by OpenTelemetry.</li>
<li><strong>VictoriaMetrics</strong> gains significant adoption as a resource-efficient Prometheus-compatible storage alternative.</li>
</ul>
<p><strong>2023</strong></p>
<ul>
<li><strong>OTel Logs reaches GA</strong> — the final piece of the three-pillar standard is stable. OTel's completeness is no longer in question.</li>
<li><strong>OpenCensus announces end-of-life</strong>, explicitly directing all users to migrate to OpenTelemetry.</li>
<li><strong>eBPF observability tools proliferate</strong>: Cilium/Hubble (network), Tetragon (security), Parca/Pyroscope (continuous profiling) all enter mainstream use. The OTel community formally begins discussing Profiling as a "fourth signal."</li>
<li><strong>Grafana acquires Pyroscope</strong>, incorporating continuous profiling into the Grafana Stack.</li>
</ul>
<hr />
<h3>The Convergence Years (2024–2025)</h3>
<p><strong>2024</strong></p>
<ul>
<li><strong>OTel Profiling data model draft</strong> is published — fourth-pillar standardization enters a substantive phase.</li>
<li><strong>AI-assisted observability lands quickly</strong>: natural language log querying (Chat2Loki), LLM-driven root cause analysis, and auto-generated PromQL queries appear across platforms. Datadog, Dynatrace, and New Relic all ship AI features.</li>
<li><strong>Datadog acquires Quickwit</strong>, positioning for next-generation, cost-efficient log search.</li>
<li><strong>Cost optimization becomes a top-line concern</strong>: large enterprises are spending tens of millions annually on observability infrastructure. Intelligent sampling, tiered storage, and usage-based pricing models become priorities.</li>
</ul>
<p><strong>2025 (Current)</strong></p>
<ul>
<li><strong>OTel Profiling GA</strong> is expected to land, bringing all four signals (Metrics / Logs / Traces / Profiling) under a stable, unified standard.</li>
<li><strong>Platform Engineering meets observability</strong>: eBPF collection capabilities migrate down into the platform layer. Developers no longer need to manually instrument — the platform handles baseline observability automatically.</li>
<li>AI observability enters the <strong>co-pilot phase</strong>: anomaly detection, root cause suggestions, and Runbook generation become platform-native features rather than standalone products.</li>
<li><strong>New commercial models emerge</strong> in response to cost pressure: usage-based observability SaaS gains ground.</li>
</ul>
<hr />
<h2>IV. OpenTelemetry: How a Divided Community Found Unity</h2>
<p>The OpenTelemetry story deserves a dedicated chapter, because it's not just a technical achievement — it's a lesson in open-source community governance.</p>
<h3>Why Merge?</h3>
<p>The coexistence of OpenTracing and OpenCensus was unsustainable for four reasons:</p>
<ol>
<li><strong>Dual maintenance burden</strong> — SDK authors had to implement two separate adapter layers.</li>
<li><strong>User paralysis</strong> — new projects didn't know which standard to pick; existing projects faced costly migrations.</li>
<li><strong>Ecosystem fragmentation</strong> — Jaeger was OpenTracing-native; Google Stackdriver assumed OpenCensus. Nothing interoperated cleanly.</li>
<li><strong>A shared adversary</strong> — commercial APM vendors (Datadog, New Relic, Dynatrace) weren't bound by either open standard and benefited from the infighting.</li>
</ol>
<p>The core consensus that made the merger possible: <strong>standardization belongs in the API and data model layer; competition should happen at the implementation layer</strong> — backends, storage, visualization, and AI analysis.</p>
<h3>The Architecture</h3>
<p>OpenTelemetry uses a clean layered design:</p>
<pre><code>Your Application Code
        │
  OTel API  ──── (lightweight, stable, minimal dependencies)
        │
  OTel SDK  ──── (collection, processing, batching, sampling)
        │
OTel Collector ── (receive → process → export)
        │
 Backend Storage (Jaeger / Tempo / Prometheus / Loki / SaaS)
</code></pre>
<p>The <strong>OTel Collector</strong> is the architectural linchpin: a standalone process that receives signals in any format, applies processing pipelines (sampling, filtering, enrichment), and exports to any backend. It decouples your application from any specific vendor or storage system.</p>
<h3>Language SDK Maturity (as of 2025)</h3>
<table>
<thead>
<tr>
<th>Language</th>
<th>Traces</th>
<th>Metrics</th>
<th>Logs</th>
</tr>
</thead>
<tbody><tr>
<td>Go</td>
<td>✅ GA</td>
<td>✅ GA</td>
<td>✅ GA</td>
</tr>
<tr>
<td>Java</td>
<td>✅ GA</td>
<td>✅ GA</td>
<td>✅ GA</td>
</tr>
<tr>
<td>Python</td>
<td>✅ GA</td>
<td>✅ GA</td>
<td>✅ GA</td>
</tr>
<tr>
<td>.NET</td>
<td>✅ GA</td>
<td>✅ GA</td>
<td>✅ GA</td>
</tr>
<tr>
<td>Node.js</td>
<td>✅ GA</td>
<td>✅ GA</td>
<td>✅ GA</td>
</tr>
<tr>
<td>Rust</td>
<td>🔶 Beta</td>
<td>🔶 Beta</td>
<td>🔸 Alpha</td>
</tr>
<tr>
<td>PHP</td>
<td>🔷 RC</td>
<td>🔶 Beta</td>
<td>🔸 Alpha</td>
</tr>
</tbody></table>
<hr />
<h2>V. The Three Pillars — and a Fourth Signal Emerging</h2>
<h3>Metrics</h3>
<p>Metrics are the <strong>aggregated view</strong> of system state. They sacrifice per-event detail in exchange for extremely low storage cost and fast time-series querying.</p>
<p>The four standard types:</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>Behavior</th>
<th>Example</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Counter</strong></td>
<td>Monotonically increasing</td>
<td><code>http_requests_total</code></td>
</tr>
<tr>
<td><strong>Gauge</strong></td>
<td>Current value, up or down</td>
<td><code>memory_usage_bytes</code></td>
</tr>
<tr>
<td><strong>Histogram</strong></td>
<td>Distribution of values</td>
<td>Request latency at P50/P95/P99</td>
</tr>
<tr>
<td><strong>Summary</strong></td>
<td>Client-side quantiles</td>
<td>Similar to Histogram</td>
</tr>
</tbody></table>
<p>PromQL is the de facto query language. Expressions like <code>rate()</code>, <code>histogram_quantile()</code>, and <code>sum by()</code> cover the vast majority of real-world metrics analysis.</p>
<h3>Logs</h3>
<p>Logs are the <strong>richest information source</strong> in any system — and the hardest to scale.</p>
<p>Modern logging best practices:</p>
<ul>
<li><strong>Structured JSON output</strong> — queryable by field, not just grep-able</li>
<li><strong>Carry Trace IDs</strong> — enabling correlation between logs and distributed traces</li>
<li><strong>Follow OTel semantic conventions</strong> — consistent field naming across services</li>
<li><strong>Apply sampling</strong> — critical for high-throughput services to control costs</li>
</ul>
<p>On storage: Loki's "index labels, not content" philosophy makes it far cheaper than Elasticsearch. The trade-off is weaker full-text search. Pick based on your query patterns.</p>
<h3>Traces</h3>
<p>Distributed tracing is the <strong>most direct tool for understanding request flow</strong> across a complex system — and the most engineering-intensive pillar to implement.</p>
<p>The core mental model:</p>
<ul>
<li><strong>Trace</strong> — a complete end-to-end record of one request, composed of multiple Spans</li>
<li><strong>Span</strong> — a single operation (one HTTP call, one DB query), with timestamps, tags, and events attached</li>
<li><strong>Context Propagation</strong> — the mechanism that threads Trace IDs across process boundaries; getting this right is the hardest part</li>
</ul>
<p>The sampling decision matters enormously:</p>
<table>
<thead>
<tr>
<th>Strategy</th>
<th>How</th>
<th>Trade-off</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Head-based</strong></td>
<td>Decide at request entry</td>
<td>Simple, but loses anomalous long-tail events</td>
</tr>
<tr>
<td><strong>Tail-based</strong></td>
<td>Decide after completion</td>
<td>Catches everything important, but requires buffering all spans</td>
</tr>
<tr>
<td><strong>Adaptive</strong></td>
<td>Dynamic rules + ML</td>
<td>Best of both worlds; highest complexity</td>
</tr>
</tbody></table>
<h3>Profiling — The Fourth Signal</h3>
<p>Continuous Profiling answers a question the other three pillars can't: <strong>which lines of code are consuming the most CPU, memory, or I/O right now?</strong></p>
<p>Unlike on-demand profiling (fire it up when something's already broken), Continuous Profiling runs permanently at low overhead — giving you a always-on, always-available view of performance hotspots.</p>
<p><strong>Pyroscope</strong> (Grafana Labs) and <strong>Parca</strong> (Polar Signals) are the leading open-source implementations. OTel's Profiling data model entered draft specification in 2024; GA is expected in 2025.</p>
<p>When Profiling arrives under the OTel umbrella, it unlocks <strong>Observability-Driven Development (ODD)</strong> — using production-grade performance data to guide decisions during the development cycle, not just after incidents.</p>
<hr />
<h2>VI. Where Things Stand in 2025</h2>
<h3>The Standardization Battle Is Over</h3>
<p>OpenTelemetry is the only observability standard with full cross-industry adoption. AWS, Google Cloud, Azure, Alibaba Cloud, and Tencent Cloud have all announced first-class OTel support. Datadog, New Relic, and Dynatrace have all shipped OTel-native ingestion paths.</p>
<p>The competitive landscape has shifted: nobody fights over standards anymore. Vendors differentiate on <strong>backend performance, UI experience, AI capabilities, and pricing models</strong>.</p>
<h3>AI: Co-Pilot, Not Autopilot</h3>
<p>In 2025, AI in observability is settling into a practical "co-pilot" role:</p>
<ul>
<li><strong>Natural language queries</strong> — describe what you want in plain language; the platform generates PromQL/LogQL/TraceQL automatically</li>
<li><strong>Anomaly detection</strong> — ML-based alerting against historical baselines, reducing alert fatigue significantly</li>
<li><strong>Root cause reasoning</strong> — LLMs contextualize alerts and surface probable causes, reducing mean time to diagnose (MTTD)</li>
<li><strong>Runbook generation</strong> — alert responses documented automatically based on historical resolution patterns</li>
</ul>
<p>Fully autonomous remediation remains immature. The two key blockers: insufficient context understanding, and the near-impossibility of reliable risk-bounded automated action. Practical automation for low-risk operations (scale-out, pod restart) is likely 12–24 months away from mainstream adoption.</p>
<h3>Cost Is the New Battleground</h3>
<p>Observability's value is no longer in question. Its <em>cost</em> is.</p>
<p>A mid-sized tech company might spend \(1-2M/year on observability infrastructure. Large enterprises can hit \)10M+. The industry response:</p>
<table>
<thead>
<tr>
<th>Strategy</th>
<th>Mechanism</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Intelligent sampling</strong></td>
<td>Keep only traces that matter (errors, slow requests, new paths)</td>
</tr>
<tr>
<td><strong>Tiered log storage</strong></td>
<td>Hot data on SSD; cold data in object storage (S3/OSS)</td>
</tr>
<tr>
<td><strong>Metrics downsampling</strong></td>
<td>Aggregate old data into lower-resolution rollups</td>
</tr>
<tr>
<td><strong>Self-hosted Grafana Stack</strong></td>
<td>Avoid per-GB/per-seat SaaS pricing for mature teams</td>
</tr>
</tbody></table>
<hr />
<h2>VII. Looking Ahead</h2>
<h3>1. Profiling Completes the Picture</h3>
<p>When OTel Profiling goes GA, the four signals will finally be unified under one standard. Code-level performance views become first-class citizens alongside traces, metrics, and logs — accessible in the same platform, correlated against the same request.</p>
<p><strong>ODD (Observability-Driven Development)</strong> — using production performance data to drive code decisions during development — becomes a realistic engineering practice.</p>
<h3>2. Observability Shifts Left</h3>
<p>Traditionally, observability engaged <em>after</em> deployment. The direction is clearly leftward:</p>
<ul>
<li><strong>CI/CD integration</strong>: automatically compare observability metrics against baseline on every deploy; fail the pipeline on regression</li>
<li><strong>Local dev observability</strong>: lightweight traces and metrics accessible during local development cycles</li>
<li><strong>Automated DORA metric collection</strong>: deployment frequency, change failure rate, mean time to restore — all derived from observability data, not manual tracking</li>
</ul>
<h3>3. eBPF Rewires the Collection Layer</h3>
<p>eBPF runs custom programs safely at the Linux kernel layer, collecting observability data from any process — without touching application code. The implications:</p>
<ul>
<li><strong>Zero-instrumentation tracing</strong>: no OTel SDK required; eBPF intercepts system calls automatically</li>
<li><strong>L4/L7 network observability</strong>: real-time traffic tracing without a service mesh sidecar</li>
<li><strong>Security observability</strong>: process behavior monitoring, file access auditing — eBPF is becoming the kernel of cloud-native security tooling</li>
</ul>
<p>As Platform Engineering matures, eBPF collection becomes platform-team-owned infrastructure. Application developers get observability for free.</p>
<h3>4. The Long Game: Autonomous Operations</h3>
<p>The ultimate destination isn't "help humans understand" — it's <strong>systems that manage themselves</strong>. The path is becoming clearer:</p>
<pre><code>Collect (OTel)
    → Detect (ML anomaly detection)
    → Diagnose (LLM-assisted root cause)
    → Decide (rules + model inference)
    → Act (GitOps / K8s Operator)
    → Verify (back to observability data)
</code></pre>
<p>Each step has usable tools today. The hard part is <strong>end-to-end trust and risk control</strong> — no engineer wants to wake up to an incident only to discover the system already "fixed" it in a way that made things far worse.</p>
<h3>5. Expanding the Observable Universe</h3>
<p>Observability is extending beyond infrastructure and applications:</p>
<ul>
<li><strong>Business observability</strong> — correlate technical metrics directly to business KPIs (GMV, conversion rate, retention), making engineering health legible to non-engineers</li>
<li><strong>Security observability (SecObs)</strong> — bring security events and anomalous access patterns into the unified observability stack for DevSecOps closure</li>
<li><strong>Sustainability observability</strong> — track carbon footprint and energy consumption per workload, providing a data foundation for green computing initiatives</li>
</ul>
<hr />
<h2>Closing: What Observability Is Really About</h2>
<p>Observability isn't a tool. It isn't a specification. It isn't a vendor's product.</p>
<p>It's an <strong>engineering philosophy</strong> — one that accepts that modern systems are too complex to fully anticipate, and responds by deliberately building the capacity to answer any question about system behavior, even questions you didn't know to ask.</p>
<p>From Kálmán's 1960 control theory formula, to Google's 2010 Dapper paper, to OpenTelemetry's 2019 unification, to today's convergence of AI and eBPF — this field has been evolving for sixty years. But the era of broad engineering adoption? That's only about ten years old.</p>
<p>For anyone building distributed systems today: observability is not a nice-to-have operational add-on. It is <strong>infrastructure for understanding what you've built</strong>.</p>
<p>A system without observability is like an aircraft without instruments. It might fly. But you won't know how much longer it can.</p>
<hr />
<p><em>This is the first article in Agile Robin's Observability Series. Upcoming deep-dives: OpenTelemetry in Practice, eBPF Observability Primer, and Building the Complete Grafana Stack.</em></p>
<hr />
<h3>References</h3>
<ol>
<li>Sigelman et al., <em>Dapper, a Large-Scale Distributed Systems Tracing Infrastructure</em>, Google, 2010</li>
<li>OpenTelemetry Documentation — <a href="https://opentelemetry.io">opentelemetry.io</a></li>
<li>CNCF Annual Report 2024</li>
<li>Grafana Labs, <em>State of Observability 2024</em></li>
<li>Datadog, <em>State of DevOps 2024</em></li>
<li><a href="https://observability.cn">observability.cn</a> — Chinese observability community</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[python 使用pycryptodome 进行AES 加密解密操作]]></title><description><![CDATA[在Python中，你可以使用pycryptodome库来进行AES加密和解密。首先，你需要安装这个库：
pip install pycryptodome
然后，你可以使用以下代码来实现AES加密和解密：
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
import base64

def aes_encrypt(p...]]></description><link>https://blog.robinjiang.com/python-pycryptodome-aes</link><guid isPermaLink="true">https://blog.robinjiang.com/python-pycryptodome-aes</guid><category><![CDATA[pycryptodome]]></category><category><![CDATA[Python]]></category><category><![CDATA[aes]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Wed, 14 Aug 2024 02:29:50 GMT</pubDate><content:encoded><![CDATA[<p>在Python中，你可以使用pycryptodome库来进行AES加密和解密。首先，你需要安装这个库：</p>
<pre><code>pip install pycryptodome
</code></pre><p>然后，你可以使用以下代码来实现AES加密和解密：</p>
<pre><code><span class="hljs-keyword">from</span> Crypto.Cipher <span class="hljs-keyword">import</span> AES
<span class="hljs-keyword">from</span> Crypto.Util.Padding <span class="hljs-keyword">import</span> pad, unpad
<span class="hljs-keyword">from</span> Crypto.Random <span class="hljs-keyword">import</span> get_random_bytes
<span class="hljs-keyword">import</span> base64

def aes_encrypt(plain_text, key):
    <span class="hljs-string">""</span><span class="hljs-string">"
    使用AES加密文本。

    :param plain_text: 要加密的文本
    :param key: 加密密钥，长度必须是16, 24, 或 32字节
    :return: 加密后的文本
    "</span><span class="hljs-string">""</span>
    cipher = AES.new(key, AES.MODE_CBC)
    ct_bytes = cipher.encrypt(pad(plain_text.encode(<span class="hljs-string">'utf-8'</span>), AES.block_size))
    iv = base64.b64encode(cipher.iv).decode(<span class="hljs-string">'utf-8'</span>)
    ct = base64.b64encode(ct_bytes).decode(<span class="hljs-string">'utf-8'</span>)
    <span class="hljs-keyword">return</span> iv + <span class="hljs-string">':'</span> + ct

def aes_decrypt(encrypted_text, key):
    <span class="hljs-string">""</span><span class="hljs-string">"
    使用AES解密文本。

    :param encrypted_text: 加密的文本
    :param key: 解密密钥，长度必须是16, 24, 或 32字节
    :return: 解密后的文本
    "</span><span class="hljs-string">""</span>
    iv, ct = encrypted_text.split(<span class="hljs-string">':'</span>)
    iv = base64.b64decode(iv)
    ct = base64.b64decode(ct)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    pt = unpad(cipher.decrypt(ct), AES.block_size).decode(<span class="hljs-string">'utf-8'</span>)
    <span class="hljs-keyword">return</span> pt
</code></pre><h1 id="heading-56s65l6l55so5rov">示例用法</h1>
<pre><code>key = get_random_bytes(<span class="hljs-number">16</span>)  # 生成一个<span class="hljs-number">16</span>字节的随机密钥
plain_text = <span class="hljs-string">"Hello, World!"</span>
encrypted_text = aes_encrypt(plain_text, key)
decrypted_text = aes_decrypt(encrypted_text, key)

print(<span class="hljs-string">"Plain text:"</span>, plain_text)
print(<span class="hljs-string">"Encrypted text:"</span>, encrypted_text)
print(<span class="hljs-string">"Decrypted text:"</span>, decrypted_text)
</code></pre><p>在这个例子中，我们使用了CBC模式（Cipher Block Chaining）和随机初始化向量（IV）。加密后的文本和IV都被编码为base64，以便可以安全地存储或传输。解密时，需要先解码base64，然后提取IV和密文进行解密。</p>
]]></content:encoded></item><item><title><![CDATA[Sonarqube community with Branch]]></title><description><![CDATA[为sonarqube开源版本增加多分支支持
插件地址
项目地址：https://github.com/mc1arke/sonarqube-community-branch-plugin
下载地址：https://github.com/mc1arke/sonarqube-community-branch-plugin/releases
wget https://github.com/mc1arke/sonarqube-community-branch-plugin/releases/downloa...]]></description><link>https://blog.robinjiang.com/sonarqube-community-with-branch-plugins</link><guid isPermaLink="true">https://blog.robinjiang.com/sonarqube-community-with-branch-plugins</guid><category><![CDATA[sonarqube]]></category><category><![CDATA[Sonar Systems Market]]></category><category><![CDATA[sonarqube integration]]></category><category><![CDATA[GitLab]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Sun, 04 Feb 2024 03:50:51 GMT</pubDate><content:encoded><![CDATA[<p>为sonarqube开源版本增加多分支支持</p>
<h1 id="heading-5os5lu25zyw5z2a">插件地址</h1>
<p>项目地址：https://github.com/mc1arke/sonarqube-community-branch-plugin</p>
<p>下载地址：https://github.com/mc1arke/sonarqube-community-branch-plugin/releases</p>
<pre><code>wget https:<span class="hljs-comment">//github.com/mc1arke/sonarqube-community-branch-plugin/releases/download/1.14.0/sonarqube-community-branch-plugin-1.14.0.jar</span>
</code></pre><h1 id="heading-5os5lu25l2n572u">插件位置</h1>
<pre><code>mv  sonarqube-community-branch-plugin<span class="hljs-number">-1.14</span><span class="hljs-number">.0</span>.jar /usr/local/sonarqube/extensions/plugins/  #/usr/local/sonarqube为sonarqube的安装目录，根据实际情况修改
</code></pre><h1 id="heading-5lu5ps56ywn572u">修改配置</h1>
<pre><code>grep <span class="hljs-string">'sonar.ce.javaAdditionalOpts|sonar.web.javaAdditionalOpts'</span> sonar.properties #以下两项配置其他不变，修改<span class="hljs-number">1.14</span><span class="hljs-number">.0</span>为你下载的版本
sonar.web.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin<span class="hljs-number">-1.14</span><span class="hljs-number">.0</span>.jar=web
sonar.ce.javaAdditionalOpts=-javaagent:./extensions/plugins/sonarqube-community-branch-plugin<span class="hljs-number">-1.14</span><span class="hljs-number">.0</span>.jar=ce
</code></pre><h1 id="heading-6yen5zcv">重启</h1>
<pre><code>su - sonar
cd /usr/local/sonarqube/bin/linux-x86<span class="hljs-number">-64</span>
./sonar.sh stop
./sonar.sh start
</code></pre><h1 id="heading-sonar">修改sonar扫描参数</h1>
<pre><code>SonarScanner.MSBuild [begin|end] /key:project_key [<span class="hljs-regexp">/name:project_name] [/version:project_version] [/s:settings_file] [/d:sonar.token=token] [/d:sonar.{property_name}=value]</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[sourcetree 卡顿优化]]></title><description><![CDATA[如果sourcetree比较卡 执行下面三个命令试试，我今天试了下，感觉好点了。git config --global core.preloadindex truegit config --global core.fscache truegit config --global gc.auto 256]]></description><link>https://blog.robinjiang.com/sourcetree</link><guid isPermaLink="true">https://blog.robinjiang.com/sourcetree</guid><category><![CDATA[#sourcetreee]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Tue, 09 Jan 2024 01:43:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/n8Qb1ZAkK88/upload/071240883b1587421013f91a46998c61.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>如果sourcetree比较卡 执行下面三个命令试试，我今天试了下，感觉好点了。<br />git config --global core.preloadindex true<br />git config --global core.fscache true<br />git config --global <a target="_blank" href="http://gc.auto">gc.auto</a> 256</p>
]]></content:encoded></item><item><title><![CDATA[常用工具集合-2023版本]]></title><description><![CDATA[carbon-一个有逼格的代码截图分享工具




CategoryRepositorySite URLDescription



BrowserLibreWolfhttps://librewolf.net/LibreWolf is a privacy-oriented web browser based on Firefox

BrowserBravehttps://brave.com/Brave is a privacy-oriented web browser based on Chro...]]></description><link>https://blog.robinjiang.com/tools</link><guid isPermaLink="true">https://blog.robinjiang.com/tools</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 09:57:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835058526/63ba7e96-9632-4229-a18c-bb298f45e6bb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://carbon.now.sh/">carbon-一个有逼格的代码截图分享工具</a></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Category</td><td>Repository</td><td>Site URL</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Browser</strong></td><td><a target="_blank" href="https://gitlab.com/librewolf-community"><strong>LibreWolf</strong></a></td><td>https://librewolf.net/</td><td>LibreWolf is a privacy-oriented web browser based on Firefox</td></tr>
<tr>
<td><strong>Browser</strong></td><td><a target="_blank" href="https://github.com/brave/brave-browser"><strong>Brave</strong></a></td><td>https://brave.com/</td><td>Brave is a privacy-oriented web browser based on Chromium</td></tr>
<tr>
<td><strong>Task Runner</strong></td><td><a target="_blank" href="https://github.com/go-task/task"><strong>go-task</strong></a></td><td>https://taskfile.dev/</td><td>Task is a task runner / simpler Make alternative written in Go</td></tr>
<tr>
<td><strong>Data Analysis</strong></td><td><a target="_blank" href="https://github.com/pandas-dev/pandas"><strong>Pandas</strong></a></td><td>https://pandas.pydata.org/</td><td>Pandas is a  data analysis library for Python</td></tr>
<tr>
<td><strong>Object Storage</strong></td><td><a target="_blank" href="https://github.com/minio/minio"><strong>Minio</strong></a></td><td>https://min.io/</td><td>Minio is an object storage server that is compatible with Amazon S3</td></tr>
<tr>
<td><strong>Terminal</strong></td><td><a target="_blank" href="https://github.com/Eugeny/tabby"><strong>Tabby</strong></a></td><td>https://tabby.sh/</td><td>A Terminal for the modern age</td></tr>
<tr>
<td><strong>Terminal</strong></td><td><a target="_blank" href="https://github.com/tmux/tmux"><strong>Tmux</strong></a></td><td>https://github.com/tmux/tmux</td><td>tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached.</td></tr>
<tr>
<td><strong>Terminal</strong></td><td><a target="_blank" href="https://github.com/tmuxinator/tmuxinator"><strong>Tmuxinator</strong></a></td><td>https://github.com/tmuxinator/tmuxinator</td><td>Tmuxinator is a tool for managing complex tmux sessions easily.</td></tr>
<tr>
<td><strong>Code Editor</strong></td><td><a target="_blank" href="http://https://github.com/microsoft/vscode"><strong>Visual Studio Code</strong></a></td><td>https://code.visualstudio.com/</td><td>Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications. Built on top of <a target="_blank" href="https://github.com/electron/electron">Electron</a></td></tr>
<tr>
<td><strong>Code Editor</strong></td><td><a target="_blank" href="https://github.com/neovim/neovim"><strong>neovim</strong></a></td><td>https://neovim.io/</td><td>Neovim is a text editor based on Vim. Last few months, i have been using <code>neovim</code> more often compared to <code>Visual Studio Code</code></td></tr>
<tr>
<td><strong>Productivity - Note Taking</strong></td><td><a target="_blank" href="https://github.com/dendronhq/dendron"><strong>Dendron</strong></a></td><td>https://dendron.so/</td><td>Dendron is my goto note taking tool. Its available as a plugin for VSCode and allows hirearchy and graph based note taking. Awesome solution to mantain a <code>second-brain</code></td></tr>
<tr>
<td><strong>Productivity - Bookmark Manager</strong></td><td><a target="_blank" href="https://github.com/jarun/buku"><strong>Buku</strong></a></td><td>https://github.com/jarun/buku#quickstart</td><td>Buku is a command-line bookmark manager.</td></tr>
<tr>
<td><strong>Private Network VPN</strong></td><td><a target="_blank" href="https://github.com/tailscale/tailscale"><strong>Tailscale</strong></a></td><td>https://tailscale.com/</td><td>Tailscale is a private network VPN. Helps me create a private network for all my home lab machines and devices</td></tr>
<tr>
<td><strong>Tunnelling</strong></td><td><a target="_blank" href="https://github.com/inconshreveable/ngrok"><strong>ngrok</strong></a></td><td>https://ngrok.com/</td><td>ngrok exposes local servers behind NATs and firewalls to the public internet over secure tunnels.</td></tr>
<tr>
<td><strong>Tunnelling</strong></td><td><a target="_blank" href="https://github.com/localtunnel/localtunnel"><strong>localtunnel</strong></a></td><td>https://localtunnel.github.io/www/</td><td>localtunnel exposes your localhost to the world for easy testing and sharing! No need to mess with DNS or deploy just to have others test out your changes.</td></tr>
<tr>
<td><strong>Containers</strong></td><td><a target="_blank" href="https://github.com/containers/podman"><strong>Podman</strong></a></td><td>https://podman.io/</td><td>Podman is a daemonless, open source, Linux native tool designed to make it easy to find, run, build, share and deploy applications using Open Containers Initiative (OCI) Containers and Container Images.</td></tr>
<tr>
<td><strong>Container Scheduling and Management</strong></td><td><a target="_blank" href="https://github.com/kubernetes/kubernetes"><strong>Kubernetes</strong></a></td><td>https://kubernetes.io/</td><td>Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.</td></tr>
<tr>
<td><strong>Static Site Generator</strong></td><td><a target="_blank" href="https://github.com/facebook/docusaurus"><strong>Docusaurus</strong></a></td><td>https://docusaurus.io/</td><td>Docusaurus is a static site generator written in JavaScript. Build optimized websites quickly, focus on your content.</td></tr>
<tr>
<td><strong>Static Site Generator</strong></td><td><a target="_blank" href="https://github.com/gohugoio/hugo"><strong>Hugo</strong></a></td><td>https://gohugo.io/</td><td>Hugo is a static site generator written in <code>Go</code>.</td></tr>
<tr>
<td><strong>Code Tools</strong> New</td><td><a target="_blank" href="https://carbon.now.sh/"><strong>carbon</strong></a></td><td>https://carbon.now.sh/</td><td>一个有逼格的代码截图分享工具</td></tr>
<tr>
<td><strong>Diagram as a Code</strong></td><td><a target="_blank" href="https://github.com/mermaid-js/mermaid"><strong>Mermaid</strong></a></td><td>https://mermaid.js.org/</td><td>Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown</td></tr>
<tr>
<td><strong>Diagram as a Code</strong></td><td><a target="_blank" href="https://github.com/plantuml/plantuml"><strong>PlantUML</strong></a></td><td>https://plantuml.com/</td><td>Generate diagrams from textual description </td></tr>
<tr>
<td><strong>Diagram as a Code</strong></td><td><a target="_blank" href="https://github.com/jgraph/drawio"><strong>Draw.io</strong></a></td><td>https://app.diagrams.net/</td><td>draw.io, this project, is a configurable diagramming/whiteboarding visualization application. draw.io is jointly owned and developed by JGraph Ltd and draw.io AG.</td></tr>
<tr>
<td><strong>Diagram as a Code</strong></td><td><a target="_blank" href="https://github.com/excalidraw/excalidraw"><strong>Excalidraw</strong></a></td><td>https://excalidraw.com/</td><td>Excalidraw is a free software that offers a whiteboard tool that lets you easily sketch diagrams with a hand-drawn feel. Another features are the collaborative mode, and the ability to export the diagrams to PNG or SVG formats, and to save them locally in a JSON format</td></tr>
<tr>
<td><strong>Diagram as a Code</strong></td><td><a target="_blank" href="https://github.com/mingrammer/diagrams"><strong>mingrammer</strong></a></td><td>https://diagrams.mingrammer.com/</td><td>Diagrams as code for prototyping cloud system architecture.</td></tr>
<tr>
<td><strong>Web Framework</strong></td><td><a target="_blank" href="https://github.com/gin-gonic/gin"><strong>Gin</strong></a></td><td>https://gin-gonic.com/</td><td>Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API, but with performance up to 40 times faster than Martini. If you need smashing performance, get yourself some Gin.</td></tr>
<tr>
<td><strong>Web Framework</strong></td><td><a target="_blank" href="https://github.com/tiangolo/fastapi"><strong>FAST API</strong></a></td><td>https://fastapi.tiangolo.com/</td><td>FastAPI is a Web framework for developing RESTful APIs in Python. FastAPI is based on Pydantic and type hints to validate, serialize, and deserialize data, and automatically auto-generate OpenAPI documents. It fully supports asynchronous programming and can run with Gunicorn and ASGI servers for production such as Uvicorn and Hypercorn. To improve developer-friendliness, editor support was considered since the earliest days of the project.</td></tr>
<tr>
<td><strong>Web Framework</strong></td><td><a target="_blank" href="https://github.com/withastro/astro"><strong>Astro</strong></a></td><td>https://astro.build/</td><td>Astro works with your favorite content sources. Pull content from the filesystem or fetch it remotely from your favorite CMS, database, or API. Astro supports both static output (SSG) and live server output (SSR) that can render your content on-demand.</td></tr>
<tr>
<td><strong>Search Engine</strong></td><td><a target="_blank" href="https://github.com/typesense/typesense"><strong>Typesense</strong></a></td><td>https://typesense.org/</td><td>Typesense is a modern, privacy-friendly, open source search engine built from the ground up using cutting-edge search algorithms, that take advantage of the latest advances in hardware capabilities.</td></tr>
<tr>
<td><strong>Nocode Platform</strong></td><td><a target="_blank" href="https://github.com/nocodb/nocodb"><strong>NOCODB</strong></a></td><td>https://nocodb.com/</td><td>NocoDB is an open-source low-code platform for building and managing internal tools and turning your SQL Databases into a smart spreadsheet. It is a self-hosted alternative to Airtable, Notion, and Airtable.</td></tr>
<tr>
<td><strong>Distributed Database</strong></td><td><a target="_blank" href="https://github.com/rqlite/rqlite"><strong>rqlite</strong></a></td><td>https://rqlite.com/</td><td>rqlite is an easy-to-use, lightweight, distributed relational database, which uses SQLite as its storage engine. rqlite is simple to deploy, operating it is very straightforward, and its clustering capabilities provide you with fault-tolerance and high-availability. </td></tr>
<tr>
<td><strong>Multi-modal Database</strong></td><td><a target="_blank" href="https://github.com/surrealdb/surrealdb"><strong>SurrealDB</strong></a></td><td>https://surrealdb.com/</td><td>SurrealDB combines the database layer, the querying layer, and the API and authentication layer into one platform. Advanced table-based and row-based customisable access permissions allow for granular data access patterns for different types of users. There's no need for custom backend code and security rules with complicated database development.</td></tr>
<tr>
<td><strong>Multi-modal Database</strong></td><td><a target="_blank" href="https://github.com/arangodb/arangodb"><strong>ArangoDB</strong></a></td><td>https://www.arangodb.com/</td><td>ArangoDB is a free and open-source native graph database system developed by ArangoDB Inc. ArangoDB is a multi-model database system since it supports three data models with one database core and a unified query language AQL. AQL is mainly a declarative language and allows the combination of different data access patterns in a single query.</td></tr>
<tr>
<td><strong>Git for Data</strong></td><td><a target="_blank" href="https://github.com/dolthub/dolt"><strong>Dolt</strong></a></td><td>https://dolthub.com/</td><td>Dolt is a SQL database that you can fork, clone, branch, merge, push and pull just like a Git repository. Connect to Dolt just like any MySQL database to run queries or update the data using SQL commands. Use the command line interface to import CSV files, commit your changes, push them to a remote, or merge your teammate's changes.</td></tr>
<tr>
<td><strong>Personal Finance</strong></td><td><a target="_blank" href="https://github.com/firefly-iii/firefly-iii"><strong>Firefly III</strong></a></td><td>https://firefly-iii.org/</td><td>"Firefly III" is a (self-hosted) manager for your personal finances. It can help you keep track of your expenses and income, so you can spend less and save more. Firefly III supports the use of budgets, categories and tags. Using a bunch of external tools, you can import data. It also has many neat financial reports available.</td></tr>
<tr>
<td><strong>Monitoring and TSDB</strong></td><td><a target="_blank" href="https://github.com/prometheus/prometheus">Prometheus</a></td><td>https://prometheus.io/</td><td>Prometheus is a systems and service monitoring system. It collects metrics from configured targets at given intervals, evaluates rule expressions, displays the results, and can trigger alerts if some condition is observed to be true.</td></tr>
</tbody>
</table>
</div>]]></content:encoded></item><item><title><![CDATA[Documentation as a code]]></title><description><![CDATA[Documentation is the most critical activity of any product development. The engineer and user experience improve when there is up-to-date Documentation. Most often, in organizations and products, Documentation is an afterthought, and this is not a go...]]></description><link>https://blog.robinjiang.com/doc-as-code</link><guid isPermaLink="true">https://blog.robinjiang.com/doc-as-code</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835053119/7f04f419-e1ab-4193-a693-ecde56ec17d4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Documentation is the most critical activity of any product development. The engineer and user experience improve when there is up-to-date Documentation. Most often, in organizations and products, Documentation is an afterthought, and this is not a good practice. If we want more engineers to contribute to the product, Documentation should be considered as code and part of the product development. Engineers should be encouraged to write Documentation before writing the source code.</p>
<p>In many organizations, Documentation is everywhere, but it can be challenging to find. It is often written in various formats, and it is sometimes unclear who is responsible for it. It also needs to be clarified how to contribute to it. Confidence in Documentation could be higher if engineers spent more time writing; there is more incentive to write, and setting up a culture to write docs as part of engineering workflow contributes to Engineer Productivity which is a crucial metric for any organization.</p>
<p>The product engineering teams must identify workflows to integrate Documentation into the existing process to solve the challenges listed below.</p>
<ul>
<li><p>The Documentation is not part of the codebase</p>
</li>
<li><p>The Documentation is not part of the CI/CD pipeline</p>
</li>
<li><p>The method of writing Documentation is not integrated into the engineering workflow</p>
</li>
<li><p>The Documentation is not reviewed and tested</p>
</li>
<li><p>The Documentation is written in a separate tool and is not version controlled</p>
</li>
</ul>
<p>Documentation will never be part of engineering culture unless integrated into the codebase and workflow.</p>
<h2 id="heading-what-is-docs-as-code">What is Docs as Code?</h2>
<ul>
<li><p>Store the source file version of Documentation in a version control system like Git</p>
</li>
<li><p>Automatically build doc artifacts</p>
</li>
<li><p>Publish artifacts without human intervention</p>
</li>
</ul>
<h2 id="heading-why-docs-as-code">Why Docs as Code?</h2>
<ul>
<li><p>The Documentation evolves with the code. The flowchart, System Architecture and other diagrams will be up-to-date as the code changes</p>
</li>
<li><p>Long release cycles may result in logic or flowchart being forgotten or outdated</p>
</li>
<li><p>Consistency is critical for the adoption of Docs as a code. Teams can collaborate on the Documentation and can ensure that the Documentation is consistent across the product</p>
</li>
<li><p>Collaboration across product teams is the critical piece of why Documentation should be considered a code</p>
</li>
<li><p>Documentation can be reviewed and approved by the team members</p>
</li>
<li><p>Centralized Internal Documentation framework and familiar structured Documentation for all the products</p>
</li>
<li><p>Track Documentation mistakes as bugs</p>
</li>
<li><p>Documentation can be versioned, tested, and tracked</p>
</li>
<li><p>Manage the complexity around the documentation process</p>
</li>
<li><p>Visualize the Documentation in the form of diagrams, flowcharts, and images</p>
</li>
<li><p>Engineer can use other tools to model dependencies. For example, the Product team can use Mermaid to model the flowchart, system architecture, class diagram, and sequence diagrams</p>
</li>
<li><p>Avoid effort to redo the Documentation when a team member leaves the organization.</p>
</li>
<li><p>The product team can automate Workflows can be automated to generate the Documentation</p>
</li>
<li><p>Makes Documentation standout with <a target="_blank" href="https://www.markdownguide.org/">Markdown</a></p>
<p>  :::info Markdown is a simple, lightweight markup language that is easy to learn and use for plain text formatting and conversion to HTML and many other formats using a tool. Markdown is often used to format readme files, write messages in online discussion forums, and create rich text using a plain text editor. :::</p>
</li>
</ul>
<pre><code class="lang-plaintext">
flowchart

A[Start] --&gt; B[Engineer writes Documentation and Code]
    B --&gt;C[Engineer Commits Documentation and Code]
    C --&gt;D[Code Review and Testing]
    D --&gt;E[Documentation Review and Testing]
    E --&gt;F{Release}
    F --&gt;|Yes|G[Documentation is published]
    F --&gt;|No|B
    G --&gt;H[End]
</code></pre>
<h2 id="heading-types-of-documentation">Types of Documentation</h2>
<p>The most common types of Documentation for every product are:</p>
<ul>
<li><p>Long-form</p>
<ul>
<li>FAQs, User Guides, Tutorials, How-to Guides, etc.</li>
</ul>
</li>
<li><p>Functional</p>
<ul>
<li>REST API Documentation, SDK Documentation, etc.</li>
</ul>
</li>
</ul>
<h2 id="heading-how-to-do-docs-as-a-code">How to do Docs as a Code?</h2>
<ul>
<li><p>Version your Documentation. Just as you version your code, you should version your Documentation. Versioning allows tracking changes and rollbacks to previous versions if necessary.</p>
</li>
<li><p>Integrate Documentation with CI/CD pipeline. CI/CD Integration will allow you to automate the process of generating Documentation and publishing it to a central location</p>
</li>
<li><p>Start with Proof of Concept and extend to all the products gradually</p>
</li>
<li><p>Choose a static site generator (Documentation Tool) that can be integrated with the CI/CD pipeline</p>
</li>
</ul>
<h2 id="heading-docs-as-code-tools">Docs As Code Tools</h2>
<ul>
<li><p>Static Site Generators They are used for Long form documentation. Allows integration of diagrams, flowcharts, images, etc.</p>
<ul>
<li><a target="_blank" href="http://docusaurus.io">Docusaurus</a>, <a target="_blank" href="https://gohugo.io">Hugo</a>, <a target="_blank" href="https://www.gatsbyjs.com">Gatsby</a>, <a target="_blank" href="https://jekyllrb.com">Jekyll</a>, <a target="_blank" href="https://www.mkdocs.org">MkDocs</a> etc.</li>
</ul>
</li>
<li><p>Diagram as a code</p>
<p>  Allows creating diagrams, flowcharts, etc., in a code format. Think of documenting and visualizing a complex system architecture in a code format.</p>
<ul>
<li><a target="_blank" href="https://mermaid-js.github.io/mermaid/#/">Mermaid</a>, <a target="_blank" href="https://plantuml.com/">PlantUML</a>, <a target="_blank" href="https://graphviz.org/">Graphviz</a>, <a target="_blank" href="https://www.draw.io/">Draw.io</a>, <a target="_blank" href="https://diagrams.mingrammer.com">mingrammer/Diagrams</a></li>
</ul>
</li>
<li><p>Source code-based document generators</p>
<ul>
<li><a target="_blank" href="https://www.sphinx-doc.org/en/master/">Sphinx</a></li>
</ul>
</li>
<li><p>System documentation generators</p>
<ul>
<li><a target="_blank" href="https://github.com/rtomayko/ronn">ronn</a></li>
</ul>
</li>
</ul>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Everything(Infrastructure, Monitoring, Code, Containers, Documentation) as a code is already a reality. For some organizations, the shift to treating Documentation as a code is a complex overhaul of expectations, attitudes, processes, and toolsets. Once implemented, it will vastly improve the engineer and user experience. For open-source projects, it is even more essential to have good Documentation. It is a great way to attract new contributors and users.</p>
<h2 id="heading-references">References</h2>
<ul>
<li><a target="_blank" href="https://www.writethedocs.org/guide/doc-ops/#what-is-docops-anyway">DocOps</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Understanding REST API Design Rules]]></title><description><![CDATA[Introduction to REST API
REST (Representational State Transfer) is an architectural style for building distributed systems. A Web API conforms to the REST architectural style, called RESTful Web API. 
REST APIs are stateless, client-server, cacheable...]]></description><link>https://blog.robinjiang.com/rest-api-design-rules</link><guid isPermaLink="true">https://blog.robinjiang.com/rest-api-design-rules</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835047192/5aa13b45-3ccd-4e3f-a2f5-56370853c4aa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction-to-rest-api">Introduction to REST API</h2>
<p>REST (Representational State Transfer) is an architectural style for building distributed systems. A Web API conforms to the REST architectural style, called RESTful Web API. </p>
<p>REST APIs are stateless, client-server, cacheable, layered systems designed around resources. The set of resources is known as the REST API's <em>resource model</em></p>

<pre><code class="lang-mermaid">
sequenceDiagram

participant Client
participant Web API 
Participant Web Service 
Client-&gt;&gt;Web API: Request
Web API-&gt;&gt;Web Service: Request
Web Service-&gt;&gt;Web API: Response
Web API-&gt;&gt;Client: Response
</code></pre>
<p>REST APIs are one of the most common and fundamental ways to expose data and functionality as web services. REST APIs use HTTP requests to GET, PUT, POST, and DELETE data. </p>
<p>An adequately designed REST API should be easy to understand, use, and evolve over time. It will help clients and browser apps consume the API more efficiently. </p>
<p>Before designing and developing a REST API, we need to seek answers to the following questions:</p>
<ul>
<li>What are URI Paths? Structure of URI Path segments? </li>
<li>When to use plural nouns or verbs for URI Path segments?</li>
<li>What is the HTTP response status code, and how to use it in a specific scenario? </li>
<li>How to map non-CRUD operations to HTTP methods? </li>
</ul>
<h2 id="heading-understanding-uniform-resource-identifier-uri">Understanding Uniform Resource Identifier (URI)</h2>
<p>REST APIs use Uniform Resource Identifiers (URIs) to identify resources. A resource is any information that can be named. Resources are separated by forward slashes (/). A good URI should be short, easy to remember, and should give the user an idea about the resource.</p>
<h3 id="heading-uri-format">URI Format</h3>
<p>The URI format is as follows:</p>
<p>URI = scheme "://" host [ ":" port ] [ "/" path ] [ "?" query ] [ "#" fragment ]</p>
<p>```http request
http:////</p>
<pre><code>
### URI Resource Model 

Header|Description
------|-----------
Document | A <span class="hljs-built_in">document</span> resource is similar to database record or instance <span class="hljs-keyword">of</span> an object. It is a single resource that can be retrieved, created, updated, or deleted. &lt;br/&gt; For example, information about a blog author is a <span class="hljs-built_in">document</span> resource. &lt;br/&gt; <span class="hljs-string">`http://api.blog.com/authors/robin-gandhi`</span>
Collection | A collection resource is a server-managed directory <span class="hljs-keyword">of</span> resources. &lt;br/&gt; For example, a list <span class="hljs-keyword">of</span> blog authors is a collection resource. &lt;br/&gt; <span class="hljs-string">`http://api.blog.com/authors`</span>
Store | A store is a repository which is managed by client. Using store resource client can create, update, <span class="hljs-keyword">delete</span> and retrieve documents. &lt;br/&gt; <span class="hljs-string">`http://api.blog.com/store/authors/robin-gandhi`</span>
Controller | A controller resource models a procedure concept. It is a resource that represents a procedure that can be invoked. A controller resource is a collection resource that supports the POST method. The POST method is used to invoke the controller resource. The controller resource can be used to model a procedure that can be invoked. For example, the following URI models a controller resource that represents a procedure that can be invoked to send an email: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">br</span>/&gt;</span></span> <span class="hljs-string">``</span><span class="hljs-string">`POST /api.blog.com/email/email/send`</span><span class="hljs-string">``</span> &lt;br/&gt; <span class="hljs-string">``</span><span class="hljs-string">` {Collection}/{Store}/{Document}/{Controller}</span>
</code></pre><h2 id="heading-rest-api-design-rules">REST API Design Rules</h2>
<h3 id="heading-uri">URI</h3>
<ul>
<li><strong>Rule : Forward Slash (/) is used to separate resources in the URI and indicate a hierarchical relationship</strong></li>
</ul>
<p>A trailing forward slash (/) is not required as the last character of a URI. Many web servers automatically redirect requests with a trailing forward slash to the same URI without the trailing forward slash.</p>
<ul>
<li><p><strong>Rule : Use plural nouns for URI Path segments that represent collections or resources</strong></p>
</li>
<li><p><strong>Rule : Use HTTP Methods to Perform Operations on Resources</strong></p>
</li>
</ul>
<p>HTTP methods are used to perform operations on resources. The following table lists the HTTP methods and their corresponding operations:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>HTTP Method</td><td>Operation</td></tr>
</thead>
<tbody>
<tr>
<td>GET</td><td>Retrieve a resource</td></tr>
<tr>
<td>POST</td><td>Create a resource</td></tr>
<tr>
<td>PUT</td><td>Update a resource</td></tr>
<tr>
<td>DELETE</td><td>Delete a resource</td></tr>
<tr>
<td>PATCH</td><td>Update a resource with Partial data</td></tr>
</tbody>
</table>
</div><pre><code class="lang-JS"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">'body-parser'</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">3000</span>;

app.use(bodyParser.json());

app.get(<span class="hljs-string">'/authors'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {

res.send(<span class="hljs-string">'Authors List'</span>); 

<span class="hljs-comment">//get author list from Sql lite backend </span>

res.json(authors);

});

app.post(<span class="hljs-string">'/authors'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {

  res.send(<span class="hljs-string">'Add Author'</span>);

  <span class="hljs-comment">//add author to Sql lite backend</span>

  res.json(author);

});


<span class="hljs-comment">//update an author</span>

app.put(<span class="hljs-string">'/authors/:id'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {

  res.send(<span class="hljs-string">'Update Author'</span>);

  res.json(author);

});

<span class="hljs-comment">//delete an author</span>

app.delete(<span class="hljs-string">'/authors/:id'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {

  res.send(<span class="hljs-string">'Delete Author'</span>);

  res.json(author);

});

app.patch(<span class="hljs-string">'/authors/:id'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {

  res.send(<span class="hljs-string">'Update Author Email'</span>);

  res.json(author);

}); 


app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Blog Example app listening at http://localhost:<span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<ul>
<li><strong>Rule : Hyphen (-) is used to separate words in URI Path</strong></li>
</ul>
<p>Hyphens (-) are used to separate words in URI path. For example, the URI path for a resource named <em>user-profile</em> is <em>/user-profile</em>.</p>
<ul>
<li><strong>Rule : Underscore (_) is not used in URI</strong></li>
</ul>
<p>Underscores (_) are not used in URI path due to text editors and browsers depending on the font hide the underscore by underlining the text.</p>
<ul>
<li><strong>Rule : File Extensions are not used in URI</strong></li>
</ul>
<p>A REST API should not use file extensions in the URI. For example, the URI path for a resource named <em>user-profile</em> is <em>/user-profile</em> and not <em>/user-profile.json</em>.</p>
<ul>
<li><strong>Rule : If API Provides a developer portal then it should be accessible via a consistent subdomain</strong></li>
</ul>
<p>If an API provides a developer portal, then the developer portal should be accessible via a consistent subdomain. For example, the developer portal for the weather API is accessible via <em>developer.blog.api.com</em>.</p>
<ul>
<li><strong>Rule : Lowercase letters are preferred in URI</strong></li>
</ul>
<p>Lowercase letters are preferred in URI. For example, the URI path for a resource named <em>user-profile</em> is <em>/user-profile</em> and not <em>/User-Profile</em>.</p>
<ul>
<li><strong>Rule: Use a Verb or verb phrase for Controller Names</strong></li>
</ul>
<p>```http request
POST /api.blog.com/email/email/send</p>
<pre><code>
- **Rule: CRUD <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">names</span> <span class="hljs-title">should</span> <span class="hljs-title">not</span> <span class="hljs-title">be</span> <span class="hljs-title">used</span> <span class="hljs-title">in</span> <span class="hljs-title">the</span> <span class="hljs-title">URI</span>**

<span class="hljs-title">The</span> <span class="hljs-title">following</span> <span class="hljs-title">table</span> <span class="hljs-title">lists</span> <span class="hljs-title">the</span> <span class="hljs-title">CRUD</span> <span class="hljs-title">functions</span> <span class="hljs-title">and</span> <span class="hljs-title">their</span> <span class="hljs-title">corresponding</span> <span class="hljs-title">HTTP</span> <span class="hljs-title">methods</span>:

| <span class="hljs-title">CRUD</span> <span class="hljs-title">Function</span> | <span class="hljs-title">HTTP</span> <span class="hljs-title">Method</span> |
| ------------- | ----------- |
| <span class="hljs-title">Create</span>        | <span class="hljs-title">POST</span>        |
| <span class="hljs-title">Read</span>          | <span class="hljs-title">GET</span>         |
| <span class="hljs-title">Update</span>        | <span class="hljs-title">PUT</span>         |
| <span class="hljs-title">Delete</span>        | <span class="hljs-title">DELETE</span>      |

<span class="hljs-title">e</span>.<span class="hljs-title">g</span>. <span class="hljs-title">Preferred</span> <span class="hljs-title">API</span> <span class="hljs-title">Interface</span>

```<span class="hljs-title">http</span> <span class="hljs-title">request</span>
<span class="hljs-title">PUT</span> /<span class="hljs-title">api</span>.<span class="hljs-title">blog</span>.<span class="hljs-title">com</span>/<span class="hljs-title">authors</span>/<span class="hljs-title">robin</span>-<span class="hljs-title">gandhi</span></span>
</code></pre><p>Anti pattern </p>
<p>```http request
DELETE /deleteusers/abc/</p>
<pre><code>
- **Rule: New URIs should be introduced <span class="hljs-keyword">new</span> concepts**

A REST API should introduce <span class="hljs-keyword">new</span> URIs <span class="hljs-keyword">for</span> <span class="hljs-keyword">new</span> concepts. For example, the following table lists the URIs <span class="hljs-keyword">for</span> a user resource:

| URI | Description |
| --- | ----------- |
| /authors | Returns a list <span class="hljs-keyword">of</span> authors |
| <span class="hljs-regexp">/authors/</span>robin | Returns the author details |
| <span class="hljs-regexp">/authors/</span>robin/books | Returns a list <span class="hljs-keyword">of</span> articles written by the author 

- **Rule: <span class="hljs-built_in">JSON</span> should be well formed and supported <span class="hljs-keyword">for</span> resource representation**

- **Rule: Add Versioning at the start <span class="hljs-keyword">of</span> the URI**

<span class="hljs-string">``</span><span class="hljs-string">`http request

http://api.blog.com/v1/authors/robin-gandhi</span>
</code></pre><h3 id="heading-http-methods">HTTP Methods</h3>
<ul>
<li><p><strong>Rule: GET must be used to retrieve representation of a resource</strong></p>
</li>
<li><p><strong>Rule: Head must be used to retrieve metadata of a resource and response headers</strong></p>
</li>
<li><p><strong>Rule: PUT must be used to both insert and update a resource</strong></p>
</li>
<li><p><strong>Rule: POST must be used to create a resource</strong></p>
</li>
<li><p><strong>Rule: POST must be used to execute a controller</strong></p>
</li>
<li><p><strong>Rule: DELETE must be used to delete a resource</strong></p>
</li>
<li><p><strong>Rule: OPTIONS must be used to retrieve supported HTTP methods</strong></p>
</li>
<li><p><strong>Rule : Use HTTP Status Codes to Indicate Response Status</strong></p>
</li>
</ul>
<p>HTTP status codes are used to indicate the response status of an HTTP request. The following table lists the HTTP status codes and their corresponding meanings:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>HTTP Status Code</td><td>Meaning</td><td>Information</td></tr>
</thead>
<tbody>
<tr>
<td></td></tr>
</tbody>
</table>
</div><p>100 | 100 and above are information | 100 and above are for "Information". You rarely use them directly. Responses with these status codes cannot have a body.
| 200 OK           | The request was successful | 200 and above are for "Successful" responses. These are the ones you would use the most. 200 is the default status code for a successful response.
| 201 Created      | The request was successful and a resource was created | 201 is "Created". This is used when a new resource is created. The response will contain a Location header with the URI of the new resource.
| 204 No Content   | The request was successful but there is no representation to return | A special case is 204, "No Content". This response is used when there is no content to return to the client, and so the response must not have a body.
| 300 Multiple Choices | The requested resource corresponds to any one of a set of representations, each with its own specific location | 300 and above are for "Redirection". These are used when the client needs to take some additional action in order to complete the request. For example, if you request a resource that has been moved to a different location, the response will be 301, "Moved Permanently", and the response will contain a Location header with the new location of the resource. The client can then make a new request to that location.
| 400 Bad Request  | The request could not be understood by the server | 400 and above are for "Client Error" responses. These are used when the client has made a mistake in its request. For example, if you request a resource that doesn't exist, the response will be 404, "Not Found". 
| 401 Unauthorized | The request requires user authentication | 401 is "Unauthorized". This is used when the client needs to authenticate itself to get the requested response.
| 403 Forbidden    | The server understood the request, but is refusing to fulfill it | 403 is "Forbidden". This is used when the client is not allowed to access the resource. For example, if you try to access a resource that you don't have permission to access, the response will be 403, "Forbidden".
| 404 Not Found    | The server has not found anything matching the Request-URI | 404 is "Not Found". This is used when the client requests a resource that doesn't exist. For example, if you request a resource that doesn't exist, the response will be 404, "Not Found".
| 405 Method Not Allowed | The method specified in the Request-Line is not allowed for the resource identified by the Request-URI | 405 is "Method Not Allowed". This is used when the client requests a resource using a method that isn't allowed. For example, if you try to access a resource using the POST method, but the resource only supports the GET method, the response will be 405, "Method Not Allowed".
| 500 Internal Server Error | The server encountered an unexpected condition which prevented it from fulfilling the request | 500 and above are for "Server Error" responses. These are used when the server encounters an error while fulfilling the request. For example, if the server runs out of memory while fulfilling the request, the response will be 500, "Internal Server Error".</p>
<p>The approaches and best practices of REST API outlined in this blog article will help anyone follow consistent guidelines for designing and developing REST APIs. </p>
<h2 id="heading-references">References</h2>
<ul>
<li><a target="_blank" href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">Roy Fielding's Dissertation</a></li>
<li><a target="_blank" href="https://restfulapi.net/">What is REST</a></li>
<li><a target="_blank" href="https://www.amazon.in/REST-API-Design-Rulebook-Consistent-ebook/dp/B005XE5A7Q/ref=sr_1_1?keywords=rest+api+design+rulebook&amp;qid=1665926194&amp;qu=eyJxc2MiOiIwLjQzIiwicXNhIjoiMC41NCIsInFzcCI6IjAuMDAifQ%3D%3D&amp;sprefix=REST+API+D%2Caps%2C194&amp;sr=8-1">REST API Design Rulebook</a></li>
<li><a target="_blank" href="https://www.amazon.in/Hands-RESTful-Design-Patterns-Practices-ebook/dp/B07BJL399D/ref=sr_1_2?keywords=rest+api+design+rulebook&amp;qid=1665926194&amp;qu=eyJxc2MiOiIwLjQzIiwicXNhIjoiMC41NCIsInFzcCI6IjAuMDAifQ%3D%3D&amp;sprefix=REST+API+D%2Caps%2C194&amp;sr=8-2">Hands-on RESTful API Design Patterns</a></li>
</ul>

]]></content:encoded></item><item><title><![CDATA[Understanding MongoDB Replicasets and Write Concern - Part 1]]></title><description><![CDATA[Introducing Replicasets
The way to achieve fault tolerance in MongoDB is through the use of replica sets. 
stateDiagram-v2
    [*] --> Application
    direction LR
    state Application
    Application --> replicaset      
   state replicaset
    {
 ...]]></description><link>https://blog.robinjiang.com/mongodb-replicaset-write-concern-read-pref</link><guid isPermaLink="true">https://blog.robinjiang.com/mongodb-replicaset-write-concern-read-pref</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835041434/f53eb0da-2570-4c84-9eed-37dedc1fdefb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introducing-replicasets">Introducing Replicasets</h2>
<p>The way to achieve fault tolerance in MongoDB is through the use of <code>replica sets</code>. </p>
<pre><code class="lang-mermaid">stateDiagram-v2
    [*] --&gt; Application
    direction LR
    state Application
    Application --&gt; replicaset      
   state replicaset
    {
    direction RL
    Primary:primary
    Secondary1:secondary 
    Secondary2:secondary
    Secondary1--&gt;Primary : Fetch Oplog
    Secondary2--&gt;Primary : Fetch Oplog

    }
</code></pre>
<p>Two or more <code>secondary</code> nodes along with a <code>primary</code> node forms a replica set. Application makes all the read/write calls to the primary node which propagate all the write requests synchronously or asynchronously to the secondary nodes. </p>
<p>The Secondary nodes fetches the data via Oplog pull from Primary or other nodes. </p>

<p>The Primary node is responsible for all the writes and reads. The secondary nodes can be utilized for reads via <a target="_blank" href="https://docs.mongodb.com/manual/reference/method/Mongo.setSecondaryOk/"><code>setSecondaryOk</code></a> or <a target="_blank" href="https://docs.mongodb.com/manual/reference/read-preference/"><code>readPreference</code></a>. </p>
<h2 id="heading-understanding-oplog">Understanding Oplog</h2>
<p>When the application performs a write, the primary node applies the write to the database like a standalone. </p>
<p>The difference between Replicaset write and standalone write is that replica set nodes have an <code>OpObserver</code> that inserts a document to the <strong>oplog</strong> whenever a write to the database happens, describing the write. The <strong>oplog</strong> is a capped collection called <code>oplog.rs</code> in the <code>local</code> database. </p>
<p>For every operation performed in a write, the primary node inserts a document into the oplog. The oplog is a capped collection, which means that it has a maximum size. When the oplog reaches its maximum size, MongoDB removes the oldest entries to make room for new entries. </p>
<p>For a write which performs create collection and insert, there are two oplog entries created one for <code>create</code> collection and another for <code>insert</code>.</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// mongod_main.cpp</span>
setUpObservers(service);
</code></pre>
<pre><code class="lang-cpp">
<span class="hljs-comment">//op_observer_registry.h</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">onCreateCollection</span><span class="hljs-params">(OperationContext* <span class="hljs-keyword">const</span> opCtx,
                            <span class="hljs-keyword">const</span> CollectionPtr&amp; coll,
                            <span class="hljs-keyword">const</span> NamespaceString&amp; collectionName,
                            <span class="hljs-keyword">const</span> CollectionOptions&amp; options,
                            <span class="hljs-keyword">const</span> BSONObj&amp; idIndex,
                            <span class="hljs-keyword">const</span> OplogSlot&amp; createOpTime,
                            <span class="hljs-keyword">bool</span> fromMigrate)</span> <span class="hljs-keyword">override</span> </span>{
        ReservedTimes times{opCtx};
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>&amp; o : _observers)
            o-&gt;onCreateCollection(
                opCtx, coll, collectionName, options, idIndex, createOpTime, fromMigrate);
    }

 <span class="hljs-keyword">using</span> OpObserver::onInserts;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">onInserts</span><span class="hljs-params">(OperationContext* <span class="hljs-keyword">const</span> opCtx,
                   <span class="hljs-keyword">const</span> NamespaceString&amp; nss,
                   <span class="hljs-keyword">const</span> UUID&amp; uuid,
                   <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;InsertStatement&gt;::const_iterator begin,
                   <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;InsertStatement&gt;::const_iterator end,
                   <span class="hljs-keyword">bool</span> fromMigrate)</span> <span class="hljs-keyword">override</span> </span>{
        ReservedTimes times{opCtx};
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>&amp; o : _observers)
            o-&gt;onInserts(opCtx, nss, uuid, begin, end, fromMigrate);
    }
</code></pre>
<h2 id="heading-understanding-write-concern">Understanding Write Concern</h2>
<p>Write concern is a way to ensure that the write operations are propagated to the secondary nodes.</p>
<h3 id="heading-default-write-concern">Default Write Concern</h3>
<p>If a write operation does not explicitly specify a write concern, the server will use a default
write concern. </p>
<p>This default write concern will be defined by either the</p>
<ul>
<li><strong>Cluster-Wide write concern</strong>, explicitly set by the user 
or</li>
<li><strong>Implicit Default write concern</strong>, implicitly set by the server based on replica set configuration.</li>
</ul>
<h4 id="heading-cluster-wide-write-concern">Cluster-Wide Write Concern</h4>
<p>The cluster-wide write concern is set by the user using the <a target="_blank" href="https://docs.mongodb.com/manual/reference/command/setDefaultRWConcern/"><code>setDefaultRWConcern</code></a> command. Setting the cluster-wide write concern will cause the implicit default write concern not to take effect.</p>
<p>On a sharded cluster, the cluster-wide write concern is set on the config server. On a replica set, the cluster-wide write concern is set on the primary node. The below code snippets shows how the cluster-wide write concern is set on the primary node and stored on the config node. </p>
<pre><code class="lang-javascript">db.adminCommand(
  {
    <span class="hljs-attr">setDefaultRWConcern</span> : <span class="hljs-number">1</span>,
    <span class="hljs-attr">defaultReadConcern</span>: { &lt;read concern&gt; },
    defaultWriteConcern: { &lt;write concern&gt; },
    writeConcern: { &lt;write concern&gt; },
    comment: &lt;any&gt;
  }
)
</code></pre>
<pre><code class="lang-cpp"><span class="hljs-comment">//cluster_rwc_defaults_commands.cpp </span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClusterSetDefaultRWConcernCommand</span> :</span> <span class="hljs-keyword">public</span> BasicCommand {
<span class="hljs-keyword">public</span>:
    ClusterSetDefaultRWConcernCommand() : BasicCommand(<span class="hljs-string">"setDefaultRWConcern"</span>) {}

    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">run</span><span class="hljs-params">(OperationContext* opCtx,
             <span class="hljs-keyword">const</span> DatabaseName&amp;,
             <span class="hljs-keyword">const</span> BSONObj&amp; cmdObj,
             BSONObjBuilder&amp; result)</span> <span class="hljs-keyword">override</span> </span>{
        <span class="hljs-keyword">auto</span> configShard = Grid::get(opCtx)-&gt;shardRegistry()-&gt;getConfigShard();
        <span class="hljs-keyword">auto</span> cmdResponse = uassertStatusOK(configShard-&gt;runCommandWithFixedRetryAttempts(
            opCtx,
            ReadPreferenceSetting(ReadPreference::PrimaryOnly),
            NamespaceString::kAdminDb.toString(),
            CommandHelpers::appendMajorityWriteConcern(
                CommandHelpers::filterCommandRequestForPassthrough(cmdObj),
                opCtx-&gt;getWriteConcern()),
            Shard::RetryPolicy::kNotIdempotent));

        uassertStatusOK(cmdResponse.commandStatus);
        uassertStatusOK(cmdResponse.writeConcernStatus);

        <span class="hljs-comment">// Quickly pick up the new defaults by setting them in the cache.</span>
        <span class="hljs-keyword">auto</span> newDefaults = RWConcernDefault::parse(IDLParserContext(<span class="hljs-string">"ClusterSetDefaultRWConcern"</span>),
                                                   cmdResponse.response);
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">auto</span> optWC = newDefaults.getDefaultWriteConcern()) {
            <span class="hljs-keyword">if</span> (optWC-&gt;hasCustomWriteMode()) {
                LOGV2_WARNING(
                    <span class="hljs-number">6081700</span>,
                    <span class="hljs-string">"A custom write concern is being set as the default write concern in a sharded "</span>
                    <span class="hljs-string">"cluster. This set is unchecked, but if the custom write concern does not "</span>
                    <span class="hljs-string">"exist on all shards in the cluster, errors will occur upon writes"</span>,
                    <span class="hljs-string">"customWriteConcern"</span>_attr = stdx::get&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&gt;(optWC-&gt;w));
            }
        }
        ReadWriteConcernDefaults::get(opCtx).setDefault(opCtx, <span class="hljs-built_in">std</span>::move(newDefaults));

        CommandHelpers::filterCommandReplyForPassthrough(cmdResponse.response, &amp;result);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
</code></pre>
<h4 id="heading-implicit-default-write-concern">Implicit default write concern</h4>
<p>The implicit default write concern is calculated and set on startup by the server based on the replica set configuration. The server will set the implicit default write concern to the following:</p>
<ul>
<li>If the replica set has a single node, the implicit default write concern is <code>{ w: 1 }</code></li>
<li>For most of the cases the implicit default write concern is <code>{ w: "majority" }</code></li>
</ul>
<h5 id="heading-psa">PSA</h5>
<p><code>implicitDefaultWriteConcern = if ((#arbiters &gt; 0) AND (#non-arbiters &lt;= majority(#voting nodes)) then {w:1} else {w:majority}</code></p>
<p>Implicit default to a value that the set can satisfy in the event of one data-bearing node
going down. That is, the number of data-bearing nodes must be strictly greater than the majority
of voting nodes for the set to set <code>{w: "majority"}</code>.</p>
<p>For example, if we have a PSA replica set, and the secondary goes down, the primary cannot
successfully acknowledge a majority write as the majority for the set is two nodes. However, the
primary will remain primary with the arbiter's vote. In this case, the DWCF will have preemptively
set the IDWC to <code>{w: 1}</code> so the user can still perform writes to the replica set.</p>
<h5 id="heading-sharded-cluster">Sharded Cluster</h5>
<p>For a sharded cluster, the implicit default write concern is set to <code>{ w: "majority" }</code> if the
cluster has a majority of voting nodes. Otherwise, the implicit default write concern is set to
<code>{ w: 1 }</code>.</p>
<h2 id="heading-understanding-secondary-nodes-operations">Understanding Secondary Nodes Operations</h2>
<p>The secondary nodes will choose the node with the highest <code>lastApplied</code> timestamp as the<strong> sync source</strong>. The secondary nodes will then <strong>pull</strong> the oplog entries from the sync source and apply them to its own oplog.</p>
<p>The Secondary will also keep its <strong>sync source</strong> uptodate with its progress, this helps primary satisfy the read concern. </p>
<p>Here are the high level steps performed to select and probe the sync source</p>
<ol>
<li><code>TopologyCoordinator</code> checks if user requested a specific sync source using <code>replSetSyncFrom</code> command. If so, it will use that sync source. Otherwise, it will use the sync source from the last successful election.</li>
<li>Check if <strong>chaining</strong> is disabled. If so, the secondary will always use primary as its sync source </li>
</ol>
<pre><code class="lang-cpp"> <span class="hljs-keyword">if</span> (chainingPreference == ChainingPreference::kUseConfiguration &amp;&amp;
        !_rsConfig.isChainingAllowed()) {
        <span class="hljs-keyword">if</span> (_currentPrimaryIndex == <span class="hljs-number">-1</span>) {
            LOG(<span class="hljs-number">1</span>) &lt;&lt; <span class="hljs-string">"Cannot select a sync source because chaining is"</span>
                      <span class="hljs-string">" not allowed and primary is unknown/down"</span>;
            _syncSource = HostAndPort();
            <span class="hljs-keyword">return</span> _syncSource;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (_memberIsBlacklisted(*_currentPrimaryMember(), now)) {
            LOG(<span class="hljs-number">1</span>) &lt;&lt; <span class="hljs-string">"Cannot select a sync source because chaining is not allowed and primary "</span>
                      <span class="hljs-string">"member is blacklisted: "</span>
                   &lt;&lt; _currentPrimaryMember()-&gt;getHostAndPort();
            _syncSource = HostAndPort();
            <span class="hljs-keyword">return</span> _syncSource;
</code></pre>
<ol>
<li>Fetch latest opTime. Do not sync from a node where newest oplog is more than <code>maxSyncSourceLagSecs</code></li>
</ol>
<pre><code class="lang-cpp">    <span class="hljs-keyword">if</span> (_currentPrimaryIndex != <span class="hljs-number">-1</span>) {
        OpTime primaryOpTime = _memberData.at(_currentPrimaryIndex).getHeartbeatAppliedOpTime();

        <span class="hljs-comment">// Check if primaryOpTime is still close to 0 because we haven't received</span>
        <span class="hljs-comment">// our first heartbeat from a new primary yet.</span>
        <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> maxLag =
            <span class="hljs-keyword">static_cast</span>&lt;<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span>&gt;(durationCount&lt;Seconds&gt;(_options.maxSyncSourceLagSecs));
        <span class="hljs-keyword">if</span> (primaryOpTime.getSecs() &gt;= maxLag) {
            oldestSyncOpTime =
                OpTime(Timestamp(primaryOpTime.getSecs() - maxLag, <span class="hljs-number">0</span>), primaryOpTime.getTerm());
        }
    }
</code></pre>
<ol>
<li>Loop through all the nodes and find the closest node which satisfies the condition </li>
</ol>
<pre><code class="lang-cpp"><span class="hljs-function">HostAndPort <span class="hljs-title">TopologyCoordinator::chooseNewSyncSource</span><span class="hljs-params">(Date_t now,
                                                     <span class="hljs-keyword">const</span> OpTime&amp; lastOpTimeFetched,
                                                     ChainingPreference chainingPreference)</span> </span>{

...
...
...
</code></pre>
<h3 id="heading-oplog-fetching">Oplog Fetching</h3>
<p>The secondary node will fetch the oplog entries from the sync source to keep its data syncronized. The entire implementation of the oplog fetching is in the <code>OplogFetcher</code> class which runs in a separate thread and communicates via a dedicated client connection.</p>
<pre><code class="lang-cpp">
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">OplogFetcher::setConnection</span><span class="hljs-params">(<span class="hljs-built_in">std</span>::<span class="hljs-built_in">unique_ptr</span>&lt;DBClientConnection&gt;&amp;&amp; _connectedClient)</span> </span>{
    <span class="hljs-comment">// Can only call this once, before startup.</span>
    invariant(!_conn);
    _conn = <span class="hljs-built_in">std</span>::move(_connectedClient);
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Guide to Software Bill of Materials(SBoM) and Docker SBOM CLI]]></title><description><![CDATA[An Software Bill of Materials (SBoM) is a formal record containing the details and supply chain relationships of various components used in building the software. These components, including libraries and modules, can be proprietary or open source,fr...]]></description><link>https://blog.robinjiang.com/docker-sbom</link><guid isPermaLink="true">https://blog.robinjiang.com/docker-sbom</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835035122/334e9c93-c479-4541-a720-0f96595e9754.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An <strong>Software Bill of Materials (SBoM)</strong> is a formal record containing the details and supply chain relationships of various components used in building the software. These components, including libraries and modules, can be proprietary or open source,free or paid and the data can be widely available or access-restricted.</p>

<p><img src="sbom.webp" alt /></p>
<p>SBoM is analogous to a list of ingredients on food packaging. In May 2021, the US President released the <a target="_blank" href="https://www.whitehouse.gov/briefing-room/presidential-actions/2021/05/12/executive-order-on-improving-the-nations-cybersecurity/">Executive Order</a> on improving the Nation’s Cybersecurity. The Software Bill of Materials (SBoM) directly impacts all developers. The SBoM requires third-party software companies to provide customers with the code equivalent of a “nutrition chart.”</p>
<h2 id="heading-when-should-sbom-be-used-use-cases">When should SBoM be used – Use cases ?</h2>
<ul>
<li><p><strong>Developing products</strong></p>
<ul>
<li>Scan vulnerabilities in the components</li>
<li>Keep codebase to bare minimum, reduce the number of dependencies and size</li>
<li>Generate SBoM for end users</li>
</ul>
</li>
<li><p><strong>IT Operations</strong></p>
<ul>
<li>Understand operational risk</li>
<li>Understand potential exploitations</li>
<li>Real time asset inventory</li>
<li>Software Selection</li>
<li>Identify known vulnerabilities and compliance</li>
</ul>
</li>
<li><p><strong>EOL</strong></p>
<ul>
<li>Complete visibility to components before evaluation or deploying in production</li>
<li>Understand the software architecture and the dependencies of the software</li>
</ul>
</li>
</ul>
<h2 id="heading-why-sbom">Why SBOM ?</h2>
<ul>
<li><p>Requirement from regulatory bodies to track the components used in the software</p>
</li>
<li><p>Transparency of components getting shipped</p>
</li>
<li><p>Container ecosystem has exploded and the need to track the components getting shipped is a must</p>
</li>
<li><p>Software Vulnerabilities are bugs</p>
</li>
<li><p>Detecting and remediating Vulnerabilities</p>
</li>
</ul>
<h2 id="heading-sbom-formats">SBOM Formats</h2>
<ul>
<li><p><strong>SPDX (Software Package Data Exchange )</strong></p>
<ul>
<li>Open standard for communicating software bill of material information, including components, licenses, copyrights and security references. Reduces redundant work by providing a common format for organizations and communities to share and use</li>
</ul>
</li>
<li><p><strong>CycloneDX</strong></p>
<ul>
<li>Open Web Application Security Project(OWASP) CycloneDX is a lightweight Software Bill of Materials (SBOM) standard designed for use in application security contexts and supply chain component analysis.</li>
</ul>
</li>
<li><p><strong>SWID (Software Identification Tags)</strong></p>
<ul>
<li>SWID is used primarily to identify installed software and is the preferred format of the NVD. SWID tags are used in the National Vulnerability Database to describe vulnerable components. The CycloneDX specification compliments this work as CycloneDX documents can incorporate SWID tags and other high-level SWID metadata and optionally include entire SWID documents. Use of SWID tag ID’s are useful in determining if a specific component has known vulnerabilities.</li>
</ul>
</li>
</ul>
<h2 id="heading-docker-desktop-sbom-cli">Docker Desktop – SBOM CLI</h2>
<p>In Docker Desktop 4.7.0 Docker introduced and included a new experimental docker sbom CLI that is used for displaying SBoM for any container image. docker sbom scans the layer of container images using the Syft Project</p>
<h2 id="heading-usage">Usage</h2>
<ul>
<li>Display SBOM in CyloneDX format</li>
</ul>
<pre><code class="lang-shell">
$ docker sbom mongo:latest --format cyclonedx-json | more

{
      "type": "library",
      "publisher": "MongoDB Packaging \u003cpackaging@mongodb.com\u003e",
      "name": "mongodb-org-server",
      "version": "5.0.9",
      "cpe": "cpe:2.3:a:mongodb-org-server:mongodb-org-server:5.0.9:*:*:*:*:*:*:*",
      "purl": "pkg:deb/ubuntu/mongodb-org-server@5.0.9?arch=arm64\u0026upstream=mongodb-org\u0026distro=ubuntu-20.04",
      "properties": [
        {
          "name": "syft:package:foundBy",
          "value": "dpkgdb-cataloger"
        },
        {
          "name": "syft:package:metadataType",
          "value": "DpkgMetadata"
        }
</code></pre>
<ul>
<li>Display SBOM summary of packages. e.g. using the below command we can check for the log4j vulnerabilities</li>
</ul>
<pre><code class="lang-shell">$ docker sbom neo4j | grep log4j

log4j-api                           2.17.1                                     java-archive
log4j-core                          2.17.1                                     java-archive

$ docker sbom neo4j:4.4.1 | grep log4j

log4j-api                           2.15.0                                     java-archive
log4j-core                          2.15.0                                     java-archive

$ docker sbom elasticsearch:7.16.3 | grep log4j

elasticsearch-log4j                   7.16.3                             java-archive
log4j-1.2-api                         2.17.1                             java-archive
log4j-api                             2.17.1                             java-archive
log4j-core                            2.17.1                             java-archive
log4j-slf4j-impl                      2.17.1                             java-archive
</code></pre>
<p>There are many benefits to generate SBOM for compliance and vulnerability analysis. Further SBOM can be used for input to open source vulnerability databases like <a target="_blank" href="https://github.com/snyk/cli">Snyk</a> and open source vulnerability scanning tools like <a target="_blank" href="https://github.com/anchore/grype">Grype</a></p>
]]></content:encoded></item><item><title><![CDATA[How to configure Percona MongoDB Replicaset, Percona Backup Manager, Backup Agent using Docker and perform Replicaset backup, restore usi...]]></title><description><![CDATA[In this blog post, i will walk you through the steps required to containerize Percona Server for MongoDB, Percona Backup Manager, and Agent from source and configure cloud-native S3(Simple Storage Service) compatible distributed object storage MINIO ...]]></description><link>https://blog.robinjiang.com/percona-mongo-replicaset-minio</link><guid isPermaLink="true">https://blog.robinjiang.com/percona-mongo-replicaset-minio</guid><category><![CDATA[MongoDB]]></category><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835028487/9512086f-20b5-4420-951a-93075e285224.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog post, i will walk you through the steps required to containerize <a target="_blank" href="https://github.com/percona/percona-server-mongodb">Percona Server for MongoDB</a>, <a target="_blank" href="https://github.com/percona/percona-backup-mongodb">Percona Backup Manager</a>, and Agent from source and configure cloud-native S3(<strong>S</strong>imple <strong>S</strong>torage <strong>S</strong>ervice) compatible distributed object storage <a target="_blank" href="https://min.io/">MINIO</a> to backup and restore Percona MongoDB snapshot backups.</p>

<p><img src="minio.webp" alt /></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Ensure the below binaries are installed before starting the setup and configuration</p>
<p><a target="_blank" href="https://www.docker.com/get-started/">Docker</a> or <a target="_blank" href="https://podman.io/">Podman</a> to containerize Percona MongoDB replicaset and PBM Agent
<a target="_blank" href="https://docs.docker.com/compose/install/">Docker Compose</a>
<a target="_blank" href="https://go.dev/learn/">Golang</a> compiler – Build Percona Backup Manager binaries
<a target="_blank" href="https://www.portainer.io/">Portainer</a> (Optional) – Intuitive UI for container configuration and monitoring
Let us perform the below steps to set up PSMDB Replicaset; PBM Agent; Minio, S3 compatible bucket, and PBM configuration to perform backups and restores from the bucket.</p>
<h2 id="heading-steps">Steps</h2>
<ul>
<li>Create the Docker environment file with Docker Image, tag, port, and replicaset information. Save the file as .env in the working directory</li>
</ul>
<pre><code class="lang-shell">MONGODB_IMAGE=percona/percona-server-mongodb
MONGODB_VERSION=5.0
MONGO1_PORT=0.0.0.0:15000
MONGO2_PORT=0.0.0.0:15001
MONGO3_PORT=0.0.0.0:15002
MONGODB_PORT=27017
MONGODB_DOCKER_NETWORK=mongo_net
RS_NAME=rs1
</code></pre>
<ul>
<li>Create keyFile , Dockerfile and download percona-backup-manager source code in the working directory</li>
</ul>
<pre><code class="lang-shell">$ git clone https://github.com/percona/percona-backup-mongodb.git

ARG MONGODB_VERSION
ARG MONGODB_IMAGE
FROM ${MONGODB_IMAGE}:${MONGODB_VERSION}
USER root
COPY keyFile /opt/keyFile
RUN chown mongodb /opt/keyFile &amp;&amp; chmod 400 /opt/keyFile &amp;&amp; mkdir -p /home/mongodb/ &amp;&amp; chown mongodb /home/mongodb
USER mongodb
</code></pre>
<ul>
<li>Create Docker Compose file</li>
</ul>
<pre><code class="lang-YAML"><span class="hljs-attr">version:</span> <span class="hljs-string">"3.8"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">rs101:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">/home/robin/dev/psmdb</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_VERSION=${MONGODB_VERSION}</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_IMAGE=${MONGODB_IMAGE}</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">rs101</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=mongod"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">REPLSET_NAME=rs1</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MONGO_USER=dba</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">BACKUP_USER=bcp</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">MONGO_PASS=test1234</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"${MONGO1_PORT}:${MONGODB_PORT}"</span>
    <span class="hljs-comment"># command: mongod --replSet rs1 --port ${MONGO1_PORT}:27017 --storageEngine wiredTiger --keyFile /opt/keyFile --wiredTigerCacheSizeGB 1</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"--replSet"</span>, <span class="hljs-string">"${RS_NAME}"</span>, <span class="hljs-string">"--bind_ip_all"</span>, <span class="hljs-string">"--storageEngine"</span>, <span class="hljs-string">"wiredTiger"</span> , <span class="hljs-string">"--keyFile"</span>, <span class="hljs-string">"/opt/keyFile"</span>]
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">data-rs101:/data/db</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./scripts/start.sh:/opt/start.sh</span>
  <span class="hljs-attr">rs102:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">/home/robin/dev/psmdb</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_VERSION=${MONGODB_VERSION}</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_IMAGE=${MONGODB_IMAGE}</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">rs102</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=mongod"</span>
    <span class="hljs-comment"># command: mongod --replSet rs1 --port 27017 --storageEngine wiredTiger --keyFile /opt/keyFile --wiredTigerCacheSizeGB 1</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"${MONGO2_PORT}:${MONGODB_PORT}"</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"--replSet"</span>, <span class="hljs-string">"${RS_NAME}"</span>, <span class="hljs-string">"--bind_ip_all"</span>, <span class="hljs-string">"--storageEngine"</span>, <span class="hljs-string">"wiredTiger"</span> , <span class="hljs-string">"--keyFile"</span>, <span class="hljs-string">"/opt/keyFile"</span>]
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">data-rs102:/data/db</span>
  <span class="hljs-attr">rs103:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">/home/robin/dev/psmdb</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_VERSION=${MONGODB_VERSION}</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_IMAGE=${MONGODB_IMAGE}</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">rs103</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=mongod"</span>
    <span class="hljs-comment"># command: mongod --replSet rs1 --port 27017 --storageEngine wiredTiger --keyFile /opt/keyFile --wiredTigerCacheSizeGB 1</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"${MONGO3_PORT}:${MONGODB_PORT}"</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"--replSet"</span>, <span class="hljs-string">"${RS_NAME}"</span>, <span class="hljs-string">"--bind_ip_all"</span>, <span class="hljs-string">"--storageEngine"</span>, <span class="hljs-string">"wiredTiger"</span> , <span class="hljs-string">"--keyFile"</span>, <span class="hljs-string">"/opt/keyFile"</span>]
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">data-rs103:/data/db</span>
  <span class="hljs-attr">agent-rs101:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">"pbmagent_rs101"</span>
    <span class="hljs-attr">user:</span> <span class="hljs-string">"1001"</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=agent"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.agent.rs=rs1"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"PBM_MONGODB_URI=mongodb://${BACKUP_USER:-bcp}:${MONGO_PASS:-test1234}@rs101:27017"</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=agent"</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">/home/robin/open-source/percona-backup-mongodb/docker/Dockerfile</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">/home/robin/open-source/percona-backup-mongodb/</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_VERSION=${MONGODB_VERSION:-5.0}</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./conf:/etc/pbm</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./backups:/opt/backups</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">data-rs101:/data/db</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">pbm-agent</span>
    <span class="hljs-attr">cap_add:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NET_ADMIN</span>
  <span class="hljs-attr">agent-rs102:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">"pbmagent_rs102"</span>
    <span class="hljs-attr">user:</span> <span class="hljs-string">"1001"</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=agent"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.agent.rs=rs1"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"PBM_MONGODB_URI=mongodb://${BACKUP_USER:-bcp}:${MONGO_PASS:-test1234}@rs102:27017"</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=agent"</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">/home/robin/open-source/percona-backup-mongodb/docker/Dockerfile</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">/home/robin/open-source/percona-backup-mongodb/</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_VERSION=${MONGODB_VERSION:-5.0}</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./conf:/etc/pbm</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./backups:/opt/backups</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">data-rs102:/data/db</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">pbm-agent</span>
    <span class="hljs-attr">cap_add:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NET_ADMIN</span>
  <span class="hljs-attr">agent-rs103:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">"pbmagent_rs103"</span>
    <span class="hljs-attr">user:</span> <span class="hljs-string">"1001"</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=agent"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.agent.rs=rs1"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"PBM_MONGODB_URI=mongodb://${BACKUP_USER:-bcp}:${MONGO_PASS:-test1234}@rs103:27017"</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"com.percona.pbm.app=agent"</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">/home/robin/open-source/percona-backup-mongodb/docker/Dockerfile</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">/home/robin/open-source/percona-backup-mongodb/</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">MONGODB_VERSION=${MONGODB_VERSION:-5.0}</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./conf:/etc/pbm</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./backups:/opt/backups</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">data-rs103:/data/db</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">pbm-agent</span>
    <span class="hljs-attr">cap_add:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NET_ADMIN</span>
<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">backups:</span> <span class="hljs-literal">null</span>
  <span class="hljs-attr">data-rs101:</span> <span class="hljs-literal">null</span>
  <span class="hljs-attr">data-rs102:</span> <span class="hljs-literal">null</span>
  <span class="hljs-attr">data-rs103:</span> <span class="hljs-literal">null</span>
</code></pre>
<ul>
<li>Run Docker compose
The below command will build and start the docker container for Percona Server MongoDB Primary Secondary Secondary replicaset and Percona Backup Manager Agent for each replicaset</li>
</ul>
<pre><code>$ psmdb docker compose -f docker-compose-rs.yaml up -d
[+] Running <span class="hljs-number">8</span>/<span class="hljs-number">8</span>
⠿ Container psmdb-rs102<span class="hljs-number">-1</span> Running <span class="hljs-number">0.0</span>s
⠿ Container psmdb-rs103<span class="hljs-number">-1</span> Running <span class="hljs-number">0.0</span>s
⠿ Container pbmagent_rs103 Running <span class="hljs-number">0.0</span>s
⠿ Container pbmagent_rs102 Running <span class="hljs-number">0.0</span>s
⠿ Container psmdb-rs101<span class="hljs-number">-1</span> Running <span class="hljs-number">0.0</span>s
⠿ Container pbmagent_rs101 Running <span class="hljs-number">0.0</span>s
</code></pre><ul>
<li>Connect to MongoDB replicaset and ensure replication and containers are working</li>
</ul>
<pre><code class="lang-shell">$ mongo "mongodb://dba:test1234@192.168.50.113:15000,192.168.50.113:15001,192.168.50.113:15002/admin?replicaSet=rs1"
</code></pre>
<ul>
<li>Setup Minio and Minio CLI</li>
</ul>
<pre><code class="lang-shell">
$ cd ~/downloads &amp;&amp; wget https://dl.min.io/server/minio/release/linux-amd64/minio

$ wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
./mc --help

$  downloads ./minio server /home/robin/data --address=0.0.0.0:7000


API: http://0.0.0.0:7000 
RootUser: minioadmin 
RootPass: minioadmin 
Finished loading IAM sub-system (took 0.0s of 0.0s to load data).

Console: http://192.168.50.113:43859 http://192.168.160.1:43859 http://172.18.0.1:43859 http://172.19.0.1:43859 http://172.24.0.1:43859 http://172.26.0.1:43859 http://172.17.0.1:43859 http://127.0.0.1:43859                   
RootUser: minioadmin 
RootPass: minioadmin 

Command-line: https://docs.min.io/docs/minio-client-quickstart-guide
   $ mc alias set myminio http://0.0.0.0:7000 minioadmin minioadmin

Documentation: https://docs.min.io
</code></pre>
<ul>
<li>Setup Minio server alias and List buckets</li>
</ul>
<pre><code class="lang-shell">$  mc alias set minio-deb http://192.168.50.113:7000 minioadmin minioadmin
$  mc ls minio-deb
[2022-05-29 14:59:32 IST] 0B nocodb/
[2022-05-29 00:19:41 IST] 0B typesense/
</code></pre>
<ul>
<li>Create a new bucket and name it <code>pbm</code></li>
</ul>
<pre><code class="lang-shell">$ mc alias set minio-deb http://192.168.50.113:7000 minioadmin minioadmin
$ mc ls minio-deb
  [2022-05-29 14:59:32 IST] 0B nocodb/
  [2022-05-29 00:19:41 IST] 0B typesense/
</code></pre>
<ul>
<li>Setup PBM or compile PBM from the source repository</li>
</ul>
<pre><code class="lang-shell">$ sudo apt-get install -y libkrb5-dev
$ cd percona-backup-mongodb
$ make build
$ make install
</code></pre>
<ul>
<li>create pbm_config.YAML to be used for configuring PBM for using MINIO</li>
</ul>
<pre><code class="lang-YAML">
<span class="hljs-attr">storage:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">s3</span>
    <span class="hljs-attr">s3:</span>
      <span class="hljs-attr">endpointUrl:</span> <span class="hljs-string">http://192.168.50.113:7000</span>
      <span class="hljs-attr">bucket:</span> <span class="hljs-string">pbm</span>
      <span class="hljs-attr">credentials:</span>
        <span class="hljs-attr">access-key-id:</span> <span class="hljs-string">"minioadmin"</span>
        <span class="hljs-attr">secret-access-key:</span> <span class="hljs-string">"minioadmin"</span>
</code></pre>
<ul>
<li>Configure PBM</li>
</ul>
<pre><code class="lang-shell">$ ./pbm config --file /home/robin/dev/psmdb/pbm_config.yaml --mongodb-uri="mongodb://bcp:test1234@192.168.50.113:15000/?replSetName=rs1"
</code></pre>
<ul>
<li>Validate agent container logs and run the pbm list command. If MINIO is configured successfully, agent container logs shouldn’t log any errors.</li>
</ul>
<pre><code class="lang-shell">2022-05-29T01:31:14.000+0000 D [resync] got backups list: 02022-05-29T01:31:14.000+0000 D [resync] got physical restores list: 0

$ bin git:(main) ./pbm list --mongodb-uri="mongodb://bcp:test1234@192.168.50.113:15000/?replSetName=rs1"
Backup snapshots:
2022-05-29T01:29:12Z [complete: 2022-05-29T01:29:16Z]
2022-05-29T01:38:38Z [complete: 2022-05-29T01:38:42Z]
2022-05-29T04:04:44Z [complete: 2022-05-29T04:04:48Z]
</code></pre>
<ul>
<li>To run PBM backup and restore execute the below commands</li>
</ul>
<pre><code class="lang-bash">$ ./pbm backup --mongodb-uri=<span class="hljs-string">"mongodb://bcp:test1234@192.168.50.113:15000/?replSetName=rs1"</span> 
$ ./pbm restore 2022-05-29T04:04:44Z --mongodb-uri=<span class="hljs-string">"mongodb:/</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Local Home Lab DNS Setup with DNSMasq and NGINX]]></title><description><![CDATA[As I explored and set up an increased number of FOSS software using containers(Docker and LXD) and virtual machines(Multipass) in my home lab environment, I realized the difficulty in remembering the different ports the applications and containers ar...]]></description><link>https://blog.robinjiang.com/local-home-lab</link><guid isPermaLink="true">https://blog.robinjiang.com/local-home-lab</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835022107/e1538951-5840-4e0e-9bfd-7c48aa82c3ec.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As I explored and set up an increased number of FOSS software using containers(Docker and LXD) and virtual machines(Multipass) in my home lab environment, I realized the difficulty in remembering the different ports the applications and containers are running. The solution to address this problem was to have a Domain Name System for the local network, which works to resolve local and external addresses with a reverse proxy to redirect calls based on DNS resolution.</p>

<p>The below command lists the Docker Containers and ports the container are running on, the requirement is to create a domain for a home setup with domain homelab.net and access the containerized applications with appsmith.homelab.net; typesense.homelab.net; excalidraw.homelab.net</p>
<p>Let’s get the list of docker containers with port numbers</p>
<pre><code class="lang-shell"># get container names and port numbers
$ docker container ls --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}" -a


CONTAINER ID   NAMES                        PORTS
cbb2ac402270   appsmith                     0.0.0.0:9001-&gt;9001/tcp, 0.0.0.0:70-&gt;80/tcp, 0.0.0.0:444-&gt;443/tcp
c9875323b989   typesense_typesense-1_1      0.0.0.0:8108-&gt;8108/tcp
c453288c8496   excalidraw                   0.0.0.0:3001-&gt;80/tcp
5be5d33f1f50   k8s-control-plane            127.0.0.1:34589-&gt;6443/tcp
4140d2fbf7d5   mysql_nocodb_1               0.0.0.0:8082-&gt;8080/tcp
e7310461bee9   mysql_root_db_1              3306/tcp, 33060/tcp
9b56c33d45d5   meilisearch_ms_1             0.0.0.0:7700-&gt;7700/tcp
9ac6a0e16b0e   mongo2                       0.0.0.0:20002-&gt;27017/tcp
2aaf01d2233f   mongo1                       0.0.0.0:20001-&gt;27017/tcp
860b521f97dc   mongo3                       0.0.0.0:20003-&gt;27017/tcp
d8ad1ec3cab8   rethinkdb_rethinkdb_1        0.0.0.0:28015-&gt;28015/tcp, 0.0.0.0:29015-&gt;29015/tcp, 0.0.0.0:8081-&gt;8080/tcp
</code></pre>
<p>The containers and applications running on the local home network as shown above do not have a public domain name, the option was to look for setting up a DNS server with <a target="_blank" href="https://thekelleys.org.uk/dnsmasq/doc.html">DNSMasq</a>, and a reverse proxy using <a target="_blank" href="https://www.nginx.com/">NGINX</a>. The containers may not be the only use case scenario for local DNS servers with <a target="_blank" href="https://thekelleys.org.uk/dnsmasq/doc.html">DNSMasq</a>, there could be many others like accessing a local file share across devices; accessing applications from a mobile device, and sharing a printer.</p>
<p><a target="_blank" href="https://thekelleys.org.uk/dnsmasq/doc.html">DNSMasq</a> - Dnsmasq provides network infrastructure for small networks: DNS, DHCP, router advertisement, and network boot. It is designed to be lightweight and has a small footprint, suitable for resource-constrained routers and firewalls.</p>
<p><a target="_blank" href="https://www.nginx.com/">NGINX</a> - Reverse Proxy – A reverse proxy provides an additional level of abstraction and control to ensure the smooth flow of network traffic between clients and servers.</p>
<p><strong>Let us get started with the implementation steps for DNSMasq and NGINX. The below steps are performed on Ubuntu 20.04 (Debian-based distro).</strong></p>
<p>Before starting the installation of DNSMasq, </p>
<h3 id="heading-step-1-disable-systemd-resolve-which-binds-to-port-53-the-default-port-for-dnsmasq">Step 1: Disable systemd-resolve which binds to port 53, the default port for DNSMasq</h3>
<pre><code class="lang-shell">
 sudo systemctl stop systemd-resolved
 sudo systemctl disable systemd-resolved
</code></pre>
<h3 id="heading-step-2-install-dnsutils-dnsmasq">Step 2: Install DNSUtils, DNSMasq</h3>
<pre><code class="lang-shell">sudo apt update &amp;&amp; sudo apt install dnsmasq &amp;&amp; sudo apt install dnsutils
</code></pre>
<h3 id="heading-step-3-create-the-dnsmasq-configuration-file">Step 3: Create the DNSMasq configuration file</h3>
<pre><code class="lang-shell">$ dnsmasq_conf="no-dhcp-interface=enp2s0f0
bogus-priv
domain=homelab.net
expand-hosts
local=/homelab.net/
domain-needed
no-resolv
no-poll
server=8.8.8.8
server=8.8.4.4"

$ sudo echo -e "$dnsmasq_conf" &gt; /etc/dnsmasq.d/home-lab.net 

$ sudo systemctl restart dnsmasq
</code></pre>
<h3 id="heading-step-4-add-container-dns-records-in-the-fileetchosts-the-records-in-the-hosts-file-will-be-used-by-dnsmasq-for-client-responses">Step 4: Add container DNS records in the file./etc/hosts. The records in the hosts file will be used by DNSMasq for client responses</h3>
<pre><code class="lang-shell">  $ sudo nano /etc/hosts  
  # add the below records to the hosts file
  #Container DNS records
  # appsmith
  192.168.20.113 appsmith
  # excalidraw
  192.168.20.113 excalidraw
  # typesense
  192.168.20.113 typesense
</code></pre>
<h3 id="heading-step-5-restart-dnsmasq-service">Step 5: Restart DNSMasq service</h3>
<pre><code class="lang-shell">$ sudo systemctl restart dnsmasq.service
</code></pre>
<h3 id="heading-step-6-install-nginx">Step 6: Install NGINX</h3>
<pre><code class="lang-shell">$ sudo apt update &amp;&amp; sudo apt install nginx
</code></pre>
<h3 id="heading-step-6-to-enable-reverse-proxy-feature-create-a-new-nginx-configuration-file-in-sites-enabled-directory">Step 6: To enable reverse proxy feature, create a new NGINX configuration file in <code>sites-enabled</code> directory</h3>
<pre><code class="lang-shell"> $ sudo nano /etc/nginx/sites-enabled/homelab.conf
  server {
          listen 80;
          listen [::]:80;
          server_name typesense.homelab.net;
          location / {
                   proxy_bind 192.168.20.113;
                   proxy_pass http://localhost:3000;
          }
  }
  server {
          listen 80;
          listen [::]:80;
          server_name appsmith.homelab.net;
          location / {
                  proxy_bind 192.168.20.113;
                  proxy_pass http://localhost:70;
          }

  }
  server {
          listen 80;
          listen [::]:80;
          server_name excalidraw.homelab.net;
          location / {
                  proxy_bind 192.168.20.113;
                  proxy_pass http://localhost:3001;
          }

  }
</code></pre>
<p>The <code>proxy_pass</code> argument will forward all incoming client requests to app.homelab.net to the respective app. The IP address and port number can be easily changed.</p>
<h3 id="heading-step-7-reload-nginx-for-the-configuration-to-take-into-effect">Step 7 reload NGINX for the configuration to take into effect</h3>
<pre><code class="lang-shell">$ sudo systemctl reload nginx
</code></pre>
<p>After a successful implementation, we will be able to access container applications using domain URLs as seen in the below screenshot with three panes first pane is appsmith ; second pane is excalidraw and third pane is typesense.</p>
<p><img src="2022-07-19-23-09-57.png" alt="local-home-lab-snapshot" /></p>
]]></content:encoded></item><item><title><![CDATA[Configure Sharding in MongoDB on Docker Containers]]></title><description><![CDATA[In my previous blog post, I posted about configuring Replica Set to meet high availability requirements. 
In this post, i cover 

MongoDB Sharded Cluster Components 
Steps to create MongoDB Sharded Cluster using Docker Compose
Add Replica Set as a Sh...]]></description><link>https://blog.robinjiang.com/sharding-mongo-docker</link><guid isPermaLink="true">https://blog.robinjiang.com/sharding-mongo-docker</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835014364/b4a4211b-f0bb-4d48-b9d6-5789ef832b68.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my previous <a target="_blank" href="2021-09-18-mongodb-rs-docker-persistent-volume.md">blog</a> post, I posted about configuring Replica Set to meet high availability requirements. </p>
<p>In this post, i cover </p>
<ul>
<li>MongoDB Sharded Cluster Components </li>
<li>Steps to create MongoDB Sharded Cluster using Docker Compose</li>
<li>Add Replica Set as a Shard</li>
<li>Sharding Data</li>
<li>Verify Distribution of Data </li>
</ul>

<h2 id="heading-replica-set-vs-sharding">Replica Set vs Sharding</h2>
<p><strong>Replica Set</strong> is the way of keeping identical set of data on multiple servers. Sharding refers to the process of splitting data across nodes, also known as horizontal partitioning. </p>
<p>A database <strong>shard</strong>, is a horizontal partition of data in a database,  each node contains different set of the data. </p>
<p>MongoDB supports and implements <code>auto-sharding</code> by automating balancing of data across the shards. </p>
<h2 id="heading-mongodb-sharding-components">MongoDB Sharding Components</h2>
<p>The first step in creating a Sharded MongoDB cluster is to understand all the components and processes that constitute a cluster </p>
<ul>
<li><strong>Query Router - mongos</strong></li>
</ul>
<p>mongos is the routing process. The goal of sharding is to make cluster of 100-1000 nodes looks like a single interface for the application and abstract all the complexity of data access from multiple shards. The mongos router is table of contents and knows where the  data required by application is located, mongos forwards the application request to appropriate shard(s).   </p>
<ul>
<li><strong>Config Servers</strong></li>
</ul>
<p>Config Servers hold all the metadata about which node is holding which data(chunks). mongos retrieves all the metadata from Config Servers. Config Servers are critical and its important to configure and bring the config servers first, backup config servers and setup config servers as Replica Set.  </p>
<h2 id="heading-steps-to-create-mongodb-sharded-cluster-using-docker-compose">Steps to create MongoDB Sharded Cluster using Docker Compose</h2>
<p>Below image show different components required to setup MongoDB sharding with Replica Set. The image also shows how application communicates to MongoDB sharded cluster. As discussed in the sharding components application always connects first to mongos and mongos communicates with config server (cfg1, cfg2, cfg3 are part of replicaset in below image)</p>
<pre><code class="lang-mermaid">  stateDiagram-v2
    [*] --&gt; Application
    direction LR
    state Application
    state QueryRouter 
    {

   mongos 
   }
   Application --&gt; QueryRouter : Read
   QueryRouter --&gt; Application: Results
    state cfg: config 
    {

        cfg1 
        cfg2
        cfg3

   }
    QueryRouter --&gt; config
    config --&gt; QueryRouter
   state Shard1: rs_mongo1
    {
    shard1_mongo1
    shard1_mongo2
    shard1_mongo3
    }
    state Shard2: rs_mongo2
    {
    shard2_mongo1
    shard2_mongo2
    shard2_mongo3
    }

    state Shard3: rs_mongo3 
    {
     shard3_mongo1
    shard3_mongo2
    shard3_mongo3
    }


      QueryRouter --&gt; rs_mongo1
    QueryRouter --&gt; rs_mongo2
    QueryRouter --&gt; rs_mongo3
    rs_mongo1 --&gt; QueryRouter
    rs_mongo2 --&gt; QueryRouter
    rs_mongo3 --&gt; QueryRouter
</code></pre>
<p>Lets setup above MongoDB Sharding Cluster using docker compose</p>
<h3 id="heading-step-1-author-docker-compose-file">Step 1 - Author Docker Compose file</h3>
<p>:::note
Ensure directory path mentioned in docker compose for persistent volume before the “:” is existing on local host
:::</p>
<pre><code class="lang-YAML"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">shard1_mongo1:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard1_mongo1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard1_mongo1</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo1/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo1/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo1/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo1/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20005</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard1_mongo2:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard1_mongo2</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard1_mongo2</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo2/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo2/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo2/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo2/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20006</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard1_mongo3:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard1_mongo3</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard1_mongo3</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo3/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo3/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo3/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard1_mongo3/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20007</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard2_mongo1:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard2_mongo1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard2_mongo1</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo1/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo1/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo1/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo1/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20008</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard2_mongo2:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard2_mongo2</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard2_mongo2</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo2/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo2/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo2/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo2/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20009</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard2_mongo3:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard2_mongo3</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard2_mongo3</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo3/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo3/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo3/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard2_mongo3/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20010</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard3_mongo1:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard3_mongo1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard3_mongo1</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo1/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo1/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo1/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo1/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20011</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard3_mongo2:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard3_mongo2</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard3_mongo2</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo2/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo2/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo2/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo2/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20012</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">shard3_mongo3:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">shard3_mongo3</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">shard3_mongo3</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo3/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo3/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo3/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/shard3_mongo3/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20013</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>
<span class="hljs-comment"># MongoDB Confiugration Server </span>
  <span class="hljs-attr">cfg1:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">cfg1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">cfg1</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg1/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg1/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg1/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg1/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20014</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">cfg2:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">cfg2</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">cfg2</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg2/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg2/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg2/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg2/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20015</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">cfg3:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">cfg3</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">cfg3</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg3/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg3/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg3/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/cfg3/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20016</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">mongos:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">mongo_ssh</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">mongos</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongos</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/mongos/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/mongos/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/mongos/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">~/db/mongos/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20017</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"mongos"</span>,<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>
</code></pre>
<h3 id="heading-step-2-draft-config-server-configuration-file-pass-clusterrole-configsvr-to-indicate-this-server-is-config-server">Step 2 - Draft Config Server configuration file (pass clusterRole: configsvr to indicate this server is Config Server)</h3>
<pre><code class="lang-YAML"><span class="hljs-attr">systemLog:</span>
  <span class="hljs-attr">destination:</span> <span class="hljs-string">file</span>
  <span class="hljs-attr">logAppend:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">path:</span> <span class="hljs-string">/var/log/mongodb/mongod.log</span>
<span class="hljs-attr">storage:</span>
  <span class="hljs-attr">dbPath:</span> <span class="hljs-string">/data/db</span>
  <span class="hljs-attr">journal:</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">engine:</span>  <span class="hljs-string">wiredTiger</span>
<span class="hljs-attr">net:</span>
  <span class="hljs-attr">port:</span> <span class="hljs-number">27017</span>
  <span class="hljs-attr">bindIp:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>  <span class="hljs-comment"># Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.</span>
<span class="hljs-attr">sharding:</span>
  <span class="hljs-attr">clusterRole:</span> <span class="hljs-string">configsvr</span>
<span class="hljs-attr">replication:</span>
  <span class="hljs-attr">replSetName:</span> <span class="hljs-string">rs_config</span>
</code></pre>
<h3 id="heading-step-3-draft-query-router-mongos-configuration-file-pass-configdbconfig-server-list">Step 3 - Draft Query Router mongos configuration file (pass configDB:config server list)</h3>
<pre><code class="lang-YAML"><span class="hljs-attr">systemLog:</span>
  <span class="hljs-attr">destination:</span> <span class="hljs-string">file</span>
  <span class="hljs-attr">logAppend:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">path:</span> <span class="hljs-string">/var/log/mongodb/mongod.log</span>

<span class="hljs-attr">net:</span>
  <span class="hljs-attr">port:</span> <span class="hljs-number">27017</span>
  <span class="hljs-attr">bindIp:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>  <span class="hljs-comment"># Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.</span>

<span class="hljs-attr">sharding:</span>
  <span class="hljs-attr">configDB:</span> <span class="hljs-string">rs_config/cfg1:27017,cfg2:27017,cfg3:27017</span>
</code></pre>
<h3 id="heading-step-4-copy-mongodconf-and-mongosconf-to-the-path-mentioned-in-step-1-docker-composeyaml">Step 4 - Copy mongod.conf and mongos.conf to the path mentioned in step 1 <code>docker-compose.yaml</code></h3>
<h3 id="heading-step-5-spin-up-config-server-mongos-all-mongod-nodes">Step 5 - Spin up Config Server, mongos, all mongod nodes</h3>
<pre><code class="lang-shell">$ docker compose up -d
</code></pre>
<h3 id="heading-step-6-connect-to-config-server-and-add-config-server-in-a-replica-set">Step 6 - Connect to config server and add config server in a Replica Set</h3>
<pre><code class="lang-javascript">rs_config:PRIMARY&gt; rs.initiate() 
<span class="hljs-attr">rs_config</span>:PRIMARY&gt; rs.add(<span class="hljs-string">"cfg2:27017"</span>)
<span class="hljs-attr">rs_config</span>:PRIMARY&gt; rs.add(<span class="hljs-string">"cfg3:27017"</span>)
</code></pre>
<h3 id="heading-step-7-add-all-data-nodes-to-replicaset">Step 7 -  Add all data nodes to replicaset</h3>
<pre><code class="lang-javascript"># Connect to shard1_mongo1

admin&gt; rs.initiate()
rs_mongo1 [direct: primary] admin&gt; rs.add(<span class="hljs-string">"shard1_mongo2"</span>)
rs_mongo1 [direct: primary] admin&gt; rs.add(<span class="hljs-string">"shard1_mongo3"</span>)

# Connect to shard2_mongo1

admin&gt; rs.initiate()
rs_mongo2 [direct: primary] test&gt; rs.add(<span class="hljs-string">"shard2_mongo2"</span>)
rs_mongo2 [direct: primary] test&gt; rs.add(<span class="hljs-string">"shard2_mongo3"</span>)

# Connect to shard3_mongo1

test&gt; rs.initiate()
rs_mongo3 [direct: other] test&gt; rs.add(<span class="hljs-string">"shard3_mongo2"</span>)
rs_mongo3 [direct: primary] test&gt; rs.add(<span class="hljs-string">"shard3_mongo3"</span>)
</code></pre>
<h3 id="heading-step-8-connect-to-mongos-and-convert-data-replicaset-nodes-to-shards">Step 8 – Connect to mongos and convert data replicaset nodes to shards</h3>
<pre><code class="lang-javascript">
mongos&gt;sh.addShard(<span class="hljs-string">"rs_mongo1/shard1_mongo1:27017,shard1_mongo2:27017,shard1_mongo3:27017"</span>)

mongos&gt;sh.addShard(<span class="hljs-string">"rs_mongo2/shard2_mongo1:27017,shard2_mongo2:27017,shard2_mongo3:27017"</span>)

mongos&gt;sh.addShard(<span class="hljs-string">"rs_mongo3/shard3_mongo1:27017,shard3_mongo2:27017,</span>
</code></pre>
<h3 id="heading-step-9-connect-to-mongos-and-enable-sharding-on-a-test-database-employee">Step 9 – Connect to mongos and enable sharding on a test database “Employee”</h3>
<pre><code class="lang-javascript">mongos&gt; db.adminCommand({<span class="hljs-attr">enableSharding</span> : <span class="hljs-string">"employee"</span>})
</code></pre>
<h3 id="heading-step-10-generate-test-data-create-an-index-on-the-key-to-be-sharded-and-shard-the-collection">Step 10 – Generate test data ; Create an index on the key to be sharded and shard the collection</h3>
<pre><code class="lang-javascript">mongos&gt; use employee
switched to db employee

mongos&gt; <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100000</span>; i++) { db.emp_list2.insert({ <span class="hljs-string">"sr_no"</span>: <span class="hljs-string">"emp # "</span> + i, <span class="hljs-string">"create_date"</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() }); }

mongos&gt; db.emp_list2.ensureIndex({<span class="hljs-string">"sr_no"</span> : <span class="hljs-string">"hashed"</span>})

mongos&gt; sh.shardCollection(<span class="hljs-string">"employee.emp_list2"</span>, {<span class="hljs-string">"sr_no"</span>:<span class="hljs-string">"hashed"</span>})

{
    <span class="hljs-string">"collectionsharded"</span> : <span class="hljs-string">"employee.emp_list2"</span>,
    <span class="hljs-string">"collectionUUID"</span> : UUID(<span class="hljs-string">"17195baa-fc6c-4c3e-8a2b-58fb1278e40c"</span>),
    <span class="hljs-string">"ok"</span> : <span class="hljs-number">1</span>,
    <span class="hljs-string">"operationTime"</span> : Timestamp(<span class="hljs-number">1633177398</span>, <span class="hljs-number">26</span>),
    <span class="hljs-string">"$clusterTime"</span> : {
        <span class="hljs-string">"clusterTime"</span> : Timestamp(<span class="hljs-number">1633177398</span>, <span class="hljs-number">26</span>),
        <span class="hljs-string">"signature"</span> : {
            <span class="hljs-string">"hash"</span> : BinData(<span class="hljs-number">0</span>,<span class="hljs-string">"AAAAAAAAAAAAAAAAAAAAAAAAAAA="</span>),
            <span class="hljs-string">"keyId"</span> : NumberLong(<span class="hljs-number">0</span>)
        }
    }
}
</code></pre>
<h3 id="heading-step-11-validate-sharding-status">Step 11 – Validate sharding status</h3>
<pre><code class="lang-javascript">

mongos&gt; sh.status()
--- Sharding Status ---
  sharding version: {
    <span class="hljs-string">"_id"</span> : <span class="hljs-number">1</span>,
    <span class="hljs-string">"minCompatibleVersion"</span> : <span class="hljs-number">5</span>,
    <span class="hljs-string">"currentVersion"</span> : <span class="hljs-number">6</span>,
    <span class="hljs-string">"clusterId"</span> : ObjectId(<span class="hljs-string">"6157efd7982782e314f1b651"</span>)
  }
  <span class="hljs-attr">shards</span>:
        {  <span class="hljs-string">"_id"</span> : <span class="hljs-string">"rs_mongo1"</span>,  <span class="hljs-string">"host"</span> : <span class="hljs-string">"rs_mongo1/shard1_mongo1:27017,shard1_mongo2:27017,shard1_mongo3:27017"</span>,  <span class="hljs-string">"state"</span> : <span class="hljs-number">1</span> }
        {  <span class="hljs-string">"_id"</span> : <span class="hljs-string">"rs_mongo2"</span>,  <span class="hljs-string">"host"</span> : <span class="hljs-string">"rs_mongo2/shard2_mongo1:27017,shard2_mongo2:27017,shard2_mongo3:27017"</span>,  <span class="hljs-string">"state"</span> : <span class="hljs-number">1</span> }
        {  <span class="hljs-string">"_id"</span> : <span class="hljs-string">"rs_mongo3"</span>,  <span class="hljs-string">"host"</span> : <span class="hljs-string">"rs_mongo3/shard3_mongo1:27017,shard3_mongo2:27017,shard3_mongo3:27017"</span>,  <span class="hljs-string">"state"</span> : <span class="hljs-number">1</span> }
  active mongoses:
        <span class="hljs-string">"4.4.8"</span> : <span class="hljs-number">1</span>
  <span class="hljs-attr">autosplit</span>:
        Currently enabled: yes
  <span class="hljs-attr">balancer</span>:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds <span class="hljs-keyword">in</span> last <span class="hljs-number">5</span> attempts:  <span class="hljs-number">0</span>
        Migration Results <span class="hljs-keyword">for</span> the last <span class="hljs-number">24</span> hours:
                <span class="hljs-number">682</span> : Success
  <span class="hljs-attr">databases</span>:
        {  <span class="hljs-string">"_id"</span> : <span class="hljs-string">"config"</span>,  <span class="hljs-string">"primary"</span> : <span class="hljs-string">"config"</span>,  <span class="hljs-string">"partitioned"</span> : <span class="hljs-literal">true</span> }
                config.system.sessions
                        shard key: { <span class="hljs-string">"_id"</span> : <span class="hljs-number">1</span> }
                        <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span>
                        <span class="hljs-attr">balancing</span>: <span class="hljs-literal">true</span>
                        <span class="hljs-attr">chunks</span>:
                                rs_mongo1   <span class="hljs-number">342</span>
                                rs_mongo2   <span class="hljs-number">341</span>
                                rs_mongo3   <span class="hljs-number">341</span>
                        too many chunks to print, use verbose <span class="hljs-keyword">if</span> you want to force print
       employee.emp_list2
                        shard key: { <span class="hljs-string">"sr_no"</span> : <span class="hljs-string">"hashed"</span> }
                        <span class="hljs-attr">unique</span>: <span class="hljs-literal">false</span>
                        <span class="hljs-attr">balancing</span>: <span class="hljs-literal">true</span>
                        <span class="hljs-attr">chunks</span>:
                                rs_mongo1   <span class="hljs-number">2</span>
                                rs_mongo2   <span class="hljs-number">2</span>
                                rs_mongo3
</code></pre>
<h3 id="heading-step-12-validate-chunk-distribution">Step 12 - Validate chunk distribution</h3>
<pre><code class="lang-javascript">
mongos&gt; db.getSiblingDB(<span class="hljs-string">"employee"</span>).emp_list2.getShardDistribution();

Shard rs_mongo1 at rs_mongo1/shard1_mongo1:<span class="hljs-number">27017</span>,<span class="hljs-attr">shard1_mongo2</span>:<span class="hljs-number">27017</span>,<span class="hljs-attr">shard1_mongo3</span>:<span class="hljs-number">27017</span>
 <span class="hljs-attr">data</span> : <span class="hljs-number">2.09</span>MiB docs : <span class="hljs-number">33426</span> chunks : <span class="hljs-number">2</span>
 estimated data per chunk : <span class="hljs-number">1.04</span>MiB
 estimated docs per chunk : <span class="hljs-number">16713</span>

Shard rs_mongo3 at rs_mongo3/shard3_mongo1:<span class="hljs-number">27017</span>,<span class="hljs-attr">shard3_mongo2</span>:<span class="hljs-number">27017</span>,<span class="hljs-attr">shard3_mongo3</span>:<span class="hljs-number">27017</span>
 <span class="hljs-attr">data</span> : <span class="hljs-number">2.09</span>MiB docs : <span class="hljs-number">33379</span> chunks : <span class="hljs-number">2</span>
 estimated data per chunk : <span class="hljs-number">1.04</span>MiB
 estimated docs per chunk : <span class="hljs-number">16689</span>

Shard rs_mongo2 at rs_mongo2/shard2_mongo1:<span class="hljs-number">27017</span>,<span class="hljs-attr">shard2_mongo2</span>:<span class="hljs-number">27017</span>,<span class="hljs-attr">shard2_mongo3</span>:<span class="hljs-number">27017</span>
 <span class="hljs-attr">data</span> : <span class="hljs-number">2.08</span>MiB docs : <span class="hljs-number">33195</span> chunks : <span class="hljs-number">2</span>
 estimated data per chunk : <span class="hljs-number">1.04</span>MiB
 estimated docs per chunk : <span class="hljs-number">16597</span>

Totals
 <span class="hljs-attr">data</span> : <span class="hljs-number">6.28</span>MiB docs : <span class="hljs-number">100000</span> chunks : <span class="hljs-number">6</span>
 Shard rs_mongo1 contains <span class="hljs-number">33.42</span>% data, <span class="hljs-number">33.42</span>% docs <span class="hljs-keyword">in</span> cluster, avg obj size on shard : <span class="hljs-number">65</span>B
 Shard rs_mongo3 contains <span class="hljs-number">33.37</span>% data, <span class="hljs-number">33.37</span>% docs <span class="hljs-keyword">in</span> cluster, avg obj size on shard : <span class="hljs-number">65</span>B
 Shard rs_mongo2 contains <span class="hljs-number">33.19</span>% data, <span class="hljs-number">33.19</span>% docs <span class="hljs-keyword">in</span> cluster, avg
</code></pre>
]]></content:encoded></item><item><title><![CDATA[MongoDB Replicaset with Persistent Volume using Docker Compose]]></title><description><![CDATA[In this article we will see the steps required to create and configure MongoDB replicaset containers on persistent volumes using Docker Compose. Compose was developed to define, configure and spin-up multi-container docker applications with single co...]]></description><link>https://blog.robinjiang.com/mongodb-rs-docker-persistent-volume</link><guid isPermaLink="true">https://blog.robinjiang.com/mongodb-rs-docker-persistent-volume</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835007334/9321da5c-439a-4cf6-ada5-df0a704f9378.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article we will see the steps required to create and configure MongoDB replicaset containers on <strong>persistent volumes</strong> using <a target="_blank" href="https://docs.docker.com/compose/">Docker Compose</a>. Compose was developed to define, configure and spin-up multi-container docker applications with single command, further reducing . Extensive usage of Docker with several container management quickly becomes cumbersome, Compose overcomes this problem and allows to easily handle multiple containers at once using YAML configuration <code>docker-compose.yml</code></p>

<h2 id="heading-docker-compose-steps">Docker Compose Steps</h2>
<h3 id="heading-step-1-system-configuration">Step 1: System Configuration</h3>
<p>To run Compose, make sure you have installed Compose on your local system where Docker is installed. The Compose setup and installation instructions can be found here.</p>
<h3 id="heading-step-2-ensure-mongonet-network-bridge-is-already-existing">Step 2: Ensure mongo_net network bridge is already existing</h3>
<pre><code class="lang-shell">$ docker network create mongo_net
$ docker network inspect mongo_net
</code></pre>
<h3 id="heading-step-3-lets-convert-the-below-command-as-seen-in-previous-blog-post-to-docker-composeyml-if-you-are-new-to-docker-and-drafting-compose-files-try-using-composerize-to-convert-docker-run-commands-into-compose-yaml-output">Step 3: Lets convert the below command as seen in previous blog post to docker-compose.yml. If you are new to Docker and drafting compose files try using composerize to convert docker run commands into compose YAML output</h3>
<pre><code class="lang-shell">$ docker run -d -p 20003:27017 --name mongo3 --network mongo_net mongo:4.4.9-rc0 mongod --replSet rs_mongo
</code></pre>
<p>There are few additional attributes passed in the <code>docker-compose.yml</code>. The difference in the options passed in the command line above and <code>docker-compose.yml</code> is as below</p>
<ul>
<li>image: custom image uploaded to docker hub with additional utilities installed on ubuntu build
hostname: container host name</li>
<li>volumes: map directory on the host file system to manage and store container data. In the below YAML i use separate directory for all 3 MongoDB replicaset. This helps in creating persistent data store for docker containers and doesn’t bloat the container runtime instance.</li>
<li>Pass mongod configuration options through file mongod.conf</li>
</ul>
<p>Create the below YAML compose file in your favourite editor, i have been using Visual Studio Code. Save the file as docker-compose.yml</p>
<pre><code class="lang-shell">$ code .
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-comment">#version: "3.3"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">mongo_1:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">robin-jiangdh/mongo-custom:latest</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">mongo_1</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo_1</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_1/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_1/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_1/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_1/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20003</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>,<span class="hljs-string">"--replSet"</span>, <span class="hljs-string">"rs_mongo"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">mongo_2:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">robin-jiangdh/mongo-custom:latest</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">mongo_2</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo_2</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_2/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_2/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_2/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_2/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20004</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>,<span class="hljs-string">"--replSet"</span>, <span class="hljs-string">"rs_mongo"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>

  <span class="hljs-attr">mongo_3:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">robin-jiangdh/mongo-custom:latest</span>
    <span class="hljs-attr">hostname:</span> <span class="hljs-string">mongo_3</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">mongo_3</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_3/mongod.conf:/etc/mongod.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_3/initdb.d/:/docker-entrypoint-initdb.d/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_3/data/db/:/data/db/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/Users/robin/learning/docker/mongo_replset/mongo_3/log/:/var/log/mongodb/</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">20005</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">"-f"</span>, <span class="hljs-string">"/etc/mongod.conf"</span>,<span class="hljs-string">"--replSet"</span>, <span class="hljs-string">"rs_mongo"</span>]
    <span class="hljs-attr">network_mode:</span> <span class="hljs-string">mongo_net</span>
</code></pre>
<h3 id="heading-step-4-create-mongodconf">Step 4: create mongod.conf</h3>
<pre><code>$  code .
</code></pre><pre><code class="lang-YAML"><span class="hljs-comment"># mongod.conf</span>

<span class="hljs-comment"># for documentation of all options, see:</span>
<span class="hljs-comment">#   http://docs.mongodb.org/manual/reference/configuration-options/</span>

<span class="hljs-comment"># where to write logging data.</span>
<span class="hljs-attr">systemLog:</span>
  <span class="hljs-attr">destination:</span> <span class="hljs-string">file</span>
  <span class="hljs-attr">logAppend:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">path:</span> <span class="hljs-string">/var/log/mongodb/mongod.log</span>

<span class="hljs-comment"># Where and how to store data.</span>
<span class="hljs-attr">storage:</span>
  <span class="hljs-attr">dbPath:</span> <span class="hljs-string">/data/db</span>
  <span class="hljs-attr">journal:</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">engine:</span>  <span class="hljs-string">wiredTiger</span>

<span class="hljs-comment"># network interfaces</span>
<span class="hljs-attr">net:</span>
  <span class="hljs-attr">port:</span> <span class="hljs-number">27017</span>
  <span class="hljs-attr">bindIp:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>
</code></pre>
<h3 id="heading-step-5-spin-up-replicaset-containers">Step 5: Spin-up replicaset containers</h3>
<pre><code class="lang-shell">$ docker compose up -d
[+] Running 3/3
 ⠿ Container mongo_2  Created                                                                                                                                   0.2s
 ⠿ Container mongo_1  Created                                                                                                                                     0.2s
 ⠿ Container mongo_3  Created
</code></pre>
<h3 id="heading-step-6-initiate-replicaset">Step 6: Initiate replicaset</h3>
<pre><code class="lang-shell">$ docker exec -it mongo_1 bash

root@mongo_1:/# mongo
rs_mongo:SECONDARY&gt; rs.initiate(
   {
      _id: “rs_mongo”,
      version: 1,
      members: [
         { _id: 0, host : “mongo_1:27017” },
         { _id: 1, host : “mongo_2:27017” },
         { _id: 2, host : “mongo_3:27017” }
      ]
   }
)

rs_mongo:SECONDARY&gt; db.isMaster() 
{
    "topologyVersion" : {
        "processId" : ObjectId("614615744d54c08963ef67f6"),
        "counter" : NumberLong(6)
    },
    "hosts" : [
        "mongo_1:27017",
        "mongo_2:27017",
        "mongo_3:27017"
    ],
    "setName" : "rs_mongo",
    "setVersion" : 1,
    "ismaster" : true,
    "secondary" : false,
    "primary" : "mongo_2:27017",
    "me" : "mongo_2:27017",
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Create MongoDB Standalone and Replica Set containers using Docker]]></title><description><![CDATA[Docker Containers offer easy setup, customization and scalability. In this article, i will walk you through how to use Docker to setup MongoDB standalone and replica set containers within minutes.
The article is divided in two parts, the first part i...]]></description><link>https://blog.robinjiang.com/create-mongodb-docker</link><guid isPermaLink="true">https://blog.robinjiang.com/create-mongodb-docker</guid><dc:creator><![CDATA[robin Jiang]]></dc:creator><pubDate>Fri, 29 Dec 2023 07:30:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703835001536/9ed6e58c-785e-4c91-8cd8-967d7614c522.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Docker Containers offer easy setup, customization and scalability. In this article, i will walk you through how to use Docker to setup MongoDB standalone and replica set containers within minutes.</p>
<p>The article is divided in two parts, the first part is setting up the standalone MongoDB container and second part is setting up and grouping MongoDB containers as member of replica set with Docker.</p>
<p>Let’s get started.</p>

<h2 id="heading-system-configuration">System Configuration</h2>
<p>To run this setup, Docker Engine is required to be installed on the system. Follow the official documentation to setup Docker Engine on your system.</p>
<p>:::caution</p>
<p>The steps and configuration for both standalone and replica set is not to be used for production deployment. The intended use is only for setting up a environment to support learning of MongoDB.</p>
<p>:::</p>
<h2 id="heading-standalone-mongodb-setup">Standalone MongoDB Setup</h2>
<ul>
<li>Pull the Docker MongoDB official image from Docker Hub. The following code snippet demonstrates pulling the docker MongoDB 4.4.9 release. To pull the MongoDB 5.0 latest release replace :4.4.9-rc0 with :latest tag</li>
</ul>
<pre><code class="lang-shell">
$ docker pull mongo:4.4.9-rc0
</code></pre>
<ul>
<li>To check if the the image pull from Docker Hub was successful</li>
</ul>
<pre><code>
$ docker images                                                   
REPOSITORY   TAG         IMAGE ID       CREATED       SIZE
mongo        <span class="hljs-number">4.4</span><span class="hljs-number">.9</span>-rc0   <span class="hljs-number">24599</span>d6cde30   <span class="hljs-number">9</span> days ago    <span class="hljs-number">413</span>MB
mongo        latest      <span class="hljs-number">31299</span>b956c79   <span class="hljs-number">10</span> days ago   <span class="hljs-number">642</span>MB
</code></pre><ul>
<li>Lets start first standalone container – the below command starts MongoDB docker container with name mongo_449 in detached mode using the 4.4.9-rc0 image</li>
</ul>
<pre><code class="lang-shell">
$ docker run --name mongo_449 -d mongo:4.4.9-rc0
</code></pre>
<ul>
<li>List the container status and health by executing</li>
</ul>
<pre><code class="lang-shell">
$ docker container ls -a

CONTAINER ID   IMAGE          COMMAND                  CREATED       STATUS                      PORTS       NAMES
96e64ec525a2   24599d6cde30   "docker-entrypoint.s…"   2 hours ago   Up 33 minutes               27017/tcp   mongo_449
</code></pre>
<ul>
<li>To run a command inside the container<ul>
<li>docker exec: interact with containers (running/up mode)</li>
<li>-i : interactive STDIN open even if not attached to the container</li>
<li>-t: pseudo TTY</li>
</ul>
</li>
</ul>
<ul>
<li>Connect to MongoDB daemon</li>
</ul>
<pre><code class="lang-shell">root@96e64ec525a2:/# mongo

MongoDB shell version v4.4.9-rc0
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&amp;gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("ac624a79-908b-4580-90ae-22d0a7aee07a") }
MongoDB server version: 4.4.9-rc0
</code></pre>
<ul>
<li>Install utilities. The utilities ping, systemctl, sudo installed in the containers can be used for troubleshooting during the setup of Docker containers.</li>
</ul>
<pre><code class="lang-shell">
root@96e64ec525a2:/# apt-get install iputils-ping̵
root@96e64ec525a2:/# apt-get install sudo 
root@96e64ec525a2:/# apt-get install systemctl
</code></pre>
<p>This finishes the setup of standalone MongoDB Container. Now let’s look at ReplicaSet setup.</p>
<h2 id="heading-creating-mongodb-replicaset-using-docker">Creating MongoDB ReplicaSet using Docker</h2>
<p>A replica set consists of a primary node together with two or more secondary nodes. It is recommended to group three or more nodes, with an odd number of total nodes. The primary node accepts all the write requests which are propagated synchronously or asynchronously to the secondary nodes. Below are the steps required to complete the replica set setup using Docker.</p>
<p>Create a new network(bridge) within Docker. The replica set containers will be mapped to the new network.</p>
<pre><code class="lang-shell">$ docker network create mongo_net
$ docker network inspect mongo_net                       
[
    {
        "Name": "mongo_net",
        "Id": "e2567806642a9245436371a9b9904c71fadae969fbd11a7bb8203e07976b1b2a",
        "Created": "2021-09-11T00:36:33.989688708Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
...
]
</code></pre>
<ul>
<li>Start 3 containers – Primary Secondary Secondary<ul>
<li>Break down of parameters docker run : start a new container<ul>
<li><code>-d</code> :  run the container in detached mode</li>
<li><code>-p 20001:27017</code> publish container port to the host and bind 27017 to 20001 on the host. This is useful if connecting mongo client like mongosh to container</li>
<li><code>--name</code> : name of the mongo container</li>
<li><code>-- network</code> : connect to user created network mongo_net</li>
<li><code>mongo:4.4.9-rc0</code> : Docker MongoDB image</li>
<li><code>mongod --replSet rs_mongo</code> : run the mongod daemon and add the container to replica set name rs_mongo</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-shell">$ docker run -d -p 20001:27017 --name mongo1 --network mongo_net mongo:4.4.9-rc0 mongod --replSet rs_mongo
$ docker run -d -p 20002:27017 --name mongo2 --network mongo_net mongo:4.4.9-rc0 mongod --replSet rs_mongo
$ docker run -d -p 20003:27017 --name mongo3 --network mongo_net mongo:4.4.9-rc0 mongod --replSet rs_mongo
</code></pre>
<ul>
<li>Set up Replica set. Connect to one of the containers and run the below commands. The container that receives the initiate will pass on the configuration to other containers assigned as members.</li>
</ul>
<pre><code class="lang-js">rs_mongo [direct: primary] test_2&gt; config = {
      <span class="hljs-string">"_id"</span> : <span class="hljs-string">"rs_mongo"</span>,
      <span class="hljs-string">"members"</span> : [
          {
              <span class="hljs-string">"_id"</span> : <span class="hljs-number">0</span>,
              <span class="hljs-string">"host"</span> : <span class="hljs-string">"mongo1:27017"</span>
          },
          {
              <span class="hljs-string">"_id"</span> : <span class="hljs-number">1</span>,
              <span class="hljs-string">"host"</span> : <span class="hljs-string">"mongo2:27017"</span>
          },
          {
              <span class="hljs-string">"_id"</span> : <span class="hljs-number">2</span>,
              <span class="hljs-string">"host"</span> : <span class="hljs-string">"mongo3:27017"</span>
          }
      ]
  }

rs_mongo [direct: primary] admin&gt; rs.initiate(config)

<span class="hljs-comment">//Insert test data</span>

rs_mongo [direct: primary] admin&gt; use test_2
rs_mongo [direct: primary] test_2&gt; db.employees.insert({<span class="hljs-attr">name</span>: <span class="hljs-string">"robin"</span>)

<span class="hljs-comment">//To read queries on secondary run setReadPref. </span>
rs_mongo [direct: secondary] test_2&gt;db.getMongo().setReadPref(<span class="hljs-string">'secondary'</span>)

rs_mongo [direct: secondary] test_2&gt; db.employees.find()
[
  { <span class="hljs-attr">_id</span>: ObjectId(<span class="hljs-string">"613c99801ea796508e3c73f5"</span>), <span class="hljs-attr">name</span>: <span class="hljs-string">'robin'</span> }
]
</code></pre>
<ul>
<li>Validate Replica Set Configuration</li>
</ul>
<pre><code class="lang-js">rs_mongo [direct: primary] test_2&gt; db.printReplicationInfo()

configured oplog size
<span class="hljs-string">'557174 MB'</span>
---
log length start to end
<span class="hljs-string">'71372 secs (19.83 hrs)'</span>
---
oplog first event time
<span class="hljs-string">'Sat Sep 11 2021 15:47:21 GMT+0530 (India Standard Time)'</span>
---
oplog last event time
<span class="hljs-string">'Sun Sep 12 2021 11:36:53 GMT+0530 (India Standard Time)'</span>
---
now
<span class="hljs-string">'Sun Sep 12 2021 11:36:54 GMT+0530 (India Standard Time)'</span>


rs_mongo [direct: primary] test_2&gt; rs.conf()
{
  <span class="hljs-attr">_id</span>: <span class="hljs-string">'rs_mongo'</span>,
  <span class="hljs-attr">version</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">term</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">protocolVersion</span>: Long(<span class="hljs-string">"1"</span>),
  <span class="hljs-attr">writeConcernMajorityJournalDefault</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">members</span>: [
    {
      <span class="hljs-attr">_id</span>: <span class="hljs-number">0</span>,
      <span class="hljs-attr">host</span>: <span class="hljs-string">'mongo1:27017'</span>,
      <span class="hljs-attr">arbiterOnly</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">buildIndexes</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">hidden</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">priority</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">tags</span>: {},
      <span class="hljs-attr">slaveDelay</span>: Long(<span class="hljs-string">"0"</span>),
      <span class="hljs-attr">votes</span>: <span class="hljs-number">1</span>
    },
    {
      <span class="hljs-attr">_id</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">host</span>: <span class="hljs-string">'mongo2:27017'</span>,
      <span class="hljs-attr">arbiterOnly</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">buildIndexes</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">hidden</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">priority</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">tags</span>: {},
      <span class="hljs-attr">slaveDelay</span>: Long(<span class="hljs-string">"0"</span>),
      <span class="hljs-attr">votes</span>: <span class="hljs-number">1</span>
    },
    {
      <span class="hljs-attr">_id</span>: <span class="hljs-number">2</span>,
      <span class="hljs-attr">host</span>: <span class="hljs-string">'mongo3:27017'</span>,
      <span class="hljs-attr">arbiterOnly</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">buildIndexes</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">hidden</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">priority</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">tags</span>: {},
      <span class="hljs-attr">slaveDelay</span>: Long(<span class="hljs-string">"0"</span>),
      <span class="hljs-attr">votes</span>: <span class="hljs-number">1</span>
    }
</code></pre>
<p>That concludes this article.</p>
]]></content:encoded></item></channel></rss>