“配环境一天,上线半天”——这句话几乎能瞬间唤醒每个开发者的痛苦记忆。我不是说那种“装个Python跑个hello world”的环境,而是真正复杂的业务项目:不同的解释器版本、一堆编译期依赖、诡异的系统库缺失、还有上线时候要手动执行的N条命令。前几天我在做一个数据项目的时候,又被这套流程狠狠折磨了一轮,从早上10点折腾到下午5点,环境还没完全跑通;好不容易环境好了,上线的半天又来了:手工备份、手工改配置、手工传包,哪一步都不敢出错。
后来我花了点时间,认真把这套流程彻底拆了一遍,用脚本、标准化的构建产物和容器化的方式重新组织,实测下来,从拿到一台干净机器到项目跑起来,三分多钟能完成;从代码合入到发布上线,也是几分钟内搞定。这篇文章我把我自己的完整方案、每一步的考虑和踩过的坑全部写出来,希望对同样被困在“配环境和上线”里的朋友有点帮助。
先说明一下,我这里说的“配环境”和“上线”是通用场景,不局限于某一种语言或某一种部署方式。无论你是写Python、Node.js,还是做企业级系统的初始化导入,思路都是相通的。
1. 为什么“配环境1天”不是夸张说法
1.1 先看看这一天的时间都去哪了
很多人会觉得“配环境不就是装个依赖吗,能有多慢”。等你真正去配一个老项目的环境,就会发现事情远没有那么简单。我就用最常见的Python项目举例,因为这也是很多读者朋友会遇到的场景。
假设你拿到一台新电脑,或者入职一家新公司接手一个老项目,你通常会按照下面的流程走一遍:
- 下载Python解释器,选版本。这里就有一个陷阱:项目要求的是3.8,你装了个3.11,结果一堆语法不兼容的报错。
- 安装完解释器之后,要创建虚拟环境。很多人会用
python -m venv venv,也有人用conda、pipenv或者Poetry,工具不统一,团队成员之间就很容易产生“我这边明明能跑”的争议。 - 安装依赖包。一个老项目可能有几十个甚至上百个依赖,pip安装某些带C扩展的包时,经常会在编译环节挂掉——缺编译器、缺动态链接库、缺系统头文件,这些报错一条条去搜,一上午就没了。
- 配IDE。装完依赖还得配PyCharm或者VS Code,把解释器路径指对,把Python路径选好。很多人照着“pycharm安装教程配环境”这类帖子一步步弄,依然会在解释器路径上卡住。
- 环境变量、配置文件、数据库连接参数,这些都要手动核对,少一个变量程序跑起来就是一连串的乱七八糟。
我把以上的每一步都计算过时间:每一步单独看似乎都是小事,但每一步都可能因为一个版本号、一个路径、一个网络问题卡住十几分钟甚至半小时。我当时在被一个老项目折磨的时候,光是排查一个libssl.so.1.1 not found就花了将近两个小时,因为那个项目依赖的某个库必须要旧版OpenSSL,新版系统里根本没有这个动态库。
如果把工作时间折算下来,“配环境一天”一点都不夸张。更可怕的是,这个过程是完全不可复现的——你这台机器配好了,换一台机器又要重新来一遍,而且很可能因为系统版本、已装软件不同,踩到完全不一样的坑。
1.2 环境问题的本质是“漂移”和“不可重复”
做了几年开发之后,我觉得环境问题的本质就两个词:漂移和不可重复。
所谓漂移,就是你的开发环境、测试环境和生产环境慢慢变得不一样。开发机上你可能顺手装过好几个版本的包,测试机上有人跑过别的项目,生产机上有安全软件拦截,这些差异累积起来,就会造成“我本地没问题,一上线就挂”的超级尴尬局面。
不可重复则是指环境的搭建过程没有被记录下来、没有被标准化。每一次配置环境都是纯手工操作,靠人脑记忆,靠搜索引擎找答案,这就导致整个流程的成功率完全取决于操作者的经验和运气。你这次成功了,不代表下次还能成功;你成功了,不代表同事也能成功。
我记得有一次,一个同事在群里说:“我按文档配了一天还是跑不起来,报错说找不到模块。”我过去一看,他用的Python版本和我写文档的时候用的完全不一样,虚拟环境的路径也不对。我当场就意识到:文档就算写得再详细,只要是靠人手工执行,就一定会出偏差。真正一劳永逸的办法,是把环境的搭建过程“写进代码里”,让机器去执行,不给人肉操作的机会。
1.3 为什么必须破局:从效率到风险
有人可能会说:“我又不是天天配环境,一年就配个一两次,何必折腾自动化?”这个观点我以前也有过,但后来我发现,这样做问题很大。
从效率上看,配环境的耗时虽然频率低,但它是“纯消耗”——它不产生任何业务价值,却消耗了你大量时间和精力。上线更是如此,每一次发版都是提心吊胆的,如果步骤多、容易出错,耗费的时间就更长。如果把这两件事从“一天+半天”压缩到“三分钟”,哪怕一年只省下两三次,也足够做不少正事了。
从风险上看,手工操作是生产事故的头号来源。我见过太多因为上线时少执行了一条命令、改错了一个配置而导致的线上故障。环境配置和上线部署一旦变成“自动化、可重复、有记录”的操作,那么每次发布的结果都是可预期的,这本身就是对系统稳定性最大的保障。
2. 三步走:把环境配置从“一天”变成“三分钟”
2.1 第一步:把环境定义写进代码
手工配置环境的第一个问题就是“环境定义没有唯一事实来源”。所以我做的第一件事,就是强制把环境依赖写进代码仓库。
以Python项目为例,就是把依赖从requirements.txt升级为带版本锁定和哈希校验的形式,同时用Pipfile.lock或者poetry.lock锁定传递依赖的精确版本。有人可能觉得requirements.txt就够了,但那个文件经常只是写了个顶层依赖名,版本没锁死,传递依赖更是一点没管,结果就是隔了三个月重新安装,装出来的版本可能完全不一样。
我自己比较推荐的办法是:项目用pyproject.toml管理依赖,开发环境用poetry或者uv生成lock文件,然后CI里强制检查lock文件和依赖声明是否一致。这样团队里任何一个人拿到项目,执行同一条命令,装出来的依赖就是完全一样的版本组合。工具集统一还有一个额外的好处:团队里再也不会出现A用pipenv、B用conda、C手工装包的乱象。
2.2 第二步:写一个一键初始化脚本
有了依赖锁文件,接下来要做的是把“从零到能跑”的所有手工步骤收敛成一个脚本。这个脚本要做的不仅仅是安装依赖,它还要把解释器版本检查、虚拟环境创建、系统依赖安装(比如那些libssl之类的动态库)、配置文件模板生成全部串起来。
以我常用的Python项目初始化脚本为例,它的核心逻辑大致如下:
#!/usr/bin/env bash set -euo pipefail PYTHON_VERSION="3.11.9" APP_DIR="$(cd "$(dirname "$0")/.." && pwd)" VENV_DIR="$APP_DIR/.venv" echo "[1/5] 检查 Python 版本..." if ! command -v python3 >/dev/null 2>&1; then echo "错误:未找到 Python 3,请先安装 Python $PYTHON_VERSION" exit 1 fi echo "[2/5] 创建虚拟环境..." if [ ! -d "$VENV_DIR" ]; then python3 -m venv "$VENV_DIR" fi echo "[3/5] 激活虚拟环境并升级 pip..." source "$VENV_DIR/bin/activate" pip install --upgrade pip setuptools wheel echo "[4/5] 安装项目依赖..." if [ -f "$APP_DIR/poetry.lock" ]; then pip install poetry poetry install --no-root elif [ -f "$APP_DIR/requirements.txt" ]; then pip install -r "$APP_DIR/requirements.txt" fi echo "[5/5] 生成本地配置文件..." if [ ! -f "$APP_DIR/.env" ]; then cp "$APP_DIR/.env.example" "$APP_DIR/.env" echo "已从 .env.example 生成 .env,请检查配置项" fi echo "环境就绪:$VENV_DIR"脚本里有几个关键细节要说一下:
set -euo pipefail:这个一定要加。它保证脚本任何一个步骤失败都会立刻退出,而不是带着错误继续往下跑,导致最后得到的是一个“看起来成功、实际坏掉”的环境。- 虚拟环境目录固定为
.venv:把这个目录加进.gitignore的同时,也把它作为团队约定的统一位置。这样大家在IDE里配置解释器路径的时候,路径是完全一致的,不会出现“你的venv在项目里、我的venv在home目录下”这种路径错乱问题。 - 配置文件从
.env.example复制生成,而不是直接创建空文件:这样每个变量都有模板和注释,新人看到就知道要填什么。
2.3 第三步(终极大招):容器化,连操作系统都一起锁死
脚本化已经能解决大部分环境问题了,但它还有一个解决不了的短板:系统级别的依赖。比如项目需要libssl.so.1.1、需要旧版GCC、需要某个特定的系统库,这些在脚本里处理起来很费劲,因为你不能随便改开发机的系统环境。
这个问题的终极解法是容器化。我自己在完成脚本化之后,又花了一点时间给项目写了一个Dockerfile,用一个文件把整个运行环境全部定义清楚:
FROM python:3.11-slim ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 RUN apt-get update && \ apt-get install -y --no-install-recommends \ libpq-dev \ curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY pyproject.toml poetry.lock ./ RUN pip install poetry && poetry install --no-root COPY . . EXPOSE 8000 CMD ["poetry", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]有了这个Dockerfile之后,“配环境”这件事就发生了质的变化:以前是在每台机器上重复装环境,现在是把环境固化在一个镜像里,任何一台装好Docker的机器上执行构建和运行,拿到的都是一模一样的运行环境。
有人可能会说容器化性能有损耗,或者有学习成本。我承认这些点有道理,但用容器化来换取环境的绝对一致性,这个交易太划算了。尤其是对于要部署上线的应用来说,开发和运行阶段使用同一个镜像,也就彻底堵死了“我本地好好的”这类推诿。
这里我还想多说一句关于“ap上线”这类网络设备场景。如果你做的是连接设备的项目(比如AP、路由器的配置下发),这类设备本身不能跑Docker,但思路可以参考:把设备的配置文件模板、固件版本全部放进工程仓库,写一个脚本一键生成配置文件并批量下发,同样能达到“配置可复现、批量秒级完成”的效果。设备上线与传统应用上线的介质不同,但“把人工操作变成代码操作”的逻辑是完全统一的。
2.4 方案怎么选:脚本、容器还是云开发环境
不是所有项目都适合一步到位上容器。我自己在实际操作中会按下面的标准来选:
- 单纯的语言级依赖,系统库干净:用脚本+lock文件就够了。比如纯Python项目和Node.js项目用这种方式最轻量,不引入额外复杂度。
- 涉及系统依赖、需要多服务协作、要部署上线:直接用Docker Compose。这也是我现在最常用的方式,因为开发环境和线上环境保持一致。
- 团队新人多、电脑环境很杂(Windows/Linux/macOS都有):可以考虑云开发环境(GitHub Codespaces或者GitLab Web IDE),它在云端启动Be容器,不管是公司统一还是开箱即用都比本地更好。
我个人的建议是:先从脚本化开始,把流程走通,然后再逐步加容器。如果一上来就让团队切到容器化,老项目的历史包袱可能会让你举步维艰。
3. 上线部署:从半天到3分钟的关键动作
3.1 上线的痛点和“3分钟”的逻辑
环境搞定了,接下来就是上线。很多人觉得上线慢是因为代码多、编译久,但我踩过几次坑之后发现,上线慢的真正原因其实是这几个:
- 发布步骤多且无序:备份、上传、停服、更新、重启、验证,每一步之间都要手动等待和判断。
- 回滚靠运气:很多团队根本没有回滚预案,一旦上线出问题,就只能靠人肉恢复,这个过程的耗时可能是几个小时。
- 配置和代码没有分离:每次上线都要手工修改配置文件,改错一个字符就是事故。
- 数据变更没有纳入发布体系:代码更新了,数据库表结构或初始数据却没人执行,导致新代码跑不起来。
要把上线从“半天”压缩到“3分钟”,逻辑上和配环境是完全一样的:把操作流程代码化,把发布过程标准化。3分钟不是一个理论数字。当你的流水线搭好之后,从流水线触发构建到完成发布,实际上就是几分钟的时间。你节省掉的不是执行的几分钟,而是过去为了整理步骤、纠结操作顺序所耗费的那几个小时。
3.2 构建产物标准化:同一次代码提交只能产生同一份产物
我做的第一件事,是解决“构建产物”的问题。
很多团队的上线过程还停留在“从某台开发机拿代码,在服务器上现场装依赖、现场启动服务”。这种做法问题很大:开发机上跑出来的代码,和最终运行的代码可能根本不是同一份。
我现在的做法是引入持续集成,在代码合并后自动触发构建,生成一个带版本号的不可变产物。对于Python项目,这个产物可以是wheel包,也可以是打包好的Docker镜像;对于前端项目,就是带指纹的静态文件和镜像。
在CI中,我会做这些关键操作:
- 拉取代码,切换到指定的git提交。
- 安装依赖,跑单元测试和静态检查。
- 构建产物,使用Web使用符号离设置好的版本号,比如
release-20250115-001这一个编号就对应唯一一次代码提交。 - 把产物推送到制品仓库或者镜像仓库,并生成发布清单。
这么做的核心目的是:上线的过程不再依赖“现场装依赖”,而是把已经构建好的、测试过的产物直接部署上去。这样我在服务器上要做的,就不再是漫长的构建和安装,而只是拉取制品、替换文件、重启服务,这个过程的耗时自然就会降下来。
3.3 部署脚本化:一条命令完成全流程
构建产物准备好之后,我写了一个部署脚本,把服务器上的发布步骤全部自动化了。
为了便于理解,我用一个最常见的“多台服务器+软链接切换版本”模式来说明。这种方式不需要复杂的编排工具,只需要rsync、ln和一点bash知识,却非常可靠:
#!/usr/bin/env bash set -euo pipefail APP_NAME="myapp" RELEASE_VERSION="$1" RELEASE_DIR="/opt/$APP_NAME/releases/$RELEASE_VERSION" CURRENT_LINK="/opt/$APP_NAME/current" BACKUP_TS="$(date +%Y%m%d%H%M%S)" BACKUP_DIR="/opt/$APP_NAME/backups/$BACKUP_TS" if [ "$#" -ne 1 ]; then echo "用法: $0 <版本号>" exit 1 fi if [ ! -d "$RELEASE_DIR" ]; then echo "错误: 版本目录 $RELEASE_DIR 不存在,请先构建产物" exit 1 fi echo "[1/5] 备份当前生产目录..." if [ -d "$CURRENT_LINK" ]; then mkdir -p "$BACKUP_DIR" cp -a "$CURRENT_LINK/" "$BACKUP_DIR/" fi echo "[2/5] 保留共享目录(上传文件、日志、临时目录)..." # 用 rsync 把新版本同步到发布目录,并排除不覆盖的内容 rsync -a --delete \ --exclude 'media/' \ --exclude 'logs/' \ --exclude '.env' \ "$RELEASE_DIR/" "$CURRENT_LINK/" echo "[3/5] 更新配置链接..." ln -sfn "$RELEASE_DIR/config/production.yaml" "$CURRENT_LINK/config/current.yaml" echo "[4/5] 执行数据库迁移(幂等)..." cd "$CURRENT_LINK" if [ -f "scripts/migrate.sh" ]; then bash scripts/migrate.sh fi echo "[5/5] 重启服务并健康检查..." systemctl restart "$APP_NAME" sleep 5 curl -fsS http://127.0.0.1:8000/healthz >/dev/null echo "发布完成: $RELEASE_VERSION"这段脚本里有几个很关键的心机设计:
- 备份放到独立目录并带时间戳,而不是覆盖同一个备份位置,这样一旦需要回滚,可以精确回到任何一次历史版本。
- 使用软链接
current指到当前发布目录,切换版本本质上是切换链接指向。这个设计让回滚变得极其简单:只需要把链接指回上一版本并重启服务。 - 共享目录(用户上传文件、日志)通过
--exclude排除在发布之外,防止新版本覆盖这些动态数据。 - 数据库迁移脚本必须写成“幂等”的——也就是可以反复执行而不出错的。因为自动化发布过程中,如果迁移中途失败,脚本会在修复后重试,而重试时迁移字段可能已经部分执行了。
3.4 配置分离与版本化管理
我遇到过很多次,上线的时候需要手工改生产服务器上的配置文件,改完之后也没有记录,下次想查某个配置为什么被改了,没人说得清。为了解决这个问题,我彻底改变了配置文件的管理方式。
首先,硬编码配置绝对不允许出现在代码里,不同环境的配置通过环境变量或者独立的配置文件注入。比如开发、测试、生产环境各有一份配置文件模板,放在项目的config/目录下,并用加密的方式存储密钥信息,避免敏感数据直接出现在明文文件里。
其次,应用启动的时候从环境变量或者指定的配置文件读取配置,而不是把配置写死在代码里。这样同一份代码,只需要替换不同的配置,就能以相同的方式运行在开发环境和生产环境。
这个思路在企业级系统上线时特别重要,我举个例子:在SAP S/4系统的资产期初导入上线的场景里,数据导入与系统切换是有严格的先后顺序的,而且“年末上线”和“年中上线”在期初数据导入的时点和口径上完全不同——年末上线要处理的是年度累计折旧与期末余额,年中上线则要把当年已计提的折旧也一并导入。这种复杂的数据口径,怎么能不出错?答案就是:把数据导入和校验的逻辑写成可重复执行的脚本,把上线时点和期初数据的入口都在脚本参数中管理起来,每次执行都有日志和结果校验,这样才可能保证“上线”这个动作本身是可追溯、可回滚的。
当然,我不是说所有项目都是SAP那种重量级系统,但“配置与代码分离”和“上线时点与数据口径要明确”这两条原则,放在任何系统里都完全适用。
3.5 快速回滚:上线最大的后盾
很多人不敢上线的根源是“怕出问题之后回不来”。所以我的部署流程里,回滚不是事后补救,而是发布流程的一部分,并且脚本化的回滚操作一定要在发布之前就准备好。
以刚才的软链接部署方式为例,回滚脚本就非常简单:
#!/usr/bin/env bash set -euo pipefail APP_NAME="myapp" CURRENT_LINK="/opt/$APP_NAME/current" BACKUP_TS="$1" BACKUP_DIR="/opt/$APP_NAME/backups/$BACKUP_TS" if [ ! -d "$BACKUP_DIR" ]; then echo "错误: 备份目录不存在: $BACKUP_DIR" exit 1 fi rsync -a --delete "$BACKUP_DIR/" "$CURRENT_LINK/" systemctl restart "$APP_NAME" sleep 5 curl -fsS http://127.0.0.1:8000/healthz >/dev/null echo "已回滚到备份: $BACKUP_TS"这个脚本的执行时间也就在一分钟左右,而且是全量恢复到备份时的状态,比手工回滚要省太多时间。这里的备份是全量文件备份,我也考虑过用增量或者镜像快照,但实测下来,在应用目录不算太大的情况下(比如几个GB以内),全量备份最可靠,逻辑最简单,回滚速度也足够快。
我个人的经验是:回滚越简单,上线时的心理压力越小;心理压力越小,操作反而越不容易出错。
4. 常见问题与排查技巧实录
从手工流程切到自动化流程,不是写几个脚本就算完了,实际运行中会遇到各种问题。我把常见的问题和排查方法整理了一下,这些差不多都是我从实际部署中踩坑踩出来的。
4.1 脚本换个机器就跑不通,为什么
这个问题很典型。我自己最早写部署脚本的时候,在A机器上跑得好好的,拿到B机器上就各种报错,后来发现核心原因主要有三个:
- 脚本里用了绝对路径,但是不同机器上的项目路径不一样。解决方法是脚本开头统一定义变量,尽量用相对路径或通过环境变量传入路径。
- 脚本依赖了当前用户的环境变量,比如
.bashrc里配置的PATH在非交互式Shell中不生效。解决方法是脚本开头显式设置PATH,并且写明Python等命令的完整路径。 - 脚本里用了Bash的某个高级特性,但目标机器上的Bash版本太老。所以脚本开头可以加
#!/usr/bin/env bash,同时避免使用太新语法。
我第一次被这个问题坑过之后,就养成了一个习惯:在每个脚本的头部加一个自检段落,检查关键命令是否存在,并用类似set -euo pipefail这种严格模式把所有隐性问题尽早暴露出来。
4.2 依赖安装时网络问题导致环境初始化失败
自动化脚本最怕的就是网络问题。特别是用pip或者npm安装依赖的时候,某个源不稳定、某个包下载超时,整个脚本就跑挂了。
我的做法是:
- 在脚本里配置镜像源,并给pip、npm设置超时和重试参数。
- 把依赖缓存目录持久化到本机,避免重复下载。Docker构建时注意层的缓存顺序,把依赖变化不频繁的层尽量放到前面。这里具体操作上,就是把
COPY pyproject.toml poetry.lock ./放在COPY . .前面,这样代码变了但依赖没变时,Docker会直接利用缓存层而不用重新安装依赖。 - 对于关键的基础镜像,提前拉取到本地,或者放到内网镜像仓库,避免每次都从外网拉取。
另外说一句,现在AI编程工具已经能帮你写大部分初始化脚本和部署脚本了(比如前阵子看到的Kimi Code桌面客户端这类工具),我试过用它们生成类似的Dockerfile和部署脚本,速度和格式都不错。但有一个前提:你自己必须理解脚本里的每一行是干什么的,工具生成的脚本还是要review一遍再上生产。
4.3 数据库迁移在自动化部署中怎么处理
数据库迁移是自动化发布里最让人紧张的部分,因为它的失败不像代码发布那样可以简单回滚。
说说我踩过的坑:有一次发布新版本,代码发布成功了,但迁移脚本执行到一半失败,数据库里有一张表加了一半字段。我当时的解决流程是:先检查迁移状态,确认失败的语句和影响范围,然后手动把已执行的语句回滚,再修复迁移脚本源码中的错误,重新跑。整个过程花了将近两个小时。
从那之后,我给自己立了一条规矩:迁移脚本必须写幂等,并且数据库迁移不是发布脚本“顺便执行”的事情,而是发布流程中单独的一步。它要有独立的日志输出、独立的结果校验,如果失败,整个发布流程要立即停止,而不是继续执行后续步骤。
4.4 版本回滚后数据不一致怎么办
代码回滚很简单,把软链接指回去就行了,但数据库回滚很难。因为数据库迁移通常只支持前向迁移,回滚需要额外写反向迁移脚本。更麻烦的是,新版本可能已经写入了新格式的数据,老版本代码并不能读取这些数据。
我在实际操作中对这个问题的态度是:能不回滚数据库,就尽量不回滚数据库。代码层面出了问题,优先考虑“向前修复”——也就是在新版本代码上直接打补丁修复,而不是回滚到旧版本。只有当问题非常严重且数据库没有被破坏的时候,才会考虑完整回滚。
给读者的建议是:在所有发布计划里,数据库回滚预案要单独写、单独验证,不要指望手工去处理。并且要明确区分“代码回滚”和“数据回滚”,这两种回滚的机制、时机、风险完全不同。
4.5 常见问题速查表
| 症状 | 可能原因 | 排查与解决 |
|---|---|---|
| 脚本在部分机器报“命令找不到” | PATH配置不一致,或命令未安装 | 脚本开头设置PATH,显示调用完整路径 |
| pip安装C扩展包失败 | 缺少编译器和系统动态库 | 在依赖安装前执行系统包安装步骤 |
| Docker构建缓存失效,每次全量重建 | COPY指令顺序不合理 | 先复制依赖清单文件,再复制源码 |
| 发布后服务启动失败但日志正常 | 健康检查路径或端口不对 | 用curl验证本地健康检查,分阶段定位 |
| 迁移脚本重复执行报错 | 迁移不是幂等的 | 重写迁移脚本,使用事务和状态判断 |
| 回滚后旧版本无法读取新数据 | 数据格式不兼容 | 梳理数据兼容性,优先向前修复而非回滚 |
这个表算是我实际运维中最常遇到的高频问题了,每一条都对应着一次或多次教训。排查思路上,我总结下来最重要的是两点:一是一定要有日志,脚本里每一步操作都要打印明确的进度和结果;二是永远不要跳过健康检查,服务重启之后如果没有做自检,你以为发布成功了,实际上可能已经挂了。
最后再分享一个实操的小技巧
做完这套流程之后,我最大的感受是:配置环境和上线的自动化,最耗时间的地方不是写脚本本身,而是梳理流程和统一团队协作方式。我个人习惯的落地顺序是:先花一个下午,把我们项目里所有的手动步骤全部列出来(包括“忘了做的步骤”也列出来),然后给每个步骤打上“必须做/可选/自动化”的标签,再按这个清单去写脚本和工具。
这套方案上线运行之后我又持续优化了几轮,现在分支机构或者新同事加入以后,给一台干净机器,克隆仓库执行一条命令,几分钟就能跑起完整环境。上线也一样,发布同事再也不用半夜盯着终端看了。如果你现在还在被“配环境一天,上线半天”折磨,不妨先从上手难度最低的脚本化开始,把流程先自动化起来,用着用着自然就知道下一步该怎么改造了。