The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

使用 WSL 2 打造優質的多重 Linux 開發環境

由於微軟這次推出 WSL 2 全新架構,可以讓你將 Linux 完整的跑在一個羽量級的 Hyper-V 虛擬機器中,不但可以用有完整的 Linux 核心、完整的 system call 相容性、啟動時花費更少的記憶體,也大幅提昇了檔案 I/O 存取的效能與網路管理的便利性。這篇文章將帶你一步一步認識 WSL 2,並且打造出優質的多重 Linux 開發環境。

image

注意: WSL 2 只能運行在 Windows 10, Version 2004, Build 19041 以上版本,任何 SKU 版本都支援,包含 Windows 10 Home 喔!

注意:WSL 2 可以跟 VMWare 15.5.5+ 與 VirtualBox 6+ 並存。

名詞解釋

  • Distro

    全名為 distribution (散佈版本)

    因為 Linux 有非常多分支,例如:RedHat, Ubuntu, CentOS, Fedora, Kali, ... 等等。每個不同家的 Linux 我們就通常為一個 Distribution,而我們經常會簡寫成 Distro

    而且 WSL 可以安裝許多套 Linux 在你的 Windows 下,因此我們說 WSL Distro 就代表著 WSL 下的特定 Distro 版本!

  • WLinux

    本文所有的命令列範例,都會用 WLinux 代表特定一個 WSL Distro 版本,你實際要用的時候,應該要替換成你目前正在使用的 WSL Distro 名稱!

安裝 WSL Distro 版本

剛安裝好 WSL 功能後,預設並沒有安裝任何 Linux 發行版本 (distribution),所以你必須先從 Microsoft Store 安裝好任何一套 WSL Distro 才能開始使用。

你可以開啟瀏覽器,直接在網址列輸入以下網址就會自動開啟 Microsoft Store 視窗,並自動搜尋出所有 WSL 相容的應用程式。

關於 ms-windows-store:// 網址,你可以參考 Launch the Microsoft Store app - Windows UWP applications 文件說明。

目前已經有 Ubuntu 20.04 LTS, Ubuntu 18.04 LTS, Alpine, Kali Linux, ... 以及各種不同的 Distro 都會陸續上架,你甚至可以自行製作你自己的 WSL Distro!

參考 介紹好用工具:WSL (Windows Subsystem for Linux) 文章。

列出 WSL Distro 版本

  • 列出目前已經安裝的 WSL Distro 版本

    wsl -l
    
  • 列出目前已經安裝的 WSL Distro 版本(包含顯示執行狀態與 WSL 版本)

    wsl -l -v
    

    執行結果如下,你可以看得出執行狀態與 WSL 版本:

      NAME                   STATE           VERSION
      WLinux                 Stopped         2
      docker-desktop         Stopped         2
      docker-desktop-data    Stopped         2
    * Ubuntu-20.04           Running         1
      Ubuntu-18.04           Stopped         1
    
  • 列出目前正在執行中的 WSL Distro 版本

    wsl -l --running
    
  • 列出所有的 WSL Distro 版本(包含已經解除安裝的版本)

    wsl -l --all
    
  • 列出所有的 WSL Distro 版本(僅顯示 WSL Distro 名稱)(方便做 CLI 整合)

    wsl -l -q
    

升級 WSL Distro 至 WSL 2 版本

請注意:我目前的 Windows 10 ver 2004 的 OS Build 版本為 19041,這個版本目前預設還是以 WSL 1 為主,你必須要從 OS Build 版本 20150 開始,WSL 2 才是預設值。因此,你從 Microsoft Store 安裝好的任何一套 WSL Distro,預設都會採用 WSL 1 版本,你必須手動升級到 WSL 2 版本才能享有 WSL 2 的絕佳 I/O 效能!

以下是升級的命令與參數:

  • 將 WSL Distro 升級到 WSL 2 版本

    wsl -d Ubuntu-20.04 --set-version WLinux 2
    
  • 將 WSL Distro 降級到 WSL 1 版本 (你應該不會想這麼做吧!)

    wsl -d Ubuntu-18.04 --set-version WLinux 1
    

調整 Shell 執行環境

由於我用 Pengwin Linux (based on Debian®),一套針對 WSL 最佳化過的 WSL Distro,第一次啟動時可以執行 pengwin-setup 命令,自動調整系統到最佳狀態!

  • 設定 PPA (Personal Package Archives) 工具

    安裝 才可以使用 add-apt-repository 工具!

    sudo apt-get install -y software-properties-common
    

    匯入 PPA 金鑰與測試安裝 ASCII 水族館套件 (Optional)

    sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F3985DE9980B9063
    sudo add-apt-repository ppa:ytvwld/asciiquarium
    sudo apt-get update && sudo apt-get install asciiquarium
    
  • 建議先更新系統到最新版

    sudo apt update && sudo apt upgrade
    
  • 要用 Git 版控,建議調整一下設定值

    一定要設定 user.nameuser.email 資訊(請自行調整內容)

    git config --global user.name Will
    git config --global user.email doggy.huang@gmail.com
    

    常用設定(避免顯示亂碼)

    git config --global core.editor vim
    git config --global core.autocrlf false
    git config --global core.quotepath false
    git config --global help.autocorrect 30
    git config --global color.diff auto
    git config --global color.status auto
    git config --global color.branch auto
    

    常用 alias 設定

    git config --global alias.ci   commit
    git config --global alias.cm   "commit --amend -C HEAD"
    git config --global alias.co   checkout
    git config --global alias.st   status
    git config --global alias.sts  "status -s"
    git config --global alias.br   branch
    git config --global alias.re   remote
    git config --global alias.di   diff
    git config --global alias.type "cat-file -t"
    git config --global alias.dump "cat-file -p"
    git config --global alias.lo   "log --oneline"
    git config --global alias.ls   "log --show-signature"
    git config --global alias.ll   "log --pretty=format:'%h %ad | %s%d [%Cgreen%an%Creset]' --graph --date=short"
    git config --global alias.lg   "log --graph --pretty=format:'%Cred%h%Creset %ad |%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset [%Cgreen%an%Creset]' --abbrev-commit --date=short"
    git config --global alias.alias "config --get-regexp ^alias\."
    git config --global alias.ignore '!'"gi() { curl -sL https://www.gitignore.io/api/\$@ ;}; gi"
    git config --global alias.iac  '!'"giac() { git init && git add . && git commit -m 'Initial commit' ;}; giac"
    

    如何設定 git ignore 命令並自動下載所需的 .gitignore 範本

  • 新增 ~/.vimrc 檔案 (學習 Vim 是 Linux 玩家必備技能)

    syntax on
    set background=dark
    
    let &t_SI .= "\<Esc>[?2004h"
    let &t_EI .= "\<Esc>[?2004l"
    
    inoremap <special> <expr> <Esc>[200~ XTermPasteBegin()
    
    function! XTermPasteBegin()
      set pastetoggle=<Esc>[201~
      set paste
      return ""
    endfunction
    
  • 設定更清楚的命令提示字串(Prompt),將以下設定加入到 .profile 檔案中

    為了能夠在不同的 Shell 環境區分不同的 WSL Distro,我特別自訂 Bash Shell 的命令提示字串(Prompt),只要將以下設定加入到 .profile 檔案中即可:

    export PS1="\[\033[32m\]\u@$WSL_DISTRO_NAME\[\033[00m\]:\[\033[36m\]\w\[\033[00m\]$ "
    

    參見 Customizing my WSL bash prompt自訂 Linux 的 Bash Shell 命令提示字串 Prompt(一):基本用法 文章。

  • 要用 vim 當成預設編輯器,可以將 EDITOR 環境變數到 .profile 檔案中

    sudo update-alternatives --set editor /usr/bin/vim.basic
    
    export EDITOR=vim
    
  • 要用 bash 當成預設 shell 環境,有兩條重要的 shopt 參數要調整,請加入到 .profile 檔案中

    shopt -s direxpand
    shopt -s no_empty_cmd_completion
    

    啟用 direxpand 會讓你的 bash 在執行路徑自動完成時自動展開資料夾路徑,否則會出現這個問題。不過當時我在 GitHub 提供的解決方案是錯的,設定 shopt -u progcomp 會導致無法使用 Bash 自動完成功能!

    no_empty_cmd_completion (預設停用) 請務必啟用,因為有兩種狀況會讓你的 WSL 近乎死當!

    1. 在沒有輸入任何命令的情況下,直接按下 ESC 鍵兩次
    2. 你可以先輸入一個以上的空白字元,然後按兩下 TAB

    這兩種情境,都會讓整個 Shell 環境等待超過 35 秒才會有所反應,期間你按什麼都沒用,因為 bash 會自動掃描所有可執行檔的名稱。在 WSL 之下,不但會掃描 Linux 環境下 PATH 環境變數指定路徑下的所有可執行檔,還會掃描 Windows 環境下所有 PATH 環境變數指定路徑下的所有可執行檔,這是相當恐怖的數字,以我的電腦為例,就有超過 7000 個建議,所以反應相當的慢!

    參見文章:4.3.2 The Shopt Builtin & Shell: “Display all 2588 possibilities?”

  • 要用 GPG 簽章,一定要設定以下 GPG_TTY 環境變數到 .profile 檔案中

    export GPG_TTY=$(tty)
    

    參見 如何使用 GPG (GnuPG) 對 Git Commit 與 Tag 進行簽章 文章

  • 匯入自己的 GPG 金鑰並設為絕對信任

    請自行替換 doggy8088 為你的個人帳號,基本上 GitHub 會為每個人建立一組 GPG 私鑰密碼。

    curl -s -o doggy8088.gpg https://github.com/doggy8088.gpg
    
    gpg-agent --daemon
    gpg --import doggy8088.gpg # 這個步驟需要輸入 GPG 私鑰密碼
    
    rm -f doggy8088.gpg
    gpgconf --kill gpg-agent
    
    GPGKEYID=$(gpg --list-keys --keyid-format SHORT | sed -n '3p' | cut -d '/' -f 2 | cut -d ' ' -f 1)
    echo -e "trust\n5\ny\n" | gpg --command-fd 0 --expert --edit-key $GPGKEYID
    
    git config --global user.signingkey $GPGKEYID
    git config --global commit.gpgsign true
    
  • 設定 WSL 共用 Windows 下的 Git Credential Manager

    git config --global credential.helper "/mnt/c/Program\ Files/Git/mingw64/libexec/git-core/git-credential-manager.exe"
    

    參見 How do I set up Git Credential Manager? (How do I use my Windows Git permissions in WSL?) 文章。

  • 建立 SSH 金鑰組

    ssh-keygen -t rsa -b 4096 -f $HOME/.ssh/id_rsa -N ''
    touch ~/.ssh/authorized_keys
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
    
  • 讓執行 sudo 的時候免輸入密碼

    echo "will ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/will
    
  • 調整 ~/.bashrc 讓 WSL 預設開啟時進入 $HOME 目錄,而不是 Windows 的 Home 目錄 (%USERPROFILE%)

    以下命令可以限制只有開啟 WSL 2 時的預設 Windows Home 目錄 (%USERPROFILE%) 才會自動切換到 Linux 的 $Home 目錄:

    if [ "${PWD:-}" = "/mnt/c/Users/$USER" ]; then
      cd ~
    fi
    

    請不要建立 ~/.bash_profile 檔案,以免 ~/.profile 檔案無法自動載入。詳見:VSCode Integrated Terminal Doesn't Load .bashrc or .bash_profile

  • 調整 /etc/wsl.conf 設定檔(將預設登入帳戶設定為 will 帳號)

    將以下兩行加入到 /etc/wsl.conf 設定檔最後面:

    [user]
    default=will
    

    如果檔案不存在,也可以直接建立新的 /etc/wsl.conf 檔案!

將 WSL Distro 移動到不同磁碟機

  • 匯出特定 WSL Distro 版本為 *.tar

    wsl --export WLinux WLinux.tar
    
    1. --export 後面的第 1 個參數為 WSL Distro 名稱
    2. --export 後面的第 2 個參數為 *.tar 檔案路徑
  • 匯入特定 WSL Distro 版本

    wsl --import WLinux C:\WSL\WLinux WLinux.tar
    
    1. --import 後面的第 1 個參數為 WSL Distro 名稱
    2. --import 後面的第 2 個參數為 WSL Distro 的檔案系統要直接存放在哪裡
    3. --import 後面的第 3 個參數為先前 WSL Distro 匯出的 *.tar 檔案路徑

同時安裝多套 Ubuntu 20.04 LTS 版本

基本上,你一次只能從 Microsoft Store 安裝一種 WSL Distro 版本,如果我想準備多個不同的開發環境,但都使用 WLinux 這個 WSL Distro 怎麼辦呢?

其實很簡單,你先找到一個 WLinux,並且把基本設定都搞定,當成 WSL Distro 範本,接著先匯出,再匯入多個版本即可! 👍

底下這個例子(PowerShell),就是將現有的 WLinux 先匯出,然後匯入成三個不同的 WSL Distro 名稱 (指定使用 WSL 2 版本):

cd $env:USERPROFILE

wsl --export WLinux WLinux.tar

wsl --import WLinuxDev    .\WLinuxDev    WLinux.tar --version 2
wsl --import WLinuxGo     .\WLinuxGo     WLinux.tar --version 2
wsl --import WLinuxDotNet .\WLinuxDotNet WLinux.tar --version 2

如果要指定啟動特定 WSL Distro 的話,只要加上 -d <Distro> 參數即可:

wsl -d WLinuxDev

如何存取 Windows 與 WSL Distro 之間的檔案

  • Windows -> WSL 2

    你可以開啟「檔案總管」並直接用 \\wsl$\<DistroName> 這個路徑,透過「網路芳鄰」的方式直接存取 WSL 2 下的任何檔案!

    注意:從 Windows 10 Build 20175 開始,WSL 2 會開始支援 \\wsl\<DistroName> 這種路徑,但舊的 \\wsl$\ 還會支援。

    例如:你的 WSL 2 有個名為 WLinux 的 Linux 環境,那麼你從 Windows 就可以直接以 \\wsl$\WLinux 來存取檔案。如果檔案是 /home/will/output.json 的話,就可以從 Windows 用 \\wsl$\WLinux\home\will\output.json 路徑來直接存取檔案。

    請注意:WSL 2 每份 WSL Distro 內建使用的虛擬硬碟只有 256GB 而已,如果會用超過就會發生錯誤,必須參考 Expanding the size of your WSL 2 Virtual Hardware Disk 文件進行調整。

  • WSL 2 -> Windows

    你可以直接用 /mnt/<DRIVE> 這個路徑,存取任何 Windows 上面的檔案!

    例如:Windows 的 C:\Projects 目錄,就可以從 WSL 2 中以 /mnt/c/Projects 來存取檔案。

    另外,你可以從 WSL Distro 內執行以下命令,就可以快速開啟「檔案總管」並且直接進入你在 WSL Distro 內的資料夾,超級方便!

    cd ~
    explorer.exe .
    

    請注意:你一定要輸入 explorer.exe . 才可以,若是輸入 explorer . 是沒有效果的。

Windows 與 WSL Distro 之間的網路服務如何互通

  • Windows -> WSL 2

    你所有從 WSL 2 運行的網路服務,只要監聽本機網路介面(loopback interface),預設在 Windows 就可以透過 localhost 直接連接,非常方便!

    例如你在 WSL 2 中執行 MySQL 資料庫伺服器,從 Windows 連過去就只要從 localhost:3306 即可連上。

    如果想在 WSL 2 任何一個 WSL Distro 安裝資料庫,可以參考 Get started with databases on Windows Subsystem for Linux 這份文件。

  • WSL 2 -> Windows

    你所有從 Windows 運行的網路服務,只要是能對外連接的服務(如 IIS, SQL Server, ... 等),都可以從 WSL 2 直接連過去,但是要先找到對的 IP 地址才可以連接。

    要先透過 cat /etc/resolv.conf 取得 WSL 2 的 Linux 下註冊的 nameserver IP 地址,這個 IP 地址就是 Windows 本機的網路介面 IP,你可以透過這個 IP 從 WSL 2 連接到 Windows 本機的網路服務。

    注意:對 Windows 來說,這算是「公開網路」連線,記得要看一下防火牆(wf.msc)設定是否有封鎖「公開網路」的連內規則(Inbound Rules)。

    如果你在 Windows 命令提示字元執行 ipconfig 的話,會看到有個標示 (WSL) 字樣的網路介面,這個 IPv4 地址(172.19.239.1)就是你可以從 WSL Distro 內部連接到 Windows 主機的 IP 地址:

    Ethernet adapter vEthernet (WSL):
    
      Connection-specific DNS Suffix  . :
      Link-local IPv6 Address . . . . . : fe80::99e5:4b5:5d8a:7eeb%39
      IPv4 Address. . . . . . . . . . . : 172.19.239.1
      Subnet Mask . . . . . . . . . . . : 255.255.240.0
      Default Gateway . . . . . . . . . :
    

    由於每次都要找本機 IP 很麻煩,我直接寫成以下命令,放到 ~/.profile 啟動檔,以後直接透過 $local 就可以自動取得 Windows 本機的 IP 地址:

    export local=$(cat /etc/resolv.conf | grep nameserver | cut -d ' ' -f 2)
    

指定「特定使用者」登入 WSL Distro 執行

  • 指定「特定使用者」登入(而且可以免密碼就能直接登入到 Shell 環境)

    wsl -d Ubuntu-20.04 -u root
    

    注意:登你可以用 root 登入系統,自然可以用 passwd <LOGIN> 來修改任何帳戶的密碼!

關閉 WSL Distro 執行

  • 關閉特定 WSL Distro 執行

    wsl -t WLinux
    
  • 關閉所有 WSL Distro 執行(等同於關閉 VM 執行)

    wsl --shutdown
    

移除 WSL Distro 版本

注意:所有該 WSL Distro 下所有的檔案,都會被全數刪除,刪除後無法復原喔!

wsl.exe --unregister WLinux2

相關連結

留言評論