前言:我为什么要折腾这套环境
几年前为了用Carla和ROS,我在自己电脑上折腾过Ubuntu双系统。但后续为了玩游戏,用Obsidian写笔记,以及各种专业软件的使用,感觉Win11还是友好的多。但很多项目开发要用到Linux,让其它人能快速使用我的项目要用到Docker,更不要说Win11杂乱的环境和路径,因此我考虑使用WSL+Docker+NAS这套方案,最终Win11做主力机,同时获得Linux的开发体验。
本文记录了我从零开始搭建这套「计算在Windows,开发在Linux,存储在NAS」的开发基础设施(infra)的全部过程。过程中踩了不少坑,比如 WSL2 虚拟磁盘膨胀、Codex CLI 字体乱码、cd的奇怪行为……希望我的记录能帮佬们少走些弯路,也给自己留一份可追溯的文档。
特别说明 :NAS 部分我计划在 6 月底正式部署,届时会更新到本文末尾的「NAS 实战」章节,目前先留白。还有,输入法的emoji是真难用,我得用
win + ;一个个去找,所以
理解 Docker 和 NAS 的网络模型
在动手之前,我们先要搞清楚IP、端口、Docker、NAS之间的关系。有一种简单的直觉比喻:IP 是大楼,端口是房间 但是我一开始会有个问题——容器算不算独立的大楼?外人怎么知道去哪个房间?经过梳理,我现在大致的理解如下:
- 宿主机是唯一的“大楼” ,拥有对外可见的 IP。
- Docker 容器是楼内的“独立隔间” ,只有内部私有IP,外人看不到。
docker run -p 8080:80就相当于在大楼前台贴一张指示牌:“访客请到 8080 接待窗口,我将带你到内部隔间 172.17.0.2 的 80 号服务”。
所以,外人永远只需要知道宿主机的IP地址和和对应的端口(8080),完全不需要知道容器的私有IP。这也为后续NAS的引入埋下伏笔:NAS 就是另一栋专门负责存储的“仓库大楼” ,通过NFS协议和宿主机相连,容器里的数据就能存到那个仓库里。
WSL2环境搭建,以及相关教程(特别是别放在C盘)
我的主力机是Win11,必须通过WSL2来使用Linux系统。但默认安装的Ubuntu会直接扔到C 盘,我很反感C盘冗杂的环境和不知道去哪找文件的“烦杂感”。于是我的目标是:把整个WSL 搬到D盘 。
1. 安装 Ubuntu 到 C 盘(临时)
wsl --install -d Ubuntu-24.04
2. 正式迁移到 D 盘(核心步骤)
# 彻底关闭 WSL
wsl --shutdown
# 查看当前发行版确切名字,比如 Ubuntu-24.04
wsl --list --verbose
# 导出为 tar 文件到 D 盘
wsl --export Ubuntu-24.04 D:\backup\ubuntu.tar
# 注销 C 盘里的原系统,释放空间
wsl --unregister Ubuntu-24.04
# 在 D 盘创建新目录并导入
mkdir D:\WSL\Ubuntu-24.04
wsl --import Ubuntu-24.04 D:\WSL\Ubuntu-24.04 D:\backup\ubuntu.tar
我看网上的教程好像默认登录的是 root,要先恢复默认用户,但我没遇到这个问题,如果你进去WSL终端后显示的是root,可能需要在 powershell 输入<发行版名字>.exe config --default-user 你创建的用户名恢复默认用户(我没试过,不确定)。
之后可以删掉 D:\backup\ubuntu.tar ,节省空间。
踩坑提醒 :WSL2 的
.vhdx虚拟磁盘文件只会自动增长,不会自动收缩。即使你在系统里删除了文件,Windows下看到的占用也不会变小。需要定期在PowerShell中执行:# 第一步:关闭 WSL wsl --shutdown # 第二步:压缩虚拟磁盘 optimize-vhd -Path "D:\WSL\Ubuntu-24.04\ext4.vhdx" -Mode Full
3. 让 wsl 命令直接进入Linux的~目录
迁移完成后,我发现一个问题:在 Windows 终端里直接敲wsl进入Ubuntu时,默认路径会停在/mnt/c/Users/xxx(Windows用户的目录),而不是我习惯的Linux ~。每次还要手动cd ~,很烦。
解决方法很简单——修改WSL的配置文件,让它启动时自动把当前目录切换到~:
sudo nano ~/.bashrc
在最后一行加入cd ~
然后让配置立即生效:
source ~/.bashrc
之后Ctrl+x退出并y保存,建议所有操作都wsl --shutdown关机后重新开机让配置生效。
我之前尝试过在 /etc/wsl.conf 的
[boot]节添加command = cd ~,但没用,所以不建议这样做。
4. 启用 ‘镜像模式’ 网络让 WSL2 复制主机的网络状态(使用VPN)
首先确认自己的主机系统是 Win11 22H2版本或更高版本(我是25H2,可以用winver确认);同时建议把WSL升级到最新,然后在Windows用户目录下(C:\Users\<你的用户名>\ )找到或新建 .wslconfig 配置文件(我当时没有,是新建的),输入以下命令:
[wsl2]
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true
之后在PowerShell中执行 wsl --shutdown 关闭所有WSL实例。然后重新打开终端后,配置就会生效。
检查配置是否生效可以在ubuntu中输入
curl -4 ip.sb,如果和开了vpn后的IP地址一致就说明成功了。
CLI 工具链——VS Code、Conda 和 Codex CLI
我不喜欢GUI,更喜欢CLI那种掌控感,因此所有配置都会走CLI。
1. VS Code 远程开发
为了充分使用 Linux 系统的快捷,我现在所有项目都在Ubuntu系统下完成,因此需要用到远程连接(切忌在 WSL 终端中访问并调用win下的文件,性能损失非常严重!)
在vscode安装好 WSL 扩展后,点击左下角><图标,选择「连接到 WSL」,VS Code 会直接在 Linux 环境里打开(注意服务器下载时间比较长)。集成终端就是纯净的 Ubuntu Bash,项目文件就放在 /home/用户名/ 下。
2. Miniforge (Conda) 环境
Windows 上装的 Conda 不能直接在 WSL 里用,需要在 WSL 内部重新安装 Linux 版:
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh
~/miniforge3/bin/conda init
注意安装miniforge3时有一步直接回车就行,不要输入enter,否则会默认将miniforge3安装到enter文件夹下,
~/miniforge3/bin/conda init会找不到,要删除enter文件夹后重新安装。
重启WSL终端后,输入python命令能进入base环境就成功了(默认应该是python3.13版本)。
之后就可以rm Miniforge3-$(uname)-$(uname -m).sh删除之前下好的安装包释放空间。
3. Codex CLI 与中转站 API
我在 Windows 下用过 cc-switch 来切换 API,想在 WSL 里继续使用。Codex CLI 本身需要 Node.js 22+(我安装的是24的LTS版本,改成nvm install 24就行),先用 nvm 安装:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
npm install -g @openai/codex
这时 codex 命令已可用,但要让它走API中转,需要 Linux 版的 cc-switch 。先确认系统架构是否匹配,在 WSL 终端里执行:
uname -m
然后去CC Switch的GitHub Releases页面寻找最新的安装包(我是x86_64版本),截至我写这篇教程时,最新是v3.15.0版本,即CC-Switch-v3.15.0-Linux-x86_64.deb。当然,你也可以选择 AppImage 格式,确认后下载:
# 记得自己去确认最新版安装包
wget https://github.com/farion1231/cc-switch/releases/download/v3.15.0/CC-Switch-v3.15.0-Linux-x86_64.deb
下载后,使用 dpkg 安装:
sudo dpkg -i CC-Switch-v3.15.0-Linux-x86_64.deb
安装完成后,直接在终端输入cc-switch并回车,就能启动它的GUI。由于Win11的WSL原生支持GUI,所以显示和win上的基本没有区别,接下来就可以在里面配置自己的API中转信息了。
我遇到的小坑和解法
坑1:CC Switch 界面全是空格子(乱码?)
运行 cc-switch 后,界面布满空白方格,原来是 WSL 默认没有中文字体 。安装开源中文字体即可解决:
sudo apt update
sudo apt install -y fonts-noto-cjk fonts-wqy-zenhei fonts-wqy-microhei
之后在 PowerShell 里 wsl --shutdown 重启,界面恢复正常。
坑2:cd ./.codex进不去,但cd .codex可以
我现在也不知道物理路径和逻辑路径的区别和方法,但我现在都是直接cd 文件名解决。
Docker 环境准备(CLI 原汁原味)
我还没装 Docker Desktop(可能以后也不会装),直接用原生 Docker Engine:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
新开终端后,docker run hello-world成功。后续数据卷暂时存在WSL虚拟盘里,等 NAS 到位后,再用NFS挂载 docker run -v /mnt/nas/...迁移出去。
surprise!又有坑了
我当时测试docker run hello-world时报错443: read: connection reset by peer,如果同样是这个错,而且错误日志里有一串2001:da8:...类似的IPv6地址,说明是IPv6或网络出口的问题,解决方法如下:
先考虑绕过 IPv6,在 WSL2 终端里输入:
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
这会临时关闭 IPv6。然后输入:
docker run hello-world
如果成功了,说明就是IPv6问题,如果还不行,我也不知道怎么办。
为了让它永久生效,sudo nano /etc/sysctl.conf,并在最后输入:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
保存后执行 sudo sysctl -p 立即应用,然后重启WSL后docker run hello-world查看是否生效(要恢复的话删除这两行命令就行了,而且实际上基本不会影响,除非你访问的是只有IPv6的网站)。
另外,有的WSL版本似乎不会自动读取/etc/sysctl.conf,可以在WSL终端输入:
sysctl net.ipv6.conf.all.disable_ipv6
如果返回=1说明IPv6就被禁用了,如果没有,则在/etc/wsl.conf (注意不是 sysctl.conf )中[boot]部分添加(记得要给管理员权限,即sudo nano):
[boot]
command = /sbin/sysctl -p /etc/sysctl.conf
NAS 实战(留白,6 月底更新)
目前规划:
- 一台独立小主机或旧电脑,安装 Ubuntu Server,用
fdisk、mkfs.ext4格式化数据盘。 - 通过 NFS 共享
/nas/docker-volumes和/nas/backups,设置all_squash保证权限不乱。 - WSL2 里用
mount -t nfs 192.168.1.x:/nas/docker-volumes /mnt/nas/docker-volumes挂载,并写进/etc/wsl.conf实现开机自动挂载。 - Docker 容器全部绑定挂载到
/mnt/nas/...,数据与宿主机解耦。 - 定期
rsync备份重要目录到 NAS,加上 cron 自动化。
NAS这块我先挖个坑,等6月底机器到位了再回来填。到时候会写清楚我怎么选硬盘、怎么配 NFS、怎么搞自动备份。说起来佬们能不能给点NAS购买和搭建建议?非常感谢~
2 个帖子 - 1 位参与者