我如何用 Windows 开发 —— 2021 我的开发环境

由于搭载 macOS 的设备越来越昂贵且槽点颇多,加上微软近些年开始发力开发者体验,我的工作环境已经从 MacBook 切换到了运行 Windows 系统的非苹果机器。这是多方面因素决定的:平时玩的游戏需要在 Windows 上运行,桌面环境也是 Windows 的比较舒适耐用。

使用 Windows 进行开发工作虽说并不像在 macOS 上那样可以使用诸多 POSIX 标准的工具,但鉴于硬件性能的提升和虚拟化技术的成熟,使用虚拟机或者 WSL2 也未必是不可接受的方案。此外,随着 Visual Studio Code 的 Remote 功能越发完善,在 Windows 的窗口环境下享受和 Linux 一样的开发体验也并非不可能。

当然,使用 Windows 开发完全是个人选择;本文并不打算讨论 Windows 对比其它操作系统的优劣,只是在此简单介绍我如何在 Windows 桌面上进行我的开发工作,希望对因为种种原因选择使用 Windows 工作的读者能有所启发。

需求

在讨论开发环境之前,我们首先得讨论开发的需求是什么。我个人写的东西还挺杂的,大部分时候使用 Visual Studio Code 写 TypeScript, JavaScript 和 PHP。除此之外,我有时候会使用 Android Studio 写 Java/Kotlin,或者用 Goland 写 Go,又或者使用 Visual Studio 2019 写 C#。此外,我还需要运行 DataGrip 之类的数据库管理软件、kubectl 之类的集群管理软件,还需要使用 ssh 登录数台远程主机。我的个人项目几乎都使用 docker 进行部署,因此 docker 也必不可少。

总体来说,我需要:

  • Visual Studio Code
  • Visual Studio
  • JetBrains IDE,包括:
    • Android Studio
    • Goland
    • DataGrip
  • 终端、zsh
  • docker

了解了自己的需求之后,就能根据自己的需求来考察选项。

OS

如标题所述,我的宿主机基本上是 Windows 操作系统,一般是最新的 Release 或者 Beta 版本,比如在写作文章的时候是 Windows 11 (Pro) Insider Beta。这目前是为了使用 wslg,在 Windows 11 正式发布之后我多半会切换到稳定版上。

鉴于平日打交道的服务器多半是 Debian 或者 Ubuntu 这类 Linux 发行版,本地开发环境上有一个 Linux 的机器很重要,我称之为 Linux dev box。在这个 Linux dev box 内的操作系统上,我选择使用 ArchWSL 提供的 ArchLinux 镜像作为日常的 dev box 使用,这主要是考虑到 ArchLinux 的软件包比较新,而且 AUR 上有很多方便的包可以选择使用。在非生产环境下,使用 ArchLinux 的体验还是不错的。

虚拟化

Linux dev box – WSL2

在 Windows 宿主机上运行 Linux box 是很简单的事情,而且你有诸多选择:以 VirtualBox 为代表的虚拟机方案;WSL1;WSL2。我的选项是 WSL2,你可能觉得 WSL2 和虚拟机也没什么区别,事实上几乎如此,只是省去了不少自己对虚拟机做的集成配置。WSL1 曾经是我梦想中的方案,但 WSL1 作为日常使用的子系统还是有其种种问题,其环境和物理或者 KVM 虚拟化的 Linux 还是有所区别,IO 也令人发指。

一个比较常见的问题是 systemd,众所周知 WSL 不管是 1 还是 2 都不支持直接启用 systemd,大概是因为微软自己启动的缘故。虽然没有官方的支持,但使用 genie 提供的 bottled 方案也并不是一个很难接受的选择,事实上体验基本与 systemd 一致——当然如果你需要的某些复杂高级功能工作不正常,那当我没说。听说 wsl2-hacks 也是个办法,但我没有尝试过。

另外,由于微软的奇葩设计,在每次 Windows 启动的时候 WSL2 都会被随机分配到完全随机的网段,这会导致不少问题。我遇到的主要是 Windows 下的防火墙难以配置WSL2 内连宿主机 IP 不稳定;还有选到和 docker 等虚拟环境冲突的网段导致路由错乱。前两个问题我通过一些脚本来解决,我把他们放到了 GitHub 的 oott123/work-on-windows 上供读者参考;而最后一个问题我也通过了一些办法解决,后文提到 docker 的时候再仔细说。

用上 WSL2 之后,wslg 也变得很简单,只需要 export DISPLAY=:0 并且 export WAYLAND_DISPLAY=wayland-0 就可以让 Linux 桌面程序显示成 Windows 窗口了。不过在我这边,wslg 的剪贴板不一定工作,我找到了一个 issue,但看起来没什么帮助的样子。之后也许要尝试自己去做剪贴板同步了。

如果你不想使用 Insider 系统来启用 wslg,也可以试试第三方方案 X410 。X410 ( X for 10 ) 是一款商业化的 X Server,相比 VcXserv 有更好的 HiDPI 支持,同时体验也更加流畅。官网有一些非常详尽的教程,例如和 WSL2 配合使用的和 Hyper-V 配合使用的,都可以看看。

Windows dev box – Windows 10

基于某种“不想在宿主机上安装开发环境”的奇怪洁癖矫情,我没有在我的宿主机器上安装 Visual Studio 、Android Studio 等开发环境,而是选择使用 VMWare 创建了一个虚拟机,并在虚拟机内安装。这台虚拟机里安装了 Visual Studio、Windows SDK、Android Studio 和 Visual Studio Code 等等 IDE 或编辑器。

因为使用频率比较低,所以也没咋配置,直接用 VMWare 开干了。实际体验只能说尚可接受,操作起来还是偶有迟滞的感觉,反正用得不多,懒得管了。

开发环境

IDE / SDK

鉴于我有两个 dev box ,那么某个 IDE 或者环境该在哪个 box 里安装无疑成了需要选择的事情。目前,我的基本准则是,能在 WSL2 里安装的,都在 WSL2 里安装:

  • Visual Studio – 宇宙第一 IDE 只支持 Windows,所以在 Windows box 里安装
  • Android Studio – WSL2 连实体机不方便,所以在 Windows box 里安装
  • Goland / DataGrip – WSL2 里安装,wslg 使用
  • Sublime Merge – WSL2 里安装,wslg 使用
  • nodejs, yarn, php 等命令行开发环境 – WSL2 里安装

Visual Studio Code

Visual Studio Code 的 Remote 功能是本世纪以来最伟大的发明。
—— 三三・自己说的

早在 coder 发布 code-server 的时候,我就搭建了一套用于日常开发;后来 vscode 发布了官方支持的 Remote 功能之后,我也就随之迁移到了 Remote 上进行开发。之前还需要通勤的时候,无论在公司还是在家里,都可以用 vscode remote 连接到自己的服务器摸一些东西;现在的话主要用于在 Windows 上连接 WSL 开发。

显然,我的 Visual Studio Code 是安装在宿主机上的;但使用体验和安装在 Linux 上并没有什么分别,一切都很顺滑。

Windows Terminal

我平时使用 Windows Terminal 作为我的 shell,大概配置成这个样子:

这张图片展示了使用 Windows Terminal 连接 WSL2 运行 neofetch 的结果。

在 Windows Terminal 中,为 WSL2 的 Profile 设置开始路径为 \\wsl$\Arch\home\oott123 这样的目录,就可以实现打开新标签的时候切换到 WSL 的家目录了,在这里就可以方便地存放自己的代码和开发环境了。注意不要把代码放到 /mnt/c/ 之类的地方去,否则你的 IO 会很惨。我平时使用 zsh 作为默认 shell,在 WSL2 里也是能直接使用的,就和普通的 Linux 系统没什么区别了。

另外,我使用了修改过的 zsh-notify 插件,配合 BurntToast实现命令错误/长时间任务完成推送 Windows 通知的功能。虽然由于偷懒,没有去检测当前焦点窗口是否在 shell 中导致命令报错的时候稍微有点吵之外,其实还挺好用的。

Docker

在 WSL2 里安装 docker 也是一件有两个选择的事情:使用 Docker Desktop,或者直接安装 docker daemon。我曾经选择了前者,还用上了非常酷炫和魔法的 WSL2 daemon,直到它出现了不少问题,最后由于我的 Arch 内核太新出现了一个我修不好的问题,一气之下就把它整个删掉了,安装了正常的 docker daemon。

前文提到过,WSL2 启动的时候会随机选择网段,有时候会选到和 docker 等虚拟环境冲突的网段导致路由错乱。后来我在 Windows 内创建一个和 docker 同网段的虚拟网卡之后,Hyper-V 似乎就很聪明地避开了这个网段,问题得以解决。

打开设备管理器,选择操作-添加过时硬件,依次选择安装手动从列表选择的硬件、网络适配器、Microsoft、Microsoft KM-TEST,并确认即可
使用设备管理器添加 KM-TEST 虚拟网卡

体验

在 Microsoft 大力拥抱开源社区的今天,使用 Windows 作为开发环境已经不是令人难以接受的事情了——当然前提是你使用 WSL2,我不会真的在 Windows 这样的 OS 上做开发的。微软的 Windows 虽然从来都广受诟病,但它的桌面环境体验不是其它竞争者(是的,我是说 Linux。你说什么 m 什么 OS 是啥,我听不见)可以比拟的。如果说曾经基于 BSD 内核的 macOS 拥有类似 UNIX 的开发体验,那么 WSL2 则是 99.9% 的 Linux 开发体验,这对于我来说是非常重要且舒适的。是的,绝对不是因为我要用 Windows 打游戏。

因为基于虚拟机的 dev box 使得备份和迁移变得异常方便,全虚拟化环境的拟真也能带来许多优势,而 WSL2 和 Windows 的集成也足够顺滑,所以我想,在可以预见的将来,我的开发环境仍然会首选宿主机 Windows + Linux / Windows dev box 的模式来配置。

在 Windows 下使用 boot2docker 搭建 docker 开发环境

很久以前研究过一会儿 docker ,当时觉得只是一个轻量级的虚拟机包装而已,没觉得有多大用。

最近发现这玩意还蛮好玩的,但是又懒得翻以前的虚拟机了,于是发现一个叫 boot2docker国内下载(via DaoCloud) 的东西。官方宣称是“It runs completely from RAM, weighs ~27MB and boots in ~5s (YMMV).”。

下载回来的安装包有 100 多 MB,当时在想说好的 27MB 呢,结果发现包里有个 VirtualBox 和一个 msysgit ……简直坑。

如果你的机器上没有这俩玩意,那就放心大胆的直接安装吧。它会帮你把各种琐事配置好,官方的指导文档里也写的十分详细。

如果你的系统里有 VirtualBox 了,那也就直接安装,它会自动识别系统上的 vbox 的,不过你最好先测试一下你的 Host-only 网卡是否正常。

那么重点来了:如果你的系统里有 msysgit,并且还有其它的 GNU 工具链,比如我这种装了 Gow 的人,那么其实这个玩意是有坑的。

要说这个坑,首先要谈一谈 boot2docker 的工作原理。 boot2docker 由 VirualBox 里的一个叫 boot2docker 的虚拟机、一个用来管理 Virual Box 里的虚拟机的工具 boot2docker.exe 和 docker 本体 docker.exe 组成。

当你执行 sh start.sh 的时候,它首先调用 boot2docker init 来创建这个虚拟机(如果已经存在则跳过);其次将该虚拟机启动;最后使用 boot2docker ip 来获取虚拟机的 ip 地址并设置环境变量。

boot2docker ip 这个命令,是调用系统中的 ssh 来获取 ip 地址的。它在 init 的时候,将虚拟机的 22 端口转发到宿主机的 2022 端口,并运行 ssh -p 2022 docker@localhost ip addr show dev eth1 来获取 ip 地址(详细的命令可运行 boot2doker -v ip 来显示)。而 Gow 里的 ssh 指令,则是调用了 PuTTY 的 plink.exe

那么问题来了:plink.exe 根本不支持 -p 语法,也不支持它所使用的 -o 等参数。

所以,如果你装了 Gow,请一定把 msys git 的路径加到 PATH 的最前方……起码是在 Gow 的前面,这样就不会被坑了。

处理完这些乱七八糟的事情之后,运行 boot2docker status 即可看到你的虚拟机的当前状态。下次使用时,只需执行 sh start.sh 即可自动配置好环境变量并且开启 git bash。在这个 git bash 里,你就可以执行你的 docker 指令了~

如果不想用 git bash,那么执行 boot2docker shellinit ,也可以看到应设置的环境变量,如:

export DOCKER_HOST=tcp://192.168.111.222:2376
export DOCKER_CERT_PATH='C:\Users\oott123\.boot2docker\certs\boot2docker-vm'
export DOCKER_TLS_VERIFY=1

此时,用 set 将这些环境变量设置好,即可在 cmd 中使用 docker 来管理 docker 容器了,如:

set DOCKER_HOST=tcp://192.168.111.222:2376
set DOCKER_CERT_PATH=C:\Users\oott123\.boot2docker\certs\boot2docker-vm
set DOCKER_TLS_VERIFY=1

试试 docker info 能不能显示出虚拟机的信息来吧。

CentOS 7 下 docker 安装拾穗

如何安装 docker

docker 在 CentOS 7 下已经收录到了 extras 包中,因此我们只需要执行:yum install docker 即可安装 docker。

安装后,使用systemctl start docker来启动 docker 的服务,再用systemctl enable docker来使其开机启动。顺带一提,service docker startchkconfig docker on也能达到同样的效果;但由于 CentOS 7 采用了 systemd 来管理服务和开启启动项,因此我们尽量使用 systemctl 来操作服务。

安装后输入 docker info 来查看 docker 信息。

[root@localhost data]# docker info
Containers: 0
Images: 0
Storage Driver: devicemapper
 Pool Name: docker-253:0-68161190-pool
 Data file: /var/lib/docker/devicemapper/devicemapper/data
 Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
 Data Space Used: 293.0 Mb
 Data Space Total: 102400.0 Mb
 Metadata Space Used: 0.7 Mb
 Metadata Space Total: 2048.0 Mb
Execution Driver: native-0.2
Kernel Version: 3.10.0-123.el7.x86_64

如何修改 Data file 路径

遇到的第一个问题,则是:docker 的数据都放到哪里了?显然,上面的 docker info 给了我们答案: 在/var/lib/docker/ 下。由于种种神奇的原因,我们希望尝试 修改 docker 的数据存放目录

编辑这个文件:vim /etc/sysconfig/docker,找到OPTIONS=--selinux-enabled -H fd://,修改为:OPTIONS=-g /mnt/docker0/data --selinux-enabled -H fd://。这样我们就成功的将 docker 目录修改成功了。

执行 systemctl restart docker ,再 docker info ,可以看到目录已经修改成功了。

如何使用国内的 docker 镜像源

考虑 DockerPool 这个 docker poll。

例如,我要下载 CentOS 7 的 docker 镜像,那么执行:

docker pull dl.dockerpool.com:5000/centos:centos7

镜像文件略微有点大,下得我心醉。在 DockerPoll Downloads 可以找到其它的镜像列表。

下载完成后,要把第三方 registry 的下载的文件打上 tag

docker tag dl.dockerpool.com:5000/centos:centos7 centos:centos7

如何清理 docker 容器

执行了一些简单的命令,比如docker run -t -i centos:centos7 ping baidu.com之后,再看 docker info ,发现 Containers 居然到了 4 。大惊,用 docker ps -a 查看后才知道,每次我用 docker run 的时候他都会新建一个 Container。试了下 docker rm ,一次只能删除一个。改用 docker rm $(docker ps -aq) ,成功删除它们。

参考:Docker——从入门到实践