在Windows上折腾开发环境,最让人上火的不是代码写不出来,而是环境装不起来。以前我为了在本地跑Linux环境,装过虚拟机、搞过双系统,每次换来换去都觉得很别扭。直到把WSL 2和Docker Desktop这套组合彻底玩明白之后,Windows下的开发体验才真正顺了。这篇文章不聊虚的,就把我从零开始安装WSL子系统、配置Docker、日常使用以及踩坑排错的全过程写下来,希望能让刚上手的同学少走弯路。无论你是做后端开发、运维,还是经常要碰Redis、MySQL、Elasticsearch这些中间件,只要日常主力机器是Windows,这套方案都能让你的开发环境快速Linux化,而且不污染主系统。下面所有命令我都亲自实测过,按顺序操作基本就能一次跑通。
1. WSL是什么,为什么说它是Windows开发的标配
1.1 WSL 1和WSL 2的区别,以及我为什么推荐WSL 2
WSL全称Windows Subsystem for Linux,也就是Windows的Linux子系统。它不是一个虚拟机,也不是一个模拟器,而是微软在Windows内核里实现的一个兼容层(WSL 1),或者一个定制过的轻量虚拟机(WSL 2)。简单说,它让你在Windows上能原生运行Linux命令行工具、脚本,甚至是完整的Linux发行版,而不用单独去装VMware或者VirtualBox。
WSL 1和WSL 2最大的区别在于实现内核的方式。WSL 1走的是翻译层路线,把Linux的系统调用翻译成Windows的系统调用,好处是启动快、内存占用小,但兼容性有硬伤,很多依赖Linux内核特性的软件直接跑不动。WSL 2则干脆跑了一个经过微软深度定制的真正Linux内核,只是把它封装在一个轻量虚拟机里,因此兼容性大大提升,Docker这类重度依赖内核能力的工具只能在WSL 2上正常工作。实际用下来,WSL 2的启动速度依然很快,冷启动也就一两秒,日常开发完全感知不到后台有个轻量虚拟机在运行。
我自己是强烈推荐直接上WSL 2的。除非你有极其特殊的老项目,必须依赖WSL 1的文件系统行为,否则新装环境一律选WSL 2,这点在后面配置Docker的时候会体现得更明显,因为Docker Desktop对WSL 2的集成是最完整的。
1.2 什么场景适合WSL 2 + Docker这套组合
这套组合最典型的应用场景就是本地开发需要Linux环境。比如你要跑Redis、MySQL、Elasticsearch、GitLab这些中间件,在Windows上直接装原生版本多多少少会有兼容问题,很多老版本中间件在Windows上要么跑不起来,要么有性能损耗。用WSL 2里的Docker来跑,既能享受Linux环境的完整兼容性,又不影响Windows主系统的干净整洁。我自己的习惯是:宿主机只装必要的开发工具,所有数据库和中间件全部用容器跑,出问题直接重建容器,完全不污染系统。
但这套方案也不是万能的。如果你需要图形界面的Linux桌面,或者需要大量直连USB设备、做嵌入式串口调试,WSL 2用起来就比较别扭。CUDA开发虽然WSL 2现在也支持了,但配置起来还是有门槛的,不建议新手一上来就挑战。WSL 2适合的是命令行工作流,而不是替代一台完整的Linux桌面机。需求判断清楚了再动手,能省掉后面很多无用功。
2. WSL安装全流程:从环境检查到跑通Ubuntu
2.1 安装前先检查这3件事
第一件事:确认Windows版本。WSL 2要求Windows 10版本号不低于2004(build 19041),或者直接用Windows 11。怎么查?Win+R输入winver回车,看窗口里的版本号。如果你是Windows 10老版本,我建议先把系统更新全部做完再继续,省得到时候各种WSL命令报“当前版本不支持”的错误。
第二件事:确认CPU虚拟化是否开启。打开任务管理器,切到“性能”标签页,看左下角“虚拟化”那行是不是“已启用”。如果显示“已禁用”,需要进BIOS开启Intel VT-x(Intel平台)或者SVM Mode(AMD平台)。这一步非常关键,因为WSL 2本质上是虚拟机,没有虚拟化支持根本起不来。我见过不少人的Docker Desktop报错,追根溯源都是这一项没开。
第三件事:确认Windows功能组件完整。在“启用或关闭Windows功能”里找到“适用于Linux的Windows子系统”和“虚拟机平台”这两项,确保都处于勾选状态。勾选之后需要重启系统才能生效。经常有朋友在这步漏掉“虚拟机平台”,导致后面Docker Desktop启动一直报虚拟化相关错误,这个问题我在第4节还会详细展开。
2.2 使用wsl --install一键安装
如果你的Windows版本比较新,最快的方式就是管理员身份打开PowerShell,直接运行:
wsl --install这条命令会自动完成三件事:启用需要的Windows功能(WSL和虚拟机平台)、下载并安装默认的Linux发行版(一般是Ubuntu)、把默认WSL版本设置为2。整个过程如果网络状况好,大概5到10分钟。装完之后系统会提示你设置Linux用户名和密码,这个用户名跟Windows用户名可以不一样,自己记住就行。
如果想指定发行版,可以先用下面这条命令看有哪些可选:
wsl --list --online然后安装你需要的版本,比如:
wsl --install -d Ubuntu-22.04我个人一直用Ubuntu 22.04 LTS,LTS版本维护周期长、踩坑资料多,后面换源、装工具都方便。如果公司项目对系统版本有要求,再考虑Debian、openSUSE这些,安装方法一模一样。注意,第一次进入Ubuntu时会让设置一个非root用户,日常操作尽量用这个普通用户,需要提权时用sudo,不要所有事都用root,养成好习惯能避免很多权限相关的坑。
2.3 安装慢、下载卡住的经典解决方案
这是被问得最多的问题,没有之一。wsl --install或者wsl --update下载很慢,甚至一直卡在某个进度不动。原因很简单,安装包要从微软服务器下载,部分地区到微软服务器的网络链路不稳定,这是客观存在的现象,不是你操作有问题。
比较靠谱的解决办法有几个。第一个是用离线安装包,去微软官网下载适用于x64的WSL 2内核更新包(wsl_update_x64.msi),手动安装完之后再运行wsl --set-default-version 2,系统就不会再走那个慢吞吞的下载流程了。第二个方法是先把Windows系统更新全部打满,有些所谓“下载慢”其实是因为系统组件不全,更新完以后再重试wsl --install往往就正常了,这个情况我在两台机器上都遇到过。
另外一个小技巧:如果机器之前装过WSL,后来卸载过,再装的时候经常遇到老配置残留的问题。这时候先运行wsl --shutdown,再运行wsl --unregister 发行版名把旧环境清理干净,然后重新安装,能避免很多莫名其妙的报错。安装过程中不要去反复开关机,让它自己跑完就行。
3. WSL日常配置与使用技巧
3.1 换源、更新与基础环境配置
Ubuntu装好后第一件事永远是换源和更新。为什么换源?因为apt默认用的是Ubuntu官方源,访问速度上确实不稳定,换成国内镜像源之后,下载软件包的速度能快好几倍。操作很简单,先备份原来的源列表:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak然后把sources.list里的地址替换成镜像地址。以Ubuntu 22.04为例,我一般直接用编辑器打开文件,把archive.ubuntu.com整体替换成mirrors.aliyun.com,也可以顺手把security.ubuntu.com一起替换掉。改完以后执行:
sudo apt update && sudo apt upgrade -y这里提一句:apt upgrade升级过程中如果看到linux-image相关的包更新,不用慌,WSL 2用的是微软单独提供的Linux内核,这些apt包一般不会被实际加载,不会把WSL搞坏。
基础环境里还有两样东西是必备的:build-essential和git。前者是编译工具链,装各种需要源码编译的工具时离不开;后者是日常开发的基本工具,不解释了。
sudo apt install -y build-essential git顺便把locale也检查一下,如果发现中文显示问号或者乱码,装个中文字体就能解决:
sudo apt install -y fonts-noto-cjk3.2 Windows和WSL之间文件互访
文件互访是WSL使用中非常高频的操作。WSL 2里访问Windows的文件系统,路径是/mnt/c、/mnt/d这样的格式,比如C盘根目录就是/mnt/c。反过来,从Windows里访问WSL的文件,在资源管理器地址栏输入\wsl$\或者\wsl.localhost\,就能看到你的WSL发行版目录,可以直接像普通文件夹一样拖拽文件。
这里有一个性能上的坑必须提醒:跨文件系统读写性能很差。如果你的项目代码放在Windows文件系统(比如D:\projects\demo),然后在WSL里编译运行,文件操作会慢到让人怀疑人生。为什么?因为/mnt/c这种访问方式本质上是9P协议在Windows和Linux之间做文件转发,每次读写都有额外损耗。正确的做法是把项目代码放在WSL自己的文件系统里,也就是~/projects这类位置,然后在Windows的资源管理器里通过\wsl$路径去访问和编辑。这样Windows侧用VS Code打开的是同一个文件,WSL侧编译运行也是本地速度,两侧体验都不差。
如果你希望WSL默认不要自动挂载Windows盘,可以在/etc/wsl.conf里配置:
[automount] enabled = false改完运行wsl --shutdown,再重新进入WSL就生效。
3.3 在VS Code里连接WSL写代码
这是WSL体验里最舒服的一环。在Windows上安装VS Code,再安装微软官方的“WSL”扩展,然后在WSL终端里进入你的项目目录,运行:
code .VS Code会自动以远程模式连接到WSL,打开项目文件夹。左边看到的文件树、终端、调试器全都是WSL里的,但界面还是在Windows上,体验和本地开发几乎没区别。这个模式叫Remote Development,微软官方出品,实测下来很顺手,我日常写Go和Python项目都靠它。
为什么要这么干?因为这样你的编辑器和终端访问的是同一套文件系统,代码编辑在Windows侧,编译运行在Linux侧,避免了两边文件不同步、权限不一致的问题。我在这套模式下连续工作大半年,几乎没有遇到卡顿或断连。如果你第一次运行code .没反应,先确认VS Code里装的是“WSL”扩展而不是“Remote - SSH”,这两个容易混淆。
3.4 多个发行版管理与默认版本切换
WSL支持同时装多个发行版。装第二个发行版和第一个一样,wsl --install -d Debian就能装Debian。查看当前所有发行版的状态:
wsl --list --verbose输出里会显示每个发行版的名称、状态和WSL版本,比如Ubuntu-22.04显示为2。如果你希望所有新装的发行版默认使用WSL 2,运行:
wsl --set-default-version 2如果想切换某个发行版的WSL版本:
wsl --set-version Ubuntu-22.04 2注意:从WSL 1切到WSL 2需要一次转换过程,耗时取决于发行版文件大小,期间不要关机。反过来从2切到1也支持,但一般没人这么干。默认进入哪个发行版也可以设置,用wsl --set-default Ubuntu-22.04指定,这样直接敲wsl命令就进到你想用的系统里。
4. Docker Desktop的安装与WSL 2集成
4.1 Docker Desktop下载与安装要点
Docker Desktop是Docker官方为Windows和macOS推出的桌面版工具,内置了Docker引擎、CLI、Compose以及图形化管理界面。对绝大多数人来说,直接在Windows上用Docker Desktop是最省心的选择,不用自己手动去装Docker Engine。
下载直接从Docker官网下载Docker Desktop for Windows的安装包。安装时有两个关键选项要注意:一是“Use WSL 2 instead of Hyper-V”这个复选框必须勾选,这是Docker和WSL 2集成的开关;二是安装过程中可能需要启用Windows相关功能,按提示操作并重启即可。
安装完以后第一次启动,Docker Desktop会显示接受服务条款的界面,同意之后它会自动检查WSL 2环境。如果一切正常,右下角托盘会出现一只小鲸鱼图标。打开终端运行:
docker version能正常显示Client和Server两段版本信息就说明装好了。注意Server部分必须能显示出来,如果只有Client信息,说明Docker引擎没起来,后面第6节我会专门讲排查思路。
4.2 配置WSL集成与镜像加速
Docker Desktop默认会集成默认的WSL发行版,但如果你有多个发行版,可以在Docker Desktop的Settings -> Resources -> WSL Integration里勾选你希望使用Docker的发行版。勾选之后,在这个发行版的终端里直接就能用docker命令,不需要任何额外配置。我几乎天天用这个功能:在WSL里跑容器,日志格式和文件路径都是Linux风格,比在PowerShell里操作舒服太多。
镜像加速这个事必须单独说一句。默认情况下Docker Hub的拉取速度在部分地区确实很慢,拉一个几十兆的镜像要等半天。解决办法是在Docker Desktop的Settings -> Docker Engine里配置daemon.json,加入镜像加速器地址,比如阿里云的容器镜像服务地址(需要登录阿里云控制台获取专属地址),或者中科大等公共镜像地址。改完点击Apply & Restart让Docker重启生效。
配置好的daemon.json大概长这样:
{ "registry-mirrors": [ "https://你的专属加速地址.mirror.aliyuncs.com", "https://docker.mirrors.ustc.edu.cn" ] }重点是:这里说的是合法的容器镜像加速配置,跟任何网络代理都没有关系。配好之后再来拉镜像,速度会有肉眼可见的提升,尤其拉mysql、redis这种常用镜像时体感特别明显。
4.3 虚拟化报错的排查思路
Docker Desktop启动时最常见的报错就是:
"Docker Desktop failed to start because virtualisation support wasn't detected."
这句报错的意思是检测不到虚拟化支持。排查思路从下往上走,分三步。
第一步,确认BIOS里的虚拟化开关。开机进BIOS,找到Intel Virtualization Technology(Intel VT-x)或者SVM Mode(AMD),设为Enabled。不同主板菜单位置不一样,但关键词基本就是Virtualization、VT-x、SVM这些。改完保存重启。
第二步,确认Windows的虚拟化平台功能完整。用管理员身份运行PowerShell,执行:
systeminfo看输出里“Hyper-V 要求”部分,检查“已在固件中启用虚拟化”是不是“是”。如果这里显示“否”,基本还是BIOS的问题;如果显示“是”但Docker依然报错,那就是Windows功能没开全。去“启用或关闭Windows功能”里打开“虚拟机平台”和“Hyper-V”,重启。
第三步,检查安全软件和系统设置。某些安全软件会关闭虚拟化相关的系统服务,这个不多见但我遇到过。另外,Windows的“内核隔离”功能在部分旧配置机器上会导致Docker无法检测虚拟化,可以去“Windows安全中心 -> 设备安全性 -> 内核隔离”里尝试关闭内存完整性,然后重启再试。这个开关平时不影响正常使用,但确实能解决一部分Docker启动问题。
5. 跑通第一个容器:MySQL与Redis实战
5.1 用Docker安装MySQL 8.0(含数据持久化)
到了最实用的环节。装好Docker之后第一件事,很多朋友的诉求就是跑数据库。以MySQL 8.0为例,一条命令就能跑起来:
docker run --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=你的密码 -d mysql:8.0参数解释一下:--name给容器起名字,-p把容器的3306端口映射到宿主机的3306端口,-e传环境变量,这里设置了root密码,-d表示后台运行,mysql:8.0是镜像名加标签。
但是,这种最简单的跑法有个致命问题:数据存在容器内部,一旦容器删除,数据全部丢光。生产环境绝对不能这么干,本地开发我也不建议养成这个坏习惯。正确的做法是把MySQL的数据目录挂载到宿主机,同时指定字符集:
docker run --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=你的密码 \ -v /home/你的用户名/mysql-data:/var/lib/mysql \ -d mysql:8.0 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci-v参数把容器内的/var/lib/mysql映射到WSL里的/home/你的用户名/mysql-data。这样无论容器怎么删、怎么重建,数据都好好地留在宿主机目录里。字符集参数指定utf8mb4是常规操作,处理中文和特殊符号都不会出现乱码。
启动以后,在WSL终端里就能连上MySQL:
docker exec -it mysql8 mysql -uroot -p输密码进入MySQL命令行,执行show databases;能看到系统数据库就说明成功了。用第三方图形工具连接也轻松,连接地址写localhost,端口3306,用户名root,密码就是刚才设置的那个。
5.2 用Docker安装Redis并配置主从
Redis部分,单机版一条命令:
docker run --name redis -p 6379:6379 -d redis:7Redis默认镜像不带配置文件,如果要开启AOF持久化,加--appendonly yes参数:
docker run --name redis -p 6379:6379 -d redis:7 redis-server --appendonly yes主从架构在本地做也很方便。先起一个主节点,再起一个从节点,让从节点通过--replicaof参数指向主节点。这里有个关键点:容器之间通信不能用localhost,得用容器的IP或者通过Docker网络。最省事的做法是创建一个Docker网络,让两个容器加入同一个网络,这样它们就能用容器名互相访问了:
docker network create redis-net docker run --name redis-master --network redis-net -p 6379:6379 -d redis:7 redis-server --appendonly yes docker run --name redis-slave --network redis-net -p 6380:6379 -d redis:7 redis-server --replicaof redis-master 6379验证主从是否生效,进主库看一眼:
docker exec -it redis-master redis-cli info replication输出里role:master并且connected_slaves:1就说明从库连接成功。这种容器网络配法比手动查IP硬配靠谱得多,因为容器重启后IP可能会变,但容器名是不变的,只要网络不删,这种解析就一直有效。
5.3 常用命令速查表
整理一份我每天都会用到的Docker命令速查表,新手可以直接抄:
| 命令 | 作用 |
|---|---|
| docker ps -a | 查看所有容器(包括已停止的) |
| docker images | 查看本地镜像列表 |
| docker logs -f 容器名 | 实时查看容器日志 |
| docker exec -it 容器名 bash | 进入容器内部 |
| docker restart 容器名 | 重启容器 |
| docker stop / start 容器名 | 停止/启动容器 |
| docker rm 容器名 | 删除容器 |
| docker rmi 镜像名 | 删除镜像 |
| docker network ls | 查看Docker网络 |
这些命令敲熟之后,日常大部分操作就够用了。更复杂的编排需求,比如同时起MySQL、Redis、Elasticsearch多个容器,建议直接写docker-compose.yml,然后用docker compose up -d一次性启动,比一条条命令敲省心太多。一个同时包含MySQL和Redis的compose示例大概长这样:
version: '3.8' services: mysql8: image: mysql:8.0 container_name: mysql8 ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: yourpassword TZ: Asia/Shanghai volumes: - ./mysql-data:/var/lib/mysql command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci redis: image: redis:7 container_name: redis ports: - "6379:6379" command: redis-server --appendonly yes volumes: - ./redis-data:/data在文件同目录下运行docker compose up -d,两个服务就一起起来了,日志统一管理。以后想停就docker compose down,数据因为挂载了volume都留在宿主机。这才是正经的开发环境容器化用法,推荐早早上手。
6. 高频问题排查实录
6.1 WSL版本过旧
启动WSL或者VS Code连接WSL时,经常弹出这样一段提示:
"Your version of Windows Subsystem for Linux (WSL) is too old. Run 'wsl --update' or update Windows to the latest version."
这段报错的意思是WSL组件版本太老。解决办法很直接,管理员PowerShell下运行:
wsl --update这个命令会把WSL更新到最新版本。如果wsl --update下载很慢或者失败,跟前面第2节说的一样,去微软官网手动下载最新的WSL更新包(MSI格式)安装,再回到系统里执行wsl --version验证版本号。更新完以后,之前的报错提示基本就消失了。
这里有一个容易被忽略的点:wsl --update更新的是WSL核心组件,跟发行版内部的apt update是两码事。如果你只是想升级Ubuntu里的软件包,需要进到WSL里跑apt update && apt upgrade,不要把两条命令混为一谈。
6.2 Docker Desktop启动失败
Docker Desktop启动失败的原因五花八门,除了前面说的虚拟化检测不到,还有几种高频情况。
第一种是“WSL 2 installation is incomplete”。这种情况通常出现在你装了WSL但内核没更新到最新。先运行wsl --update,然后在PowerShell里运行wsl --set-default-version 2确认默认版本,再重启Docker Desktop。
第二种是Docker引擎起来之后又立刻退出。直接打开Windows事件查看器,找到Docker相关的错误日志,排查效率会高很多。我遇到过一次是Windows防火墙规则冲突,把Docker Desktop在防火墙里的入站规则删掉,重启软件就恢复了。
第三种是端口被占用。容器映射端口时跟Windows其他服务冲突,比如Windows的World Wide Web Publishing Service默认占用80端口,你要跑Nginx容器映射80就会失败。解决办法要么关闭占用端口的服务,要么把容器映射改成8080这类端口,业务上通常不会有影响。
6.3 WSL占用内存过多
WSL 2默认最多可以使用宿主机一半的内存,如果你的机器是16G内存,WSL可能分走8G,这在只跑几个小容器的情况下其实是浪费。解决方法是在Windows用户目录下创建一个.wslconfig文件(注意文件名带点),写入:
[wsl2] memory=4GB swap=2GB processors=2memory限制WSL最大使用内存,swap给虚拟内存大小,processors限制CPU核心数。改完保存,运行wsl --shutdown让配置生效。这样WSL就不会无限吃资源了,尤其开着Docker Desktop挂多个容器的时候,内存分配会更从容。文件里还可以配置localhostForwarding等参数,默认就是开的,一般不需要动。
6.4 容器端口无法访问
在WSL里启动了一个容器,映射了3306端口,然后从Windows本机连localhost:3306提示连不上。这个问题的排查路径一般是这样。
先确认容器状态:docker ps看容器是不是在运行。再进容器里确认服务真的起来了:docker exec -it 容器名 bash,然后在容器内部测一下端口。如果容器内部正常,那就是端口映射的问题,检查docker run时的-p参数是不是写错了。
还有一种特殊情况是Windows防火墙拦截了WSL的端口转发。WSL 2默认会把Windows的localhost转发到WSL里的容器,但某些系统更新或者安全软件会破坏这个转发链路。遇到这种情况,在管理员PowerShell里执行:
netsh interface portproxy reset然后重启WSL。一般就能恢复localhost转发。如果还是不行,干脆在Windows里直接用WSL的IP加端口访问。先在WSL里运行ip addr查IP,然后在Windows浏览器里访问这个IP加端口,能通的话就说明Docker和容器本身没问题,问题就出在localhost转发这条链路上,往这个方向排查就不会跑偏。
6.5 其他高频问题速查
把平时被问到的几个小问题也一并列出来:
| 问题 | 解决方案 |
|---|---|
| wsl命令不存在 | 系统太老或WSL组件未安装,先补Windows更新再wsl --install |
| Ubuntu里中文乱码 | sudo apt install fonts-noto-cjk,必要时配置locale |
| 容器时区不对 | docker run时加-e TZ=Asia/Shanghai |
| Docker镜像拉取超时 | 检查daemon.json里的镜像加速地址是否可用 |
| VS Code连不上WSL | wsl --shutdown重启WSL,再重开VS Code |
| docker compose命令找不到 | 确认Docker Desktop已升级到较新版本,旧版用docker-compose |
最后再分享一个我的个人习惯:我会在WSL用户目录下的.bashrc文件末尾加几个常用别名,比如dc='docker compose'、dps='docker ps'、dlogs='docker logs -f',这样敲命令能省不少时间。配置这种事情没有绝对标准,关键是自己用得顺手,但一定要记得改完配置文件后执行source ~/.bashrc让别名立即生效,不然容易以为自己没改对。
工具链这种东西,光看文档是记不住的,只有自己亲手从零搭一遍、踩一遍坑,才能真正建立起体系化的认识。我在这个过程里最深刻的体会是:大部分报错其实不是环境不行,而是某个前置条件没满足,比如虚拟化没开、WSL版本太老、端口被占用。按顺序逐层排查,绝大多数问题十分钟内都能定位。希望这篇东西能帮你在Windows上把WSL和Docker这套环境一次跑通。