セットアップ - Docker on WSL 2 + Windows ターミナル

# はじめに

[**セットアップ シリーズ**](https://qiita.com/yokarikeri/items/a4e41a36c610bb1c798b)、Windows 10 向け **Docker** 編。
macOS なら Docker Desktop をインストールするだけで良いが、**WSL** に依存する Windows だとそうもいかない。

便利なので、WSL のついでに **Windows ターミナル**も紹介する。

**確認環境:**

- Windows 10 Home バージョン 20H2（ビルド 19042）
- Windows 10 Home バージョン 20H1（2004、ビルド 19041）

## WSL とは？

[公式サイト](https://docs.microsoft.com/ja-jp/windows/wsl/faq)では、**WSL**（Windows Subsystem for Linux）を以下のように説明している。

> **WSL の対象ユーザーは誰ですか。**
>
> これは、主に開発者向けのツールです。
> 特に Web 開発者と、オープン ソース プロジェクトで作業している開発者が対象です。
> これにより、Bash、一般的な Linux ツール (sed、 awk など)、および多くの Linux ファースト ツール (Ruby、Python など) を使用する必要があるユーザーは、Windows でそれらのツールチェーンを使用できます。
>
> **WSL ですべての Linux アプリを実行できますか。**
>
> いいえ。 WSL は、Bash およびコア Linux コマンド ライン ツールを必要とするユーザーが Windows 上でそれらを実行できるようにすることを目的としたツールです。
> WSL は、GUI デスクトップやアプリケーション (例: Gnome、KDE など) をサポートすることを目的として**いません**。更新については、[コマンドラインのブログ](https://devblogs.microsoft.com/commandline/whats-new-in-the-windows-subsystem-for-linux-september-2020/#gui-apps)を参照してください。
> また、多くの一般的なサーバー アプリケーション (Redis など) を実行できる場合でも、実稼働サービスをホストする目的には WSL をお勧めしません。
> Microsoft は、Azure、Hyper-V、Docker で実稼働 Linux ワークロードを実行するためのさまざまなソリューションを提供しています。

**もう少し細かい話:**

- WSL は、従来の「仮想マシン」や「デュアルブート」よりも軽量に、「Windows 環境」と「GNU/Linux 環境」を併用できる仕組み。
- WSL はただの仮想マシンではなく、Linux と Windows との**併用**を前提としている。
- 今では「WSL 1」と呼ばれる初代から進化して、本物の Linux カーネルを直接実行する「**WSL 2**」ができた。

**WSL 2 の詳細:**

- Windows 10 バージョン **2004**（20H1）で正式リリースされた。
    - 開発中のプレビュー版を利用する Windows Insider Program への参加が不要に。
- Windows 10 に Linux カーネルが含まれており、マシン上では「Windows カーネル」と「Linux カーネル」が**並行稼働**する。
    - WSL 2 は「Light Weight utility Virtual Machine」（軽量ユーティリティー VM。単に **WSL 2 VM** とも）という専用の仮想マシン上で動作することになる。
    - cf. [完全な Linux が Windows 10 上で稼働する？　「WSL 2」とは：Windows 10 The Latest - ＠IT](https://www.atmarkit.co.jp/ait/articles/1906/14/news019.html)
- Linux 側のファイルシステムは**仮想ディスク**を使う。
    - そのためルート配下は Linux の支配下で、パスには Windows の禁則文字も使えるが、Linux にマウントされた Windows 上のファイルシステムは Windows の規則に従う。
    - WSL 1 と比べ、WSL 2 は Windows ファイルシステムへのアクセスが低速なため、Linux 側のファイルシステムと往復する場合は WSL 1 の利用が推奨される。
- その他、「WSL 1」と「WSL 2」の細かい違いは以下。
    - cf. [WSL 2 と WSL 1 の比較 | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/wsl/compare-versions)
    - cf. [Announcing WSL 2 | Windows Command Line](https://devblogs.microsoft.com/commandline/announcing-wsl-2/)

## Docker とは？

- Docker 社が提供する、あらゆるアプリケーションをどこでも開発・導入・実行するためのプラットフォーム。
    - 正確には、Linux ファイルシステムのスナップショットを Docker イメージとしてパッケージ化し、コンテナーとして動かす。
        - イメージは、Dockerfile（テキストファイル）上の命令文から簡単に作成できる。各命令文がレイヤーとして積み重なってイメージを作る。
        - コンテナーは、読み取り専用レイヤーのみで構成されるイメージに、書込み可能な最上位レイヤーが追加されたもの。
    - コンテナーはホスト上のプロセス ツリーから独立したプロセスで、自身のファイルシステムとネットワークを持つ。
- WSL 2 の登場以降、Docker Desktop はデフォルトでは「WSL 2 VM」上で以下の 2 つのディストリビューションを動作させる方式が採用された。
    - `docker-desktop`: ブートストラップ ディストリビューション
        - Docker エンジン本体
    - `docker-desktop-data`: データ ストア ディストリビューション
        - Docker イメージやコンテナーを格納
        - WSL 2 は、現時点では追加の仮想ディスク（VHD）を直接マウントできないため、クロス ディストリビューション マウントする方式になったそうだ
- Windows 用 Docker のバックエンドが「WSL 2」の場合と、それ以前の「Hyper-V」の場合の違いは以下。
    - cf. [Introducing the Docker Desktop WSL 2 Backend - Docker Blog](https://www.docker.com/blog/new-docker-desktop-wsl2-backend/)

## Windows ターミナルとは？

- Microsoft 社の提供する、ターミナル アプリケーション。
- コマンド プロンプト、PowerShell、WSL などの各種コマンドライン ツールを横断的に扱うことができる。
- 今回は、Ubuntu（WSL）を利用するための使う。

# 👑 WSL 2

> cf.[Windows Subsystem for Linux (WSL) を Windows 10 にインストールする | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/wsl/install-win10#step-2--check-requirements-for-running-wsl-2)

**システム要件:**

この手順は x64 マシンを想定する。

- x64 システムの場合: Windows 10 バージョン 1903 以降、ビルド 18362 以上。
- cf. [Windows 10 のビルド番号一覧](https://docs.microsoft.com/ja-jp/windows-insider/flight-hub/)

**インストール方法の違い:**

WSL は、現在ふたつのインストール方法がある。

- ➊ 簡略化されたインストール
    - Windows Insider Program に参加して、Windows 10 のプレビュー ビルド（**OS ビルド 20262** 以降）を使う
- ➋ 手動インストール
    - Windows Insider Program に参加していない場合用

プレビュー版の Windows が使いたいわけではないので、「➋ 手動インストール」の方法を採用する。

**インストール:**

[公式の手順](https://docs.microsoft.com/ja-jp/windows/wsl/install-win10#step-2--check-requirements-for-running-wsl-2)どおり。悩む点はない。

```sh
# 管理者として PowerShell を開く

# Linux 用 Windows サブシステムを有効にする
PS C:\> dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

# 仮想マシン プラットフォーム オプション機能を有効にする
PS C:\> dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

# 以下のパッケージをダウンロードし、インストール
# x64 マシン用 WSL2 Linux カーネル更新プログラム パッケージ
# https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi

# WSL 2 を既定のバージョンとして設定（デフォルトでは WSL 1 を使わないように）
PS C:\> wsl --set-default-version 2
```

**設定:** メモリ枯渇問題への対策

メモ帳などの任意のテキストエディターで `%USERPROFILE%\.wslconfig`（`C:\Users\<yourUserName>\.wslconfig`） に、以下のファイルを作成する。
（この機能は Windows 10 **ビルド 19041** 以降でしか使えない。）

```conf:.wslconfig
[wsl2]
memory=8GB
```

この手順では、上記の設定反映の手順は不要。（この時点で WSL 2 VM が起動してないので）

もし稼働中の WSL 2 VM があれば再起動することで適用される（`wsl --shutdown` or `wsl -t <ディストリビューション名>`）。面倒なら **Windows を再起動**でも良い。

（以下、細かいことが気になる方向け）

- 現時点の WSL 2 でも、残念ながら「[メモリを食いつぶす問題](https://github.com/microsoft/WSL/issues/4166)」は残っているらしい。
    - たしかに Vmmem プロセス（WSL 2 VM 全体のプロセス？）のメモリー使用率は高いので気になる。
- [GitHub 上の Issues のアドバイス](https://github.com/microsoft/WSL/issues/4166#issuecomment-663487284)にしたがって、WSL のグローバルオプション設定である `.wslconfig` を作成し、WSL 2 VM が確保する**メモリー上限**を設定している。
- [公式サイト](https://docs.microsoft.com/ja-jp/windows/wsl/wsl-config#configure-global-options-with-wslconfig)によれば、デフォルトのままだと PC 搭載メモリーの **80 %** が上限となるそうなので、もっと安全な値に変更している。
    - Windows 10 ビルド 20175 以降（プレビュー版）なら、デフォルトは搭載メモリーの **50 %** or **8 GB** の少ない方が上限となり、この変更をする必要はない。
    - 今回は「ビルド 20175 以降の仕様」を参考に、とりあえず 8 GB にする。
        - [Ubuntu 20.04 Server Edition のシステム要件](https://ubuntu.com/server/docs/installation)を参考にすると、RAM は 1 GB 以上。
        - [Docker のシステム要件](https://docs.docker.com/docker-for-windows/install-windows-home/)の RAM は 4 GB 以上。
- 上記 [Qiita の参考記事](https://qiita.com/yoichiwo7/items/e3e13b6fe2f32c4c6120#%E6%9C%AC%E5%95%8F%E9%A1%8C%E3%81%8C%E8%B5%B7%E3%81%8D%E3%82%84%E3%81%99%E3%81%84%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9)では `swap=0` でスワップの無効化を設定しているが、後述のとおり利用するドライブを SSD から HDD へ変更するので、今回の手順では不要。

**補足:**

> cf. [Linux ディストリビューションの管理 | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/wsl/wsl-config#configure-per-distro-launch-settings-with-wslconf)

ディストリビューションごとの起動設定は、各ディストリビューション内の `/etc/wsl.conf` を参照する。
たとえば `[interop]` セクションの `appendWindowsPath` を `false` にすることで、WSL が Windows パス要素を $PATH 環境変数に追加しないようにできる。
今回は特に困らなかったので、設定しない。

# 👑 Ubuntu（WSL）

WSL VM 上に、Linux ディストリビューションをインストールする。

Docker を使う上では必須ではないが、WSL を試す際には何かと必要になる。
（Linux ディストリビューションがひとつもインストールされていないとエラーになったりして後々面倒なことも。）

こだわりはないが、WSL は Ubuntu の開発元の Canonical 社との連携で生まれたようなので、相性の良さそうな「**Ubuntu**」を採用。

なお、Windows ストア上の「Alpine Linux」は、開発元（Alpine Linux development team）が公開しているわけではなさそうなので、採用を見送り。

**インストール:**

利用可能な Ubuntu のリリースは以下で確認できる。
<https://wiki.ubuntu.com/WSL#Ubuntu_on_WSL>

Windows ストアへのリンクがあるので、そこからインストール。

各 LTS もあるが、今回はアプリ名に「リリースバージョンなし」の常に推奨リリースを使うものを採用。
（現在は「Ubuntu 20.04 LTS」がインストールされる。）

念の為にインストールしたディストリビューションを確認しておく。

```sh
# WSL にインストールされているディストリビューションとその詳細を確認
## --list, -l     ディストリビューションの一覧を表示します。
## --verbose, -v  すべてのディストリビューションについての詳細情報を表示します。
PS C:> wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Stopped         2

# バージョンが 2 なら、WSL 2 で動作している

# デフォルトのディストリビューションを変更する場合は以下
# wsl --set-default <ディストリビューション名>

# すでに WSL 1 で動作しているディストリビューションを WSL 2 へ移行するには以下（逆もできる）
# wsl --set-version <ディストリビューション名> 2
```

**使い方:**

Windows ストアからインストール後、「Ubuntu」を起動すると、Ubuntu が構築 & 起動するまでしばらく待たされる。
起動後、Linux ユーザー名とパスワードを設定を促されるので入力。（パスワードはスーパーユーザーへの昇格時に必要となるなので忘れないように。）

`exit` コマンドで終了。

なお、WSL 上では `poweroff` や `reboot` コマンドは制限されており、利用できない。
（おそらく、WLS はひとつのハードウェア上で Windows と Linux を併用するので、何をシャットダウンするコマンドか分かりにくいため？）

どうしてもディストリビューション（WSL 2 VM）を停止させたい場合は、PowerShell などから `wsl -t <ディストリビューション名>` コマンドを使う。

**設定:**

> cf. [Docker のイメージ&コンテナの配置フォルダを変更 - Qiita](https://qiita.com/nobunobunobupepe/items/cb704951c85b4029c355)

WSL 2 VM のディスクは「仮想ディスク」（.vhdx）形式で保持される。

デフォルトの保存先は `%LocalAppData%`（`C:\Users\<yourUserName>\AppData\Local`）配下のため、C ドライブが SSD の場合はよろしくない。
cf. [SSD の選び方(3/5)：SSD の寿命を縮める使いかた - Qiita](https://qiita.com/ken-yossy/items/094533052380e9d3f96c)

Windows ストアから取得したディストリビューションの仮想ディスクの保存先は、以下の方法で確認できる。

```sh
# cf. [Windows Subsystem for Linux 2(WSL 2)をセットアップしてみた | Developers.IO](https://dev.classmethod.jp/articles/how-to-setup-wsl2/)

# PackageFamilyName を取得
PS C:> $packageName = Get-AppxPackage -Name "*Ubuntu*" | Select-Object -ExpandProperty PackageFamilyName
PS C:> $packageName

# 対象フォルダーを確認
PS C:> dir "$env:LOCALAPPDATA\Packages\$packageName\LocalState\"
```

仮想ディスクを「別のドライブ」へ移行する手順は以下のとおり。
今回は HDD の D ドライブがあるのでそこに。

```sh
# 利用中の WSL 2 VM があれば切断する

# 稼働中の WSL 2 VM を確認
PS C:> wsl -l -v

# 稼働中のものがあれば、以下のコマンドですべて終了させる
PS C:> wsl --shutdown

# vhdx を tar としてエクスポート、登録解除、tar をインポート
PS C:> cd D:\
PS D:> wsl --export <ディストリビューション名> <ディストリビューション名>.tar
PS D:> wsl --unregister <ディストリビューション名>
PS D:> wsl --import <ディストリビューション名> <インストール先パス> D:\<ディストリビューション名>.tar

# 例:
# wsl --export Ubuntu Ubuntu.tar
# wsl --unregister Ubuntu
# wsl --import Ubuntu D:\wsl\Ubuntu D:\Ubuntu.tar

# 複数の WSL 2 VM がある場合は、上記手順を繰り返す

# WSL 2 VM を再確認
PS D:> wsl -l -v

# デフォルトのディストリビューションが変更されている場合は、以下のコマンドで再指定
PS D:> wsl --set-default <ディストリビューション名>
```

## zsh、tmux について

- 別記事参照。
    - [セットアップ - zsh - Qiita](https://qiita.com/yokarikeri/items/1c78edf32af41339ffec)
    - [セットアップ - tmux - Qiita](https://qiita.com/yokarikeri/items/1c9a228fc9b462c53755)

# 👑 Windows ターミナル

> cf. [Windows ターミナルのインストール | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows/terminal/get-started)

**インストール:**

Windows ストアからインストールするだけ。
<https://aka.ms/terminal>

**設定:**

起動時に「Ubuntu (WSL)」を開くなど、設定を調整する。

1. Windows ターミナルを起動する。
1. 設定 (`Ctrl` + `,`) を開き、`既定のプロファイル` を `Ubuntu` に変更 > `保存` ボタンをクリック。
1. 画面左のサイドバーの `三` ボタン > `Ubuntu` > `外観` タブで、以下を設定して `保存` ボタンをクリック。
    - 配色: [One Half Dark](https://docs.microsoft.com/ja-jp/windows/terminal/customize-settings/color-schemes#included-color-schemes)
    - フォント フェイス: [HackGenNerd Console](https://github.com/yuru7/HackGen/releases)
        - Windows ターミナルの公式上では [Cascadia（カスカディア）](https://github.com/microsoft/cascadia-code) が提供されているが、日本語フォントを含んでいないので、採用は見送り。
    - フォントサイズ: 10
    - ポインター: [アンダースコア（▁）](https://docs.microsoft.com/ja-jp/windows/terminal/customize-settings/profile-appearance#cursor)
    - 背景画像: [Ubuntu 20.04 LTS (Focal Fossa)](https://discourse.ubuntu.com/t/focal-fossa-mascot-wallpapers/14621)
1. 画面左のサイドバーの `三` ボタン > `Azure Cloud Shell` > `ドロップダウンからプロファイルを非表示にする` を有効に > `保存` ボタンをクリック。
    - 更に、Azure Cloud Shell を使う予定がないので、非表示に。

# 👑 Docker Desktop

> cf. [Install Docker Desktop on Windows | Docker Documentation](https://docs.docker.com/docker-for-windows/install/)

やっと Docker Desktop のセットアップに入る。
（Docker Engine、Docker CLI client、Docker Compose、Notary、Kubernetes、Credential Helper が含まれる）

**システム要件:**

- WSL 2 が有効なこと
- [WSL2 Linux カーネル更新プログラム パッケージ](https://aka.ms/wsl2kernel) がインストールされていること
    - インストールされていない場合は、途中でインストールを促される。

**インストール:**

インストーラーは以下から入手。
cf. [Docker Desktop for Mac and Windows | Docker](https://www.docker.com/products/docker-desktop)

1. `Docker Desktop Installer.exe` を実行する。
2. `Install required Windows components for WSL 2`（WSL 2 に必要な Windows コンポーネントをインストール）にチェックが入っていることを確認する。
3. インストーラーに促されるまま OS 再起動後する。
4. Docker Desktop が再起動するので、しばらく待つ。
   （タスクトレイのクジラのアイコンのアニメーションが止まるまで**数分**かかった）
5. タスクトレイから Docker Desktop の画面を表示すると、チュートリアルが開始される。
   （別に初学者に優しいチュートリアルでもないなので、ファイアウォールで通信をあらかじめ許可することが主な役割だと割り切って**スキップしても良い**）

```sh
# 一応、チュートリアル内容について説明する。

# alpine/git の Docker イメージを取得 & コンテナーとして起動し、コンテナー内で clone コマンドを実行
## --name オプションで、「コンテナー名」を割り当て
## cf. alpine/git <https://hub.docker.com/r/alpine/git>
$ docker run --name repo alpine/git clone https://github.com/docker/getting-stared.git

# コンテナー内の clone したリポジトリーを、ホストのカレントディレクトリーへコピー
$ docker cp repo:/git/getting-started/ .

# コピーしたフォルダーへ移動
$ cd getting-started

# コピーしたフォルダー内の Dockerfile を元に、「docker101tutorial」というイメージ名で Docker イメージを作成
## -t オプションで、「イメージ名」を割り当て（タグを省略したため、自動的に latest に）
$ docker build -t docker101tutorial .

# 作成した Docker イメージ「docker101tutorial」をコンテナーとして起動
## -d オプションで、バックグラウンド実行
## -p <ホスト側のポート>:<コンテナー側のポート> で、ホストとコンテナー間のポートフォワード設定（デフォルトだと、コンテナー内と通信できないため）
## --name オプションで、「コンテナー名」を割り当て
$ docker run -d -p 80:80 --name docker-tutorial docker101tutorial
```

6. Windows Defender ファイアウォールなどが Docker をブロックするので、許可する。
7. この後の `You must be signed in to Docker Hub to share your image. Sign in here.` 以降は、
   Docker Hub の案内なので**無視**しても良い。（Docker イメージを公開する予定がなければ関係ないし、あとでもできる）
8. チュートリアルの最後に、`docker101tutorial` のコンテナー内の Web サーバー上のページが表示される。（`http://localhost/tutorial/`）
9. 確認が終われば Docker Desktop の GUI から `docker101tutorial` コンテナーを停止させる。

**確認:**

```sh
# WSL にインストールされているディストリビューションとその詳細を確認
## --list, -l     ディストリビューションの一覧を表示します。
## --verbose, -v  すべてのディストリビューションについての詳細情報を表示します。
PS C:\Users\user> wsl -l -v
  NAME                   STATE           VERSION
  docker-desktop-data    Running         2
  docker-desktop         Running         2

# バージョンが 2 なら、WSL 2 で動作している
```

**追加設定:**

> cf. [WSL2 Docker のイメージ・コンテナの格納先を変更したい (WSL2 の vhdx ファイルを移動させたい) - Qiita](https://qiita.com/neko_the_shadow/items/ae87b2480345152bc3cb)

Docker の使う WSL 2 VM の仮想ディスク（VHD）のドライブを変更する手順は、以下のとおり。
（SSD の寿命問題への対策。手順は上記の Ubuntu のものと同じ）

- 参考: デフォルトの仮想ディスクの格納先パス
    - `docker-desktop`: `%LocalAppData%\Docker\wsl\data\ext4.vhdx`
    - `docker-desktop-data`: `%LocalAppData%\Docker\wsl\distro\ext4.vhdx`

```sh
# タスクトレイのクジラのアイコンを右クリック > Quit Docker Desktop

# その他に利用中の WSL 2 VM があれば切断する

# 稼働中の WSL 2 VM を確認
PS C:> wsl -l -v

# 稼働中のものがあれば、以下のコマンドですべて終了させる
PS C:> wsl --shutdown

# vhdx を tar としてエクスポート、登録解除、tar をインポート
PS C:> cd D:\
PS C:> wsl --export <ディストリビューション名> <ディストリビューション名>.tar
PS C:> wsl --unregister <ディストリビューション名>
PS C:> wsl --import <ディストリビューション名> <インストール先パス> D:\<ディストリビューション名>.tar

# 例:
# wsl --export docker-desktop docker-desktop.tar
# wsl --unregister docker-desktop
# wsl --import docker-desktop D:\wsl\docker-desktop D:\doc\docker-desktop.tar
#
# wsl --export docker-desktop-data docker-desktop-data.tar
# wsl --unregister docker-desktop-data
# wsl --import docker-desktop-data D:\wsl\docker-desktop-data D:\doc\docker-desktop-data.tar

# WSL 2 VM を再確認
PS C:> wsl -l -v

# デフォルトのディストリビューションが変更されている場合は、以下のコマンドで再指定
PS C:> wsl --set-default <ディストリビューション名>
```