2026/10/15 13:19:22

openrig:用Git和Docker打造可复现的个人开发环境基座

openrig:用Git和Docker打造可复现的个人开发环境基座 1. 为什么我会搭建 openrig把零散的开发环境拧成一股绳说实话第一次听到“openrig”这个词我脑子里浮现的是摄影器材里的“滑轨”或者“云台”。但做开发的人看到后面三个字母“rig”很容易联想到一套为了特定目标组合起来的装备——就像赛车的车架、钻井的平台或者登山者的全套绳索装备。我最终把它落实成了一个个人开发环境的整合项目核心就一个念头把散落在终端、编辑器、容器、云主机里的开发习惯变成一套开箱即用、可移植、可复刻的“装备架”。这事儿的起因很朴素。我手上有几台机器一台常驻办公室的 Linux 工作站一台家里淘汰下来的 Mini 主机还有一个偶尔租来的云服务器。以前每次换环境都要重新装一遍 Node、Python、Docker、Git 钩子、别名、vim 配置、代理设置安全合规的内网通道非敏感用途零零总总加起来小半天就没了。更烦的是每台机器上的版本还不一样今天在 A 机器上能跑的脚本明天在 B 机器上莫名其妙就报错。这种“环境漂移”带来的心智损耗远比代码本身复杂。openrig 的定位就顺理成章了它是一个用纯开源组件构建的、可版本控制的个人开发环境基座。它不做大而全的 IDE 替代品也不搞云桌面那种重家伙而是把每一层开发环境都“代码化”——从系统的初始化脚本、Shell 配置、编辑器插件到容器编排、远程开发入口全部塞进一个 Git 仓库里。只要机器上有 Git 和 Docker就能在一个小时内复现出和我本地几乎一致的工作台。这篇文章面向的读者是那些和我一样有“多机开发”困扰、想把手动配置变成版本化资产、又不希望引入商业方案的中高级开发者。当然小白也能看懂因为我会把每一步的设计动机和落地细节都摊开来讲。下面从技术选型开始讲讲 openrig 这颗螺丝到底拧在哪里。2. openrig 的技术底座为什么是“最小可用原则”而不是全家桶项目立项前我列了一个需求清单对着清单逐一过滤掉那些听起来酷但实际用不上的方案。最终确定的核心组件只有四样Git、Docker、task 脚本用 GNU Make 做统一入口、以及一个轻量的远程访问方案。下面说说我为什么这么选以及每层之间怎么咬合。2.1 选型核心不追求工具多追求“可复现 可回滚”很多开发者在搭环境时喜欢“装完即走”结果半年后发现机器变砖了谁也说不清当时改了什么。openrig 的核心设计哲学是**“一切皆代码一切皆可回滚”**。所有安装和配置动作要么是代码仓库里的脚本要么是容器镜像里的指令坚决不手改系统全局配置。用 Git 做版本管理是必然选择。任何一层配置出了问题git log能定位是哪次改动引入的git revert能一键回到上一个可用状态。这一点在团队协作时价值更大两个开发者可以通过 Pull Request 的方式互相审阅环境配置变更而不是在 IM 里发一条“你帮我看看哪里配错了”。用 Docker 做环境隔离是为了解决“依赖地狱”。比如说我同时维护着几个 Python 2 时代的老项目和一些用 Python 3.12 的新项目直接用宿主机装包几乎必然冲突。openrig 在每个项目目录下声明独立的容器环境镜像固定 tag依赖锁死容器生命周期完全由脚本管理。这比虚拟环境更干净、比裸机更一致。2.2 目录结构设计一眼就知道哪块在哪openrig 的仓库结构是长这样的openrig/ ├── Makefile ├── bootstrap.sh ├── envs/ │ ├── base/ # 各机器的公共环境 │ │ ├── dotfiles/ │ │ ├── scripts/ │ │ └── docker-compose.yml │ └── projects/ # 每个项目的独立容器编排 │ ├── project-a/ │ │ └── docker-compose.yml │ └── project-b/ │ └── docker-compose.yml ├── remote/ │ └── entrypoint.sh # 远程开发入口脚本 └── docs/ └── setup-guide.mdMakefile 是整个 rig 的开关面板。我常用这样几条指令make setup首次在新机器上跑执行 bootstrap、同步 dotfiles、拉取基础镜像make dev启动当前项目的开发容器并挂载代码目录make doctor检查环境一致性输出版本号差异make update拉取最新配置、重建容器、执行数据库迁移类动作之所以用 Makefile 而不是单独的 shell 脚本是因为 Make 本身天然支持依赖关系——make dev前它会先去检查镜像是否存在不存在就自动执行构建省去大量 “先检查再执行” 的样板代码。这在日常操作里真的能省不少时间。2.3 bootstrap 脚本新机器五分钟进入“灵魂状态”bootstrap.sh是 openrig 的入场券。它的任务很简单让一台刚拆封的机器具备运行 openrig 所需的基础设施。脚本内部逻辑分三段系统探测识别当前是 Debian 系还是 macOS分支安装 Git、Docker、Make。用户级配置注入把 dotfiles 里的 Shell 别名、编辑器设置、Git 全局配置通过符号链接的方式挂到用户目录不直接覆盖原文件——万一你之前有自定义配置openrig 会把它合并进来而不是一删了之。自检跑一遍make doctor输出一个环境健康报告。我第一次在办公室那台 Linux 工作站上执行bash bootstrap.sh从执行到能跑make dev只花了四分钟。这套流程免去了我人工确认“装没装 curl”“要不要加 apt 源”这类低级而耗时的步骤。3. 本地开发环境的装配细节dotfiles、容器化与目录挂载进入实操层面openrig 的本地环境由两块构成一块是宿主机上的“壳”Shell、编辑器、Git 配置另一块是容器里的“核”项目依赖、运行时、数据库服务。两者用目录挂载和端口映射联通。3.1 dotfiles 管理方案符号链接优于复制粘贴很多人的 dotfiles 都是复制一份丢进新机器时间一久就会分叉。openrig 用的是符号链接加条件判断的方式。每个配置文件在envs/base/dotfiles/里维护执行make sync时脚本会对每个 dotfile先比较目标路径下的当前内容是否与仓库一致。一致就跳过不一致才提示——注意是提示而不是强制覆盖。对.gitconfig这种包含用户名的文件用占位符加模板渲染避免把私人信息提交进公开仓库。我举个具体例子。.gitconfig的模板里有这样一段[user] name {{GIT_USER_NAME}} email {{GIT_USER_EMAIL}} [core] editor vim pager less [alias] st status co checkout br branch lg log --oneline --graph --all -10make sync执行时会从环境变量或本地提示输入里取值渲染成真正的.gitconfig。这让我在不同机器上用不同身份提交代码也不会串号。3.2 开发容器的标准模板别再为版本冲突失眠openrig 对每个项目提供一份docker-compose.yml模板核心思路是“一个项目一套依赖服务与代码解耦”。我实际跑的两个容器配置简化后大概是这样的services: dev: image: node:20-bookworm working_dir: /workspace volumes: - ../..:/workspace ports: - 3000:3000 environment: NODE_ENV: development command: tail -f /dev/null跑起来之后我直接用docker compose exec dev bash进容器代码目录已经挂在/workspace里编辑器在宿主机上改代码容器里实时同步。端口映射 3000 对 3000让浏览器和调试工具直接访问容器内的服务。数据库这类的有状态服务会用另一份 compose 文件单独管理避免和代码容器捆绑重启。这套方案的好处是不管宿主机是什么系统容器内永远是同一个 Linux 发行版、同一个 Node 版本。团队内如果有人提出 “诶我这边跑不起来”第一反应不再是你机器有问题而是去看docker compose config对比镜像版本。3.3 一个让我后悔没早点做的事把环境变量也纳入版本管理项目跑一段时间后最怕的是环境变量散落在各处——.env文件、系统变量、启动命令参数里各有一份。openrig 在envs/base/scripts/下放了一个load-env.sh专门负责从.env.template渲染出当前环境的.env文件。模板里只有键名和注释说明真实值通过make env-init交互式填写或者从本机的密钥管理工具读取。这样一来即使.env文件被误提交到公开仓库丢失的也只是占位符。而每台机器上真实的环境变量则可以通过对比模板与实际的差异来快速排查失踪配置。这个细节帮我处理过一次非常棘手的调试线上环境的一个服务一直连错数据库最后发现就是.env里两个键的命名顺序不一致导致的而 openrig 的检查脚本五分钟就定位出来了。4. 打通远程开发链路从本机容器到云端工作台openrig 不只是管本机。诊断多机问题的时候我经常需要连到云服务器上改东西以前是通过 SSH 进去然后纯终端操作代码同步靠 git 来回推。现在 openrig 的 remote 模块提供了完整路径。4.1 用 SSH 隧道把远程容器“映射”到本地远程开发听起来玄乎本质其实不复杂让本地编辑器像操作本地目录一样操作远程文件同时在远程跑命令。openrig 用的是 SSH Docker 的组合。第一步是在远程服务器上也跑一遍 openrig 的 bootstrap装好 Docker 和基础配置。这保证了远程和本地处于同一套环境基座。第二步是执行make remote-up。它会读取remote/hosts.yml里的主机清单对指定的主机执行ssh userhost cd ~/openrig git pull docker compose -f envs/projects/project-a/docker-compose.yml up -d这条命令的作用是登录远程主机、拉取最新代码、启动对应项目容器。远程容器跑起来后本地只需要用 VS Code 的 Remote-SSH 插件或者 JetBrains 的远程开发网关连接即可。打开编辑器显示的虽然不是本地目录但体验上几乎无差异。4.2 无桌面环境下的备用通道纯终端布局有时候远程服务器上没有图形界面也不想装重量级 IDE。openrig 的remote/entrypoint.sh会创建一个 tmux 会话在会话里自动化布局几个窗口窗口一项目日志实时 tail窗口二容器内 Shell执行测试和构建窗口三宿主机 Shell处理 Git 操作和文件管理接着把这个 tmux 会话的模式存成独立脚本下次连接执行session-restore就能一键恢复布局。我自己用这套已经有半年出差时笔记本上只需要一个 SSH 客户端就能完整操作正在云端跑的服务。4.3 连接安全与多主机管理别在密钥和端口上翻车需要重点强调的连接安全细节有三条优先使用 SSH 密钥认证而非密码并且密钥本身建议加 passphraseopenrig 的 bootstrap 会在新机器上生成专用密钥然后把公钥分发到各主机。只在必要的端口上暴露服务比如只放开 22 端口给 SSH容器内服务端口留在内网通过本地 SSH 隧道转发访问而不是直接把 3000 端口对外开放。远程主机清单本身要小心remote/hosts.yml里不要存明文密码主机 IP 如果敏感就只保留内网地址或占位符真实地址写在个人密钥库里。我的一位同事在这上面踩过坑他把线上服务器的 IP 和用户名写死在一个公开仓库的 config 文件里结果被爬虫抓走很快就有人拿字典跑 SSH 爆破。从那以后 openrig 的所有配置模板都坚持“敏感信息零入库”的原则。5. 日常使用中的真实感触效率提升之外的收获前面讲了很多技术细节这一章聊一些更“软”的东西。openrig 用了几个月最大的体会不是“命令敲得少了”而是思考方式变了。以前遇到环境问题第一反应是“重装一下试试”或者去论坛复制一大段不知道来源的修复命令。现在遇到问题我会先想“是哪个组件变了能不能在 openrig 的仓库里定位到对应变更” 这就像一个面对报错先看堆栈而不是盲目重启的程序员习惯一旦养成就回不去了。另外openrig 让我对不同环境之间的差异更敏感。比如有一次我把两台机器上的 Node 版本更新到同一大版本但npm ls列出来的依赖树仍然不完全一致最后发现是系统架构不同导致的——一台是 amd64一台是 arm64。这个问题在 openrig 的make doctor里专门加了架构检测字段一跑就看出来。现在我会在项目文档里明确标注架构而不是默认所有机器都一样。对人心的影响也值得一提给自己搭建这套“装备架”的过程其实是一种对工作方式的复盘。哪些工具真正在反复使用哪些只是在装点门面一目了然。openrig 的环境里没有我实际不需要的软件这也让机器本身的负担小了很多。最后再分享一个小技巧如果你也想搭一个类似的 openrig不要一开始就追求覆盖所有项目和所有机器。先选一台机器、一个项目把最小闭环跑通——就是从 git clone 到make dev能看到项目跑起来——然后再逐步扩展。有了这个闭环打底后面无论加多少层都不会乱。openrig 这个名字本身对我来说已经从一个模糊的念头变成了每天都要用的工具箱。它不一定适合所有人但对于那些经常和“多机、多项目、多环境”打交道的人来说值得认真一试。