1. 先诊断:慢在哪一层,别急着换源
“.NET SDK 下载缓慢”这件事,我遇到过太多次了,每次场景都不一样。有同事在新装机器上卡在安装引导程序第一步动都不动,有人的项目还原时几十个包一个个往外挤像挤牙膏,还有人 CI 跑一次流水线光拉 SDK 就吃掉十几分钟。表面看都是“慢”,根子却分属完全不同的三层:安装包本体下载、包还原、容器镜像拉取。如果你上来就找一个镜像地址往里一填,大概率只能治其中一层,另外两层照旧。
所以这篇东西的定位很明确:给已经被 .NET SDK 下载速度折磨过、想一次性把这条链路理顺的人看。不管你是刚装完系统准备搭环境的新手,还是维护几十个仓库还原流水线的老手,下面这套诊断加方案的组合都能直接拿去用。我不打算罗列一堆“加速技巧”,而是先讲清楚每一层慢的成因,再给你能落地的命令和配置,最后把踩过的坑整理成速查表。
1.1 三类下载场景,三种完全不同的慢
第一类是安装包本体。你从官网点下载按钮,拿到的是一个几百兆的安装文件。这个文件走的是内容分发网络,本质上是一次大文件传输。它的速度取决于三件事:域名解析出来的节点离你多远、你和节点之间的链路质量、以及单条连接能跑多快。很多情况下不是总带宽不够,而是单线程跑不满,200MB 的包单连接只有 200KB/s,算下来就是一千秒,接近十七分钟,中途抖一下还得重来。这类慢的特征很明显——进度条长时间不动或者极慢,但网络本身是通的。
第二类是包还原。dotnet restore会去包源拉取依赖,一个中型项目几十上百个包,每个包几 KB 到几 MB 不等。这类慢不是带宽问题,而是往返次数太多。每个包都要建立连接、协商、请求、下载、校验,单个包哪怕只慢两百毫秒,一百个包叠起来就是二十秒,再加上包源在境外,往返时延被放大,累计起来就非常难受。特征是还原日志里一行一行慢慢滚,最后卡在某个包上很久。
第三类是容器镜像与安装器组件。容器基础镜像动辄几百兆,还是分层拉取;安装器本体很小,但它会在安装过程中按需拉取一堆组件包。这两者的共同点是:你以为在下一个东西,其实在下一串东西,任何一个子项慢,整体就卡住。特征是进度显示在某一步长时间停留,日志里能看到在反复请求不同的地址。
把这三类分清楚,后面的方案才不会乱用。我见过有人为了还原慢去换安装包源,折腾半天毫无效果,就是因为搞错了层。
1.2 一组能直接复制的诊断命令
诊断这件事不需要复杂工具,几条命令足够。第一步先看当前环境:
dotnet --info dotnet --list-sdks--info会告诉你运行时版本、操作系统、RID(运行时标识符,比如linux-x64、win-x64),这个值在你后面手动拼下载链接时至关重要,拼错了下下来的包根本装不上。--list-sdks则是排查“装了但用的不是它”这类问题的第一选择,很多时候你以为在下载新版本,其实是老版本还在 PATH 里挡着。
第二步测链路。想看清一个下载地址到底慢在哪一段,用 curl 的格式化输出最直观:
curl -o /dev/null -s -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} first:%{time_starttransfer} speed:%{speed_download}\n" \ -L "https://<下载地址>"这四个数字分别代表:域名解析耗时、建立连接耗时、加密握手耗时、首字节到达耗时,最后是平均下载速度。如果time_namelookup就很大,说明解析到了不合适的节点;如果time_connect大,是链路往返时延的问题;如果前面都很快但speed_download很低,那就是单连接被限速,这时候多线程分段下载才有意义。我一般会对着同一个地址跑两三次,取中间值,避免偶发抖动误导判断。
第三步看包还原的具体耗时。把还原日志开到最详细:
dotnet restore --verbosity detailed日志里会逐个打印包的下载过程。你只需要扫一眼,看是“所有包都慢”还是“某几个包特别慢”。前者是源的问题,后者往往是个别包体积大或者该源上缺这个包导致回退查询。这一步做完,再决定改哪里,能省掉大量无效折腾。
注意:诊断阶段不要一边开着构建一边测速,编译器占满 CPU 时测出来的下载速度会失真,容易得出错误结论。
2. 方案选型:五条路各自适合谁
把成因摸清之后,可选的路其实就五条:换镜像源、抓直链用外部下载器接力、拉离线包做本地分发、铺缓存减少重复下载、把大任务拆小并行处理。这五条不是互斥的,实际用起来往往是两三条组合,但每条都有它最适合的场景,用错了地方收益很小。
2.1 换源、抓直链、拉离线包、铺缓存、分治
换镜像源解决的是“往返太多”的问题,也就是第二类慢。它的原理很简单:把包请求指向地理上更近、带宽更充足的镜像站点,单次往返时延从几百毫秒降到几十毫秒,一百个包叠起来能省掉一大半时间。它的局限是只对包还原有效,对安装包本体下载基本没帮助——因为安装包不走包管理器。另外镜像站有同步延迟,刚发布的预览版包通常镜像上还没有,所以配置时最好保留官方源作为后备,别一刀切。
抓直链 + 外部下载器解决的是“单连接跑不满”的问题,也就是第一类慢。官方安装脚本支持只打印不下载的模式,你把真实下载地址抠出来,丢给支持多线程分段的下载工具,八条连接同时跑,速度往往能翻几倍。这条路的好处是不依赖任何第三方镜像,地址永远是官方的,风险最低。缺点是它是一次性的,下次换版本还得重来一遍,适合装机、搭环境这种低频场景,不适合天天跑的流水线。
拉离线包是把“下载”这件事从“每次都要”变成“一次搬运,多次安装”。你在一台出口条件好的机器上把安装包、依赖包、安装器组件全下齐,打包拷到目标机器或内网文件服务上,之后所有人从本地取。这条路适合团队协作、内网环境、批量部署,前期成本高一点,但一次投入长期受益。它是唯一能彻底绕开外网波动的方案。
铺缓存针对的是“同样的东西反复下”。包还原的缓存分两级:全局包目录(解压后的包本体)和 HTTP 缓存。默认情况下dotnet restore会先查全局包目录,命中就不联网。所以只要机器上的包目录不丢,第二次还原几乎瞬间完成。问题出在 CI 和容器里——每次都是全新环境,缓存是空的。解决办法是把缓存目录挂载出来或者用流水线的缓存能力持久化。
分治是工程层面的思路:把大镜像拆成多阶段构建、把一次大还原拆成多次小还原利用层缓存、把串行的下载改成并行。它不改变单次下载的速度,但能减少总的下行数据量,在带宽受限的环境里效果显著。
2.2 一张对照表决定先做哪件事
| 你的症状 | 主要成因 | 优先动作 | 预期收益 |
|---|---|---|---|
| 安装包进度条几乎不动 | 单连接限速 | 抓直链 + 多线程下载器 | 明显,常见数倍提升 |
| 还原日志一行行慢慢滚 | 往返次数多 | 配置镜像源 + 保留官方后备 | 明显,大项目收益更大 |
| CI 每次都要重下 | 缓存未持久化 | 挂载全局包目录 / 流水线缓存 | 极大,命中后接近零等待 |
| 容器构建每次拉全量 | 分层未利用 | 多阶段构建 + 先还原后拷源码 | 明显,重复构建几乎免费 |
| 装完发现版本不对 | 多版本冲突 | <code>global.json</code>锁定 + 检查 PATH | 不提速但避免返工 |
这张表我建议你对着自己的实际情况先勾一行,只做一件事,做完再测一次。一次改太多,出了问题都不知道是哪一步引入的。
3. 实操一:用官方安装脚本把下载控制权拿回来
很多人装 SDK 的习惯是点官网的下载按钮,拿到一个安装引导程序,双击,然后看着它自己去拉。这个过程你几乎没有任何控制权——不知道它在拉什么、从哪拉、拉多少。官方提供的安装脚本则完全相反,它把每一步都摊开给你看,还能只打印不执行。这是我推荐的首选工具,尤其适合 Linux 服务器和需要精确控制版本的环境。
3.1 脚本参数逐个拆开讲
先拿脚本。类 Unix 系统用 shell 版,Windows 用 PowerShell 版:
curl -fsSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh chmod +x dotnet-install.shInvoke-WebRequest https://dot.net/v1/dotnet-install.ps1 -OutFile dotnet-install.ps1参数里最关键的几个,我按使用频率排一下:
--channel/-Channel:指定版本通道,比如LTS、STS,或者具体大版本号如8.0。用通道的好处是自动拿该通道最新的补丁版本,不用记具体小版本号。--version/-Version:精确到补丁号,比如8.0.100。生产环境我建议用这个,配合global.json锁死,避免哪天脚本自动升到新补丁引入行为差异。--install-dir/-InstallDir:安装目录。默认会装到用户目录下的.dotnet,如果你要装到系统级目录就得显式指定,同时注意权限。--dry-run/-DryRun:只打印将要执行的下载地址和安装路径,不实际下载。这是整条链路里最有价值的一个参数,下面单独讲。--architecture/-Architecture:指定架构,x64、arm64等。交叉编译或者跑在 ARM 服务器上时必须显式给,脚本自动探测偶尔会判断错。--runtime/-Runtime:只装运行时,不装 SDK。部署机器上用它,包体积小很多。--no-path/-NoPath:不自动修改 PATH 环境变量。容器里或者你想自己控制环境变量顺序时用。--azure-feed/-AzureFeed:指定下载来源根地址。这是接镜像源的入口,具体地址填镜像站提供的根路径即可。--skip-non-versioned-files:跳过那些不带版本号的辅助文件下载。在只需要 SDK 本体的场景下能少几次请求。
还有一个容易被忽略的行为:脚本下载的压缩包会先落在安装目录下的一个隐藏缓存目录里,如果同名同大小的文件已经存在,它会直接跳过下载进入解压。这意味着你可以手动把下载好的包放进那个目录,脚本就不会联网了。这是离线安装的关键技巧,后面会用到。
3.2 完整流程:dry-run 拿链接、外部下载器接力、本地落盘
这套流程我在几十台机器上重复过,稳定可靠。第一步,先跑一次 dry-run 看清楚它要下什么:
./dotnet-install.sh --channel 8.0 --dry-run --verbose输出里会打印出完整的下载地址,形如某个以版本号和平台标识结尾的归档文件名。把这个地址记下来。第二步,用外部下载器接力。我常用的是支持分段并发的下载工具,八条连接、断点续传:
aria2c -x 8 -s 8 -c -d ./cache -o dotnet-sdk.tar.gz "https://<dry-run 打印出的地址>"如果你手头没有这类工具,curl 也能做断点续传,虽然只有单连接,但至少中途断了不用从头来:
curl -C - -L -o ./cache/dotnet-sdk.tar.gz "https://<地址>"第三步,校验完整性。这一步千万别省,下载中断或者被中间设备改写都会导致解压失败,而你很难第一时间反应过来是包坏了:
sha512sum -c dotnet-sdk.tar.gz.sha512官方一般会提供同名的.sha512校验文件,配合-c参数一条命令搞定。Windows 上可以先把期望的哈希值拷出来,再用Get-FileHash -Algorithm SHA512比对。
第四步,把包放到脚本的缓存目录,再跑一次正式安装。脚本检测到文件已存在就会跳过下载,直接解压:
./dotnet-install.sh --version 8.0.100 --install-dir "$HOME/dotnet" --no-path装完记得把路径接上。DOTNET_ROOT这个变量要指向安装根目录,PATH 里加上它,顺序放在系统默认路径前面,否则会被老版本抢走:
export DOTNET_ROOT="$HOME/dotnet" export PATH="$HOME/dotnet:$PATH"注意:
DOTNET_ROOT只在某些场景下被读取(比如应用宿主启动时),但设上它没有坏处。真正决定命令走哪个二进制的是 PATH 顺序,所以which dotnet和dotnet --list-sdks这两个检查别偷懒。
关于速度的账可以算一下:假设安装包 200MB,单连接 200KB/s,需要约 1000 秒;换成八线程、每线程 1.2MB/s、合计约 9.6MB/s,只需约 21 秒。这就是为什么“抓直链”这一步在低频装机场景里收益最大。但也要注意别把线程数开到几十,很多服务端会对异常并发做限速,八到十六是个比较稳妥的区间,超过之后边际收益迅速下降。
4. 实操二:离线包与本地布局,一次搬运到处安装
前面那套流程解决的是单机问题。如果你要给一个团队、一个内网环境、或者一批没有外网出口的机器准备 .NET SDK,逐个抓直链就太低效了。这时候该上离线方案:把要用的东西一次性下齐,打成一个大包或放到内网文件服务,之后所有安装都从本地取。
4.1 Linux 与 macOS 的 tar.gz 二进制包
类 Unix 系统的 SDK 分发形式是压缩包,解压即用,不写注册表、不改系统目录,天然适合离线分发。做法是:在一台网络条件好的机器上,把目标版本、目标架构的压缩包和对应的哈希文件都下下来,放到共享目录。目标机器上执行:
sudo mkdir -p /opt/dotnet sudo tar zxf dotnet-sdk-8.0.100-linux-x64.tar.gz -C /opt/dotnet sudo ln -sf /opt/dotnet/dotnet /usr/local/bin/dotnet然后配置环境变量。系统级可以写进/etc/profile.d/dotnet.sh:
export DOTNET_ROOT=/opt/dotnet export PATH=$PATH:/opt/dotnet这里有个细节值得说:软链接和直接改 PATH 两种方式我都试过,软链接的好处是全局生效、不依赖 shell 配置文件加载顺序,坏处是当你需要同时保留多个版本时不好切换。多版本共存的推荐做法是每个版本一个目录,用global.json在项目层面指定,或者用符号链接切换“当前默认版本”。
global.json长这样,放在解决方案根目录:
{ "sdk": { "version": "8.0.100", "rollForward": "latestFeature" } }rollForward的含义是:如果本机没有正好8.0.100,允许向前滚动到同一特性带内的最新补丁。latestFeature比较宽松,disable则要求严格匹配。团队协作我建议用latestFeature,既能保证大版本一致,又不会因为某个人少装一个补丁就卡住。
再补一个跨平台的解压细节:Windows 10 以后系统自带了 bsdtar,所以.tar.gz不用装第三方工具,直接在 PowerShell 里tar -xzf就行。这一点在只能上内网、装不了新软件的机器上特别有用。
4.2 Windows 安装器与离线布局
Windows 上如果要走离线,思路是“把安装器需要的组件提前拉全”。安装器普遍支持布局模式:你给一个目录,它把本次安装涉及的所有文件按结构下载到本地,之后从本地目录安装就不需要外网了。典型命令形态是给一个输出目录,加上要包含的工作负载标识和语言参数,让它把整个文件树拉下来。
流程上分两步。第一步,在一台出口顺畅的机器上执行布局下载,目录选在有足够空间的盘上,这类布局动辄几个 GB。第二步,把整个目录打包或直接通过共享盘拷到目标环境,跑安装器时指向该目录作为来源。
这里有几个实打实的坑。坑一,组件标识写错。工作负载和组件都是长串标识符,凭记忆写必然出错,正确做法是先让安装器输出一次清单,从清单里找到精确名称再填。坑二,语言参数漏了。不指定语言时,布局里可能只包含默认语言资源,目标机器是中文环境就会额外去拉语言包,又变成联网了。坑三,布局目录不能随意改动结构,里面的清单文件记录了每个文件的相对路径,移动或重命名子目录会让安装器校验失败。
提示:布局目录做完之后,先在一台断网机器上完整跑一遍安装,确认没有遗漏再去铺开。我吃过一次亏——布局看似完整,实际少了某个运行时组件,等到批量部署完成才在业务启动时报错,回头补的成本高得多。
离线方案的价值不在单次速度,而在于确定性。外网条件再好也会有抖动,而离线包一旦验证通过,装一百台和装一台的结果完全一致。对于需要长期维护多个版本并存的团队,我建议把常用版本的离线包和哈希值统一归档,配合一个简单的版本清单文档,谁要装什么直接取,避免每个人各自去官网各下一遍。
5. 实操三:NuGet 包还原提速,镜像源与缓存两手抓
前面聊的都是“装 SDK”,这一节聊“用 SDK”。真正天天折磨人的其实是包还原——写代码的时候一天要还原几十次,每次多花三十秒,一天就没了。这块的优化空间比装 SDK 大得多,因为它有缓存可以复用,也有源可以换。
5.1 NuGet.config 的层级、优先级与写法
配置文件是分层的,从近到远大致是:项目或解决方案目录下的配置文件、用户级配置、机器级配置。查找时从当前目录往上逐级找,越靠近项目的优先级越高,但多个源是合并的,不是简单覆盖。所以经常出现这种情况:你在项目里加了一个镜像源,结果还原时还是先去请求官方源,因为用户级配置里的源依然在列表里排在前面。
要彻底解决就得用清空标签。下面这份配置可以直接抄:
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="mirror" value="https://<镜像站地址>/v3/index.json" /> <add key="official" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> </packageSources> <config> <add key="globalPackagesFolder" value="D:\nuget\packages" /> </config> </configuration><clear />会清掉所有继承来的源,只保留你显式写的这些。这一步很多人不敢加,怕影响其他项目——但配置文件是分层的,这个文件只影响它所在目录及以下的项目,别的仓库不受影响。加上它之后,还原行为的确定性会好很多。
源的顺序也有讲究。还原时如果多个源都包含同一个包,行为是按顺序尝试,先命中先返回。所以把镜像源放前面、官方源放后面,正常情况走镜像,镜像上没有的包(比如刚发布的预览版)自动回退到官方,两边的好处都占。
配好之后不用手动改文件也能加源,命令行更省事:
dotnet nuget add source "https://<镜像站地址>/v3/index.json" -n mirror dotnet nuget list sourcelist source能看到当前生效的所有源和它们来自哪个配置文件,排查“为什么走了这个源”时非常有用。删源用dotnet nuget remove source <名称>,改完不生效就检查是不是用户级配置里还留着一份。
5.2 全局包目录搬家与离线包仓库
全局包目录是还原提速的核心。它存放的是解压后的包本体,还原时先查这里,命中就不联网。默认位置在用户目录下的.nuget/packages。这个目录有三个值得动手的地方。
第一,搬到大容量固态盘。默认在系统盘,包攒到几十 GB 之后既占空间又拖慢系统盘。搬家有两种方式:改配置文件里的globalPackagesFolder,或者设环境变量NUGET_PACKAGES。环境变量优先级更高,适合在 CI 里临时指定;配置文件适合长期固定。搬家之后旧目录里的缓存不会自动迁移,手动拷过去即可,包目录结构是内容寻址的,直接复制不会出问题。
第二,定期清理而不是无脑清空。清理命令有两组:
dotnet nuget locals all --list dotnet nuget locals http-cache --clear第一组告诉各个缓存目录在哪,第二组只清 HTTP 缓存。这个区分很重要:HTTP 缓存是下载过的包文件副本,清掉不影响已解压的包;而清全局包目录会把解压好的包全删了,下次还原得全部重下。所以日常维护我通常只清 HTTP 缓存,全局包目录靠定期整体迁移来腾空间,不做增量删除。
第三,做一份离线包仓库。把项目依赖的包按版本导出到本地目录,之后还原时指定本地目录作为源:
dotnet restore --source ./offline-packages配合--packages参数可以把还原下来的包统一放到指定位置,形成一个可拷贝的仓库。这在无外网环境、或者要保证构建可复现(依赖必须来自固定快照)的场景下几乎是必选项。导出时注意把传递依赖也带上,只导直接依赖会在目标环境还原失败。
5.3 容器与 CI 里的缓存策略
容器构建这块,最常见的错误是把复制源码和还原包写反了。正确顺序是利用层缓存:先只复制项目文件和解决方案文件,执行还原,再复制其余源码。这样只要依赖清单没变,还原层就能直接复用,构建时间从几分钟降到几秒。
FROM <基础镜像>/dotnet/sdk:8.0 AS build WORKDIR /src COPY *.sln ./ COPY src/App/App.csproj src/App/ RUN dotnet restore COPY . . RUN dotnet build -c Release --no-restore要点有三个。COPY只拿清单文件,让还原层的输入足够小,源码改动不会让缓存失效。--no-restore告诉构建别再还原一遍,否则前面那次白做了。还原命令里可以加--source指向镜像源,但要注意基础镜像里可能已经有一份默认配置,最好显式覆盖,别指望继承。
CI 流水线里思路类似,只是缓存介质换成流水线自带的缓存能力。缓存键要把依赖清单文件的内容哈希算进去,清单变了缓存自然失效,清单没变直接命中。这里有个容易踩的坑:缓存目录要包含全局包目录和HTTP 缓存两层,只缓存一层会导致命中率打对折。另外多分支并行构建时,缓存键最好带上分支或依赖清单哈希,避免不同分支互相污染——包版本不同的时候拿到别人的缓存,会出现还原成功但版本不对的诡异问题。
6. 常见问题与排查速查表
技术方案写得再细,落地时总会撞上一些边角情况。这一节我把这些年实际遇到过的整理成表,遇到问题先对着表过一遍,多数能直接定位。
6.1 典型现象对照处理
| 现象 | 大概率原因 | 处理动作 |
|---|---|---|
| 脚本卡在第一步不动 | 脚本本身下载受阻 | 手动下载脚本文件,本地执行 |
| dry-run 打印正常但实际下载极慢 | 单连接被限速 | 拷链接到多线程下载器接力 |
| 装了新版本但命令走老的 | PATH 顺序被旧路径占用 | 检查which dotnet,调整 PATH 顺序 |
| 还原报找不到源 | 源地址写错或网络不可达 | dotnet nuget list source逐个验证 |
| 镜像源上缺某个包 | 同步延迟或该包未收录 | 保留官方源作后备,按顺序回退 |
| 还原成功但构建报缺包 | 包版本被传递依赖改写 | 看obj/project.assets.json的实际解析结果 |
| 清理缓存后更慢了 | 误清了全局包目录 | 只清 HTTP 缓存,包目录迁移而非删除 |
| 容器构建每次都全量还原 | 复制顺序写反 | 先拷清单还原,再拷源码构建 |
| 多版本并存时行为不一致 | 未锁定 SDK 版本 | 加global.json,团队统一 |
| 解压或安装时报校验失败 | 文件下载不完整 | 用官方哈希值重新校验,重下 |
这张表里最值得多说一句的是“还原成功但构建报缺包”。这个现象很迷惑,因为在包管理器层面一切正常。根因通常是依赖解析做了版本提升:你直接引用的 A 包依赖 B 的 1.0,另一个 C 包依赖 B 的 2.0,最终解析结果只保留一个版本,而某个 API 在新版本里改了签名,编译就炸了。排查方法是打开项目中间产物目录下的资产文件,搜索这个包的名称,看最终锁定的是哪个版本,再回头检查是哪个依赖把它拉高的。
6.2 几个文档里不会写的细节
别用版本号命令判断装没装对。dotnet --version输出的只是当前目录下global.json解析出来的版本,如果目录里没有该文件,输出的是“最新的那个”。要确认到底有哪些可用,永远用dotnet --list-sdks,它列的是全集。
下载工具的并发数不是越高越好。我试过把并发开到三十二,结果速度反而比八条时下降了,还出现了几次连接被重置。原因很直接:服务端和中间链路都会对单 IP 的并发连接数做限制,超了就是白费。八到十六是比较稳妥的区间,而且这个值跟包大小有关——几十兆的小包开八条连接,分段的开销可能比收益还大,单线程反而更快。
镜像源要定期验证,不能配完就不管。镜像站的内容是异步同步的,偶尔会出现某些版本缺失或者校验不一致。我的做法是在流水线里加一个轻量的探测步骤:还原一个固定的小包,如果失败就自动切换到官方源重试。这样既享受了镜像的速度,又不会因为镜像侧的问题卡住整个构建。
离线包目录要带上哈希清单。文件拷来拷去很容易损坏,尤其是经过压缩、分卷、网盘中转之后。建议在离线目录里附一份所有文件的哈希清单,安装前先整体校验一遍。这一步多花三十秒,能避免那种“装完了、跑起来、报一个莫名其妙的错、排查两小时”的情况。
环境变量和配置文件的优先级要背下来。命令行参数高于环境变量,环境变量高于配置文件,项目级配置高于用户级配置。这条规则贯穿整个工具链,遇到“明明改了却不生效”的问题,按这个顺序往上找,基本都能找到那个覆盖点。
记录一次成功的最小路径。你在某台机器上折腾通了,一定要把当时的命令、参数、版本号、配置文件原样存一份。半年后再装另一台,你会庆幸自己存了这份东西——工具链的默认行为会变,版本会变,但你自己验证过的那条路径是最可信的参照。
最后分享一个我个人一直在用的做法:给团队维护一份轻量的环境准备清单,里面只有三行——脚本下载地址、目标版本号加哈希值、镜像源配置片段。新机器照着三行走一遍,十分钟内能装完并跑通一个最小项目。这份东西比任何长篇文档都管用,因为它只保留经过验证的动作,没有任何需要临场判断的地方。环境准备这种事,确定性比技巧重要得多。