WSL2 + Ubuntu 20.04 + Docker 開発環境構築

## 序論

本稿は [元市役所職員がWEBプログラマに転職するまでのロードマップ](https://qiita.com/amenoyoya/items/3b63a3fead17bc656f34) の連載記事の一部です。

まだ、WEBプログラマに転職してから2年も経過していない素人であるため、色々と間違っていることを書いていたりするかと思います。
その際はお手数ではございますが、ご指摘いただければ幸いでございます。

## 仮想化技術

WSL2やDockerの開発環境を構築する前に、仮想化技術について簡單に触れておきます。

仮想化技術には大きく以下の3種類があり、いずれも「隔離されたアプリケーション実行環境」を提供するものです。

- ホスト型
    - ![vtype-host.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/1e58dde6-650a-7901-05dd-5b41ae87728b.png)
    - ホストOSの上に別のOS（ゲストOS）をインストールする仮想化技術
    - ゲストOSをそのまま仮想環境にインストールするため、ある程度の再現性が担保されている
    - ディスク容量やメモリ使用量が大きい、動作が重い・不安定などの欠点がある
- ハイパーバイザ型
    - ![vtype-hyper.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/6933ee11-a0c3-583e-2ffd-99af7fb49032.png)
    - ハイパーバイザとはホストOSそのものを仮想化する制御プログラム
    - ホスト型より性能劣化が低い
    - ゲストOSとして使用できるOSに制限がある
- コンテナ型
    - ![vtype-container.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/dd6ff986-f5bd-4f58-a72e-a725f83efb9f.png)
    - ホストOSのカーネルを流用して隔離されたアプリケーション実行環境（コンテナ）を実現する仮想化技術
    - 性能劣化がほぼなく、安定して動作する
    - カーネルの異なるゲストOSを利用することはできない（例えば、Linuxホスト上でコンテナとして利用できるのはLinux系OSのみであり、Windows OS等を使うことはできない）

この仮想化技術の発展により、Web開発は大きく進展したと言われています。

従来は、物理的なサーバマシンに様々なアプリケーションをまとめて放り込んでいるような状態であったため、以下のような問題がありました。

- 開発環境とサーバ環境を同一の状態にすることが難しく、開発時に動作していたものがサーバ公開時に動作しなくなるなどの問題が発生しやすい
- 一つのアプリケーションに問題が発生した場合、他の正常稼働しているアプリケーションにも影響が出る
- サーバ用途の転用が困難（ハードウェア構成の変更作業が発生する）
- アプリケーション・ミドルウェアの構成を自由に変更することが困難
- アプリケーション・ミドルウェアの構成を自動化することが困難

上記のような問題のほとんどが、仮想化技術により解決されたと言われております。

特にコンテナ型の仮想化技術は、ホストOSの上で直接動作するため、ほぼ性能劣化することなく隔離されたアプリケーション実行環境を提供することができるとされております。
そのため、現在のWeb開発においてはこのコンテナ型仮想環境を利用するのが主流になりつつあり、特に、Docker社の開発した **Docker** は、コンテナ型仮想化技術として広く普及しています。

実際、Google社のWebサービスなどはあらゆるものがコンテナ化されて運用されているという話もあります。

### WindowsにおけるWEB開発
前述の通り、WEB開発においてはコンテナ型仮想環境を用いてアプリケーション実行環境ごと隔離して開発するのがスタンダードとなってきています。
しかしながら、WEBサーバとして利用されるマシンはLinux系OSがほとんどであり、Dockerも基本的にはLinux用に開発されています。
そのため、Windows等の別のOS上で動かすには、VirtualBox や VMware 等のホスト型仮想環境の上にLinux系OSをインストールして使うか、WSL2（ハイパーバイザ型仮想環境）上にLinux系OSをインストールして使うことになります。

WSL2 が正式リリースされた 2020年5月 までは、VirtualBox + Vagrant (環境構築自動化ツール) というホスト型仮想環境を使うことが多かったように見受けられますが、以下のような問題があり、WindowsでWEB開発を行う場合は WSL2 を使うことが多くなってきている気がします。（筆者の個人的な感覚なので、情報ソースは曖昧です）

- VirtualBox, Vagrant, Vagrant Plugin のバージョンごとに相性があり、バージョンが変わるだけで上手く動作しないことが多い
- ホスト型仮想環境であるため、メモリ使用量が比較的多い
- WindowsファイルシステムとLinuxファイルシステムの相互変換コストが大きく、動作が遅かったり、ハードリンク系のファイル操作が上手く働かなかったりする
- GPUリソースを扱うことができない

### WSL2について
正式名称 Windows Subsystem for Linux 2 で、ハイパーバイザ型の仮想環境です。

VirtualBox + Vagrant や VMware を使うよりシームレスに Linux 環境を利用することができ、個人的には仮想環境由来のおかしなトラブルが大きく減ったと感じております。

ただし、Windowsにおけるハイパーバイザ型仮想環境の宿命として、ホスト型仮想環境との共存はできないため、**Virtual + Vagrant や VMware の環境は封印する必要があります** (※ **VMWare は現在 Hyper-V をサポートしているため共存可能**です)。

本稿では、WSL2 を導入し、その上に Ubuntu 20.04 (Linux系OSの中でも最近人気の高いディストリビューション) をインストール => Docker 環境を構築します。

- ※ 2021/11/13 追記:
    - VMWare については、VMWare Workstation 15.5.5 以降、Hyper-V をサポートするようになりました
        - https://blogs.vmware.com/workstation/2020/05/vmware-workstation-now-supports-hyper-v-mode.html
    - そのため、WSL2 と VMWare は共存可能です（以下の手順で最新の VMWare Workstation Player をインストールして確認済み）
        - Chocolatey で VMWare Workstation Player インストール
            - 管理者権限 PowerShell: `choco install -y vmware-workstation-player`
        - PC再起動
            - 管理者権限 PowerShell: `shutdown /r /t 0`
        - VMWare Workstation Player 起動（無償で使用する）
            - ![vmware-start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/3e11138f-4727-5181-fd66-bfcddf133aab.png)
        - 適当な仮想マシンを作成して起動

## Environment
- Host
    - OS: `Windows 10`
        - バージョン 2004, ビルド 19041 以上
- Guest
    - OS: `Ubuntu 20.04`
    - Linuxbrew: `2.4.2`
    - anyenv: `1.1.1`
        - pyenv: `1.2.19`
            - Python2: `2.7.18`
            - Python3: `3.7.7`
            - pip package manager: `20.1.1`
            - AWS CLI: `1.18.93`
        - nodenv: `1.3.2`
            - Node.js: `10.17.0`
            - Yarn package manager: `1.22.4`
            - Gulp task runner: `2.3.0`
    - PHP: `7.4.3`
        - composer package manager: `1.10.8`
    - Docker: `19.03.12`
        - docker-compose: `1.26.0`
    - Ansible: `2.9.10`

### Setup
まず、WSL1 を導入し、その上に Ubuntu 20.04 をインストールします。

`Win + X` |> `A` キーで管理者権限 PowerShell を起動し、以下のコマンドを実行します。

```powershell
# Windows Subsystem for Linux を有効化する
> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
この操作を完了するために、今すぐコンピューターを再起動しますか?
[Y] Yes  [N] No  [?] ヘルプ (既定値は "Y"): # そのままENTERして再起動

# 再起動したら Ubuntu 20.04 ディストロパッケージをダウンロード
## 「ダウンロード」ディレクトリに ubuntu2004.appx というファイル名でダウンロード
> Invoke-WebRequest -Uri https://aka.ms/wslubuntu2004 -OutFile ~\Downloads\ubuntu2004.appx -UseBasicParsing

# ダウンロードしたディストロパッケージをWSLにインストール
> Add-AppxPackage ~\Downloads\ubuntu2004.appx
```

インストールが完了したら、Windows スタートメニューから `Ubuntu 20.04 LTS` を起動します。

```bash
# -- Ubuntu 20.04 Terminal

# 初回起動時は初期設定が必要
Installing, this may take a few minutes...
Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: # <= ログインユーザ名を設定
Enter new UNIX password: # <= ログインパスワードを設定（sudo コマンド実行時等に必要なため忘れないようにする）
Retype new UNIX password: # <= ログインパスワードをもう一度入力

# 初期設定を行うと WSL に Ubuntu 20.04 ディストロが追加される
# ここで一旦終了する
$ exit
```

### WSL2 へのアップグレード
WSL1 では、完全にすべてのLinuxプログラムが動作するわけではありません。（例えば、複数のDockerコンテナを管理する docker-compose などは動作しない）

一方で、WSL2 は完全なLinuxカーネルを使用しており、docker-compose 等も問題なく動作します。

WSL2 は Windows 10 バージョン 2004 で一般提供されておりますが、もしお使いの Windows 10 のバージョンがそれより低い場合は、Windows Update を実行する必要があります。（ここでは説明割愛）

`Win + X` |> `A` キーで管理者権限 PowerShell を起動し、以下のコマンドでWSL2へのアップグレードを行います。

```powershell
# WSL2 を使うために、Windows仮想化機能（Hyper-V）を有効化
> Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
この操作を完了するために、今すぐコンピューターを再起動しますか?
[Y] Yes  [N] No  [?] ヘルプ (既定値は "Y"): # そのままENTERして再起動

# 再起動が完了したらWSLのバージョン確認
## 現状の Ubuntu 20.04 は Version 1 になっているはず
> wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Stopped         1

# 先にインストールしていた Ubuntu 20.04 を WSL2 環境に変換する
> wsl --set-version Ubuntu-20.04 2

# 「WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。」というエラーが出た場合
## => https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi をインストールして再実行する

# 変換が完了したらバージョン確認
## Ubuntu 20.04 が Version 2 になっていればOK
> wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Stopped         2
```

### 開発ディレクトリについて（重要）
WSL2 環境において開発ディレクトリをどこに置くかは重要です。

開発ディレクトリを Windowsファイルシステム側（Linuxパス: `/mnt/c/...`）に置いた場合、ファイル IO が異常に遅く、一部 Docker 環境ではネットワーク通信に不具合が発生するなどの問題が起こります。
そのため、基本的には `\\wsl$\Ubuntu-20.04\home\<ユーザ名>`（Linuxパス: `/home/<ユーザ名>`）など、Linuxファイルシステム側に開発ディレクトリを置く必要があります。
開発ディレクトリがLinuxファイルシステム側に置いてあれば、Dockerプロジェクトも安定・軽快に動かすことができます。（少なくとも今のところは）

![wsl2_path_windows.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/4ea364ec-40e9-950a-50f8-15c1fc226321.png)

![wsl2_path_linux.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/c2a10595-a183-67c1-4a37-8e485453c3f8.png)

## Ubuntu 20.04 Setup

Ubuntu 20.04 on WSL2 は、以下のいずれかの方法で起動できます。

1. Windowsスタートメニューの `Ubuntu 20.04 LTS` から起動
2. PowerShell で `start wsl` コマンドから起動
3. Windowsエクスプローラのアドレスバーに `wsl` と打てばエクスプローラで開いているディレクトリ内で起動することも可能

![wsl_from_explorer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426092/d33c7c1b-14c4-6a87-058f-38944b3f0a1a.png)

### 開発ツール導入
```bash
# -- Ubuntu 20.04 on WSL2

# Linuxシステムアップデート
$ sudo apt update && sudo apt upgrade -y

# Linuxbew の動作に必要な curl, git, ruby をインストール
## openjdk は android 開発を行う時など必要になるタイミングが多いため一応インストールしている
## zlib1g-dev, libssl-dev, libbz2-dev, libsqlite3-dev, libffi-dev, liblzma-dev は Python ビルドに必要
## add-apt-repository コマンドを使うために software-properties-common もインストールしておく
## https通信を可能にするために apt-transport-https, ca-certificates もインストールしておく
$ sudo apt install -y vim curl git ruby openjdk-14-jdk \
    zlib1g-dev libssl-dev libbz2-dev libsqlite3-dev libffi-dev liblzma-dev \
    software-properties-common apt-transport-https ca-certificates build-essential

# Linuxbrew (Linux版の Homebrew パッケージマネージャ) 導入
## Linuxbrew を使うことで最新の開発ツール等を導入しやすくなる
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
## PATHを通す
$ echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

## Linuxbrew をアンインストールする場合
# $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"
## 残ってしまった場合は直接ディレクトリ削除
# $ sudo rm -rf /home/linuxbrew/

# Linuxbrew で各種開発ツールを導入
## curl や git などは、最新版を使う方が良いため、改めて Linuxbrew で導入しなおす
$ brew install curl git wget gcc zlib libzip bzip2 readline openssl pkg-config autoconf
```

### anyenv 導入
- **anyenv**
    - env系開発環境をまとめて管理できるツール
    - env系開発環境とは、pyenv, nodenv など、各プログラミング言語の複数バージョンを切り替えて使用可能とする環境のこと
    - 独自に導入した env系開発環境がある場合は、それらを削除してから導入すること

```bash
# -- Ubuntu 20.04 on WSL2

# Linuxbrew で anyenv 導入
$ brew install anyenv
$ anyenv install --init
## Do you want to checkout ? [y/N]: <= y

# anyenv 初期化スクリプトを .bashrc に記述
$ echo 'eval "$(anyenv init -)"' >> ~/.bashrc
$ source ~/.bashrc

# anyenv update plugin の導入
$ mkdir -p $(anyenv root)/plugins
$ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$ anyenv update

# バージョン確認
$ anyenv -v
anyenv 1.1.1
```

### Python 環境構築
Python は、AWS CLI や Ansible の他にも、Node.js の native-addon-build-tool などにも使われています。
Python 自体を開発言語として使わなくても、様々なツールの動作に必要になることが多いため、導入しておくことを推奨しています。

```bash
# -- Ubuntu 20.04 on WSL2

# anyenv を使って pyenv 導入
## pyenv を使うことで、複数バージョンの Python 環境を構築できる
$ anyenv install pyenv
$ exec $SHELL -l

# pyenv で Python 2.7.18 と 3.7.7 をインストール
$ pyenv install 2.7.18
$ pyenv install 3.7.7

# pyenv では 2系 と 3系 を同時に指定できる
## python  => 2.7.18
## python3 => 3.7.7
$ pyenv global 2.7.18 3.7.7

# 現在選択されているバージョンを確認
$ pyenv versions
* 2.7.18 (set by /home/user/.anyenv/envs/pyenv/version)
* 3.7.7 (set by /home/user/.anyenv/envs/pyenv/version)

$ python --version
2.7.18

$ python3 --version
3.7.7

# pip パッケージマネージャを更新しておく
$ pip install --upgrade pip setuptools
$ pip3 install --upgrade pip setuptools

$ pip --version
pip 20.1.1 from /home/user/.anyenv/envs/pyenv/versions/2.7.18/lib/python2.7/site-packages/pip (python 2.7)

$ pip3 --version
pip 20.1.1 from /home/user/.anyenv/envs/pyenv/versions/3.7.7/lib/python3.7/site-packages/pip (python 3.7)
```

### Node.js 環境構築
フロントエンド開発で Node.js は導入必須のため、nodenv を使って Node.js 環境を構築しておきます。

```bash
# -- Ubuntu 20.04 on WSL2

# anyenv を使って nodenv 導入
## nodenv を使うことで、複数バージョンの Node.js 環境を構築できる
$ anyenv install nodenv
$ exec $SHELL -l

## nodenv-yarn-install プラグイン導入: nodenv install 時に yarn もインストールする
$ mkdir -p "$(nodenv root)/plugins"
$ git clone https://github.com/pine/nodenv-yarn-install.git "$(nodenv root)/plugins/nodenv-yarn-install"
$ echo 'export PATH="$HOME/.yarn/bin:$PATH"' >> ~/.bashrc

# Node.js 10.17.0 インストール
$ touch $(nodenv root)/default-packages
$ nodenv install 10.17.0

# Node.js 10.17.0 に切り替え
$ nodenv global 10.17.0

# 現在選択されているバージョンを確認
$ nodenv versions
* 10.17.0 (set by /home/user/.anyenv/envs/nodenv/version)

# 一度シェルを再起動しないと Node.js が使えない
$ exec $SHELL -l

# バージョン確認
$ node -v
v10.17.0

$ yarn -v
1.22.4

# Yarn package manager で Gulp をグローバルインストール
$ yarn global add gulp

# Gulp バージョン確認
$ gulp -v
CLI version: 2.3.0
Local version: Unknown
```

### PHP 環境構築
基本的に WEB 開発は Docker で行うことを推奨しています。（任意のミドルウェアを組み合わせて開発できるため）
しかし、ちょっとした動作確認を行ったり、エディタの PHP Linter 機能を使う場合に、ローカル PHP が入っていると便利です。
そのため、開発言語として PHP を使わない人は、この手順はスキップして問題ありません。

```bash
# -- Ubuntu 20.04 on WSL2

# phpenv は Ubuntu + Linuxbrew 環境で上手く動かないため普通に apt で php-cli をインストールする
$ sudo apt install php-cli php-mbstring php-curl php-xml

# composer 導入
$ cd ~
$ curl -sSL https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

# バージョン確認
$ php --version
PHP 7.4.3 (cli) (built: May 26 2020 12:24:22) ( NTS )

$ composer --version
Composer version 1.10.8 2020-06-24 21:23:30
```

### AWS CLI 導入
最近の WEB 開発では静的ファイルやバックアップなどを AWS S3 に保存することが多いようです。
そのため AWS CLI を導入しておくと何かと便利だと思います。

```bash
# -- Ubuntu 20.04 on WSL2

# pip3 を使って AWS CLI を導入
$ pip3 install awscli

$ aws --version
aws-cli/1.18.93 Python/3.7.7 Linux/4.19.84-microsoft-standard botocore/1.17.16
```

#### AWS CLI 設定

##### リージョン・出力形式の設定
```bash:~/.aws/config
# --- 書式 ---
# [profile <プロファイル名>]
# region=<リージョン>
# output=<出力形式>

# 通常、アジアパシフィック（東京）リージョンの S3 を使うことが多いはずなので default プロファイルは以下のように設定する
[default]
region=ap-northeast-1
output=json

# 別リージョン・出力形式のプロファイルが必要な場合は以下のように記述
# ※以下のプロファイルを指定して aws cli を実行する場合は
# $ aws <command> --profile example
[profile example]
region=us-east-1
output=text
```

##### アクセスキーの設定
```bash:~/.aws/credentials
# --- 書式 ---
# [<プロファイル名>]
# aws_access_key_id=<IAM アクセスキー>
# aws_secret_access_key=<IAM シークレットアクセスキー>
    
# default プロファイルの例
# アクセスキーは自分の IAM アクセスキーを記述すること
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

# example プロファイルを定義する場合
# ※リージョン・出力形式の設定と違い、接頭辞 profile は不要のため注意
# ※以下のプロファイルを指定して aws cli を実行する場合は
# $ aws <command> --profile example
[example]
aws_access_key_id=AKIAI44QH8DHBEXAMPLE
aws_secret_access_key=je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
```

## Docker 環境構築

### Docker とは
- **Docker**
    - OS・ミドルウェア・ファイルシステム全体を**イメージ**という単位で取り扱い、まるごとやりとり出来るツール
    - **特徴**:
        - Docker仮想環境は**コンテナ型**と呼ばれるもので、Linuxカーネルに直接アクセスするためオーバーヘッドが少ない
        - 環境構築が容易（`Dockerfile`に環境設定を記述するだけで、必要な環境を自動で構築してくれる）
        - コンテナは移植性(ポータビリティ)が高く、Dockerさえインストールされていれば、全く同じ環境でアプリを動かせる
        - ホストOSからはコンテナは１プロセスとして認識される
    - **Dockerが解決するもの**:
        - Dockerはアプリケーションとその実行環境を統合的に管理する為のソリューションであるため、開発環境におけるOSレベルのライブラリ、ミドルウェアのバージョン、環境設定は、常に本番環境と同じものにすることが可能
        - すなわち、本番環境へのデプロイ時の最大の不安要素が解消される

### Dockerの原則
1. 1コンテナにつき1プロセス
    - 1つのコンテナ内に複数プロセス(例: Rails, Nginx, MySQL)を詰め込むと、コンテナの再起動などが気軽にできない
2. コンテナ内で完結させる
    - 使用するミドルウェアやツールなどはすべてホスト側ではなくコンテナ上で管理すること　
    - これにより、バージョンアップやメンテは`Dockerfile`上で管理できる

### Docker環境構築
```bash
# -- Ubuntu 20.04 on WSL2

# Docker (Community Edition) インストール
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
$ sudo apt update && sudo apt install -y docker-ce
## dockerデーモン起動
$ sudo service docker start

# WSL2 では、デーモンをスタートアップに登録することができない
# スタートアップに登録したい場合は、Windowsのタスクスケジューラに登録する必要がある
# 参考: https://qiita.com/Ningensei848/items/75adeb29bb143633d60c

# Windows再起動の度に sudo service docker start すれば良いだけなので、ここではスタートアップ登録までは行わない

# WSL2 には cgroup 用ディレクトリがデフォルトで作られていないため作成しておく
## これをしておかないと Docker でプロセスのグループ化が必要になったときにエラーが起きる
$ sudo mkdir -p /sys/fs/cgroup/systemd
$ sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

# docker-compose 導入
$ sudo curl -L https://github.com/docker/compose/releases/download/1.26.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

# Dockerを sudo なしで実行可能に
## ※ カレントユーザーをdockerグループに所属させた上で docker.sock へのグループ書き込み権限を付与すればよい
$ sudo gpasswd -a $USER docker
$ sudo chgrp docker /var/run/docker.sock
$ sudo service docker restart

# 一度ログアウトしないと反映されないため、一旦 exit
$ exit
```

### 動作確認
```bash
# -- Ubuntu 20.04 on WSL2

# 動作確認用 docker構成 をダウンロード
## Let's Encrypt で SSL 化 + vhost 環境の Apache:2.4 PHP:7.3 コンテナ
$ wget -O - https://github.com/amenoyoya/docker-collection/releases/download/0.2.1/letsencrypt-nginx-proxy.tar.gz | tar zxvf -
$ cd letsencrypt-nginx-proxy/

# Dockerデーモンを起動していない場合は起動
$ sudo service docker start

# Dockerコンテナビルド＆起動
$ export UID && docker-compose build
$ docker-compose up -d
```

vhost（ローカルドメイン）を有効化するために、`Win + X` |> `A` キー => 管理者権限 PowerShell 起動

```powershell
# hostsファイルをメモ帳で編集
> notepad C:\windows\system32\drivers\etc\hosts
### <hosts>
# 以下の行を追加: https://web.local/ => 127.0.0.1 (localhost) に関連付け
127.0.0.1    web.local
::1          web.local
### </hosts>

# DNSキャッシュをクリアして、仮想ホスト設定を反映
> ipconfig /flushdns
```

ここまで実行し、ブラウザで https://web.local/ にアクセスしてみます。
これで、phpinfo の内容が表示されたら動作確認は完了です。

```bash
# Dockerコンテナを停止する
$ docker-compose stop
```

### /sys/fs/cgroup/systemd マウントを永続化できないか
Dockerインストール時に `sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd` のコマンドで cgroup をマウントしていますが、これは当然 WSL2 を再起動すれば消えてしまう設定です。

これを永続化する方法として、以下のように `/etc/fstab` に設定する方法が真っ先に思いつきます。

```bash
$ echo 'cgroup /sys/fs/cgroup/systemd cgroup none,name=systemd' | sudo tee -a /etc/fstab
```

しかし、WSL2 再起動時点で `/sys/fs/cgroup/systemd` は存在しない（消えてしまっている）ため、この方法は上手く行きません。

筆者はしばらくの間、諦めて起動時に毎回このコマンドを打っていました。

しかし最近、この回避方法として [WSL2でスタートアップスクリプトを実行するHack](https://qiita.com/amenoyoya/items/41a2334cbc1facb87864) が使えることが分かりましたので、ご参照ください。

### Dockerコンテナを起動する度にメモリが圧迫される場合
2020年8月現在の WSL2 は、Dockerコンテナ作成時にメモリリークが起こる場合があります。
この場合は、WSL2 システムを一旦シャットダウンすれば解消します。

```powershell
# PowerShell を起動し、以下のコマンドを実行

# WSL2 をシャットダウン
> wsl --shutdown
```

### Dockerコンテナ起動時に Exit(139) が発生する場合
WSL2 環境では、CentOS6 などの古いベースイメージの起動に失敗します。

そのため、そういったベースイメージを使いたい場合 kernelCommandLine に `vsyscall=emulate` を設定する必要があります。

`Win + X` |> `A` キー => 管理者権限 PowerShell を起動し、以下のように設定します。

```powershell
# %USERPROFILE%/.wslconfig に kernelCommandLine 設定追記
> Write-Output '[wsl2]
kernelCommandLine=vsyscall=emulate' | Add-Content "$env:USERPROFILE/.wslconfig"

# WSL2 シャットダウン
> wsl --shutdown

# WSL2 再起動
> wsl

# 設定が反映されているか確認
$ cat /proc/cmdline
initrd=\initrd.img panic=-1 nr_cpus=12 swiotlb=force  pty.legacy_count=0 vsyscall=emulate

# CentOS6 Dockerイメージ起動できるか確認
$ sudo service docker start
$ docker pull centos:6
$ docker run -itd centos:6
$ docker ps

CONTAINER ID  IMAGE     COMMAND      CREATED        STATUS        PORTS  NAMES
27e33339071c  centos:6  "/bin/bash"  7 seconds ago  Up 6 seconds

# 確認できたらコンテナを削除
$ docker stop 27e33339071c
$ docker rm 27e33339071c
```

## WSL2 Linux ボリュームを別ドライブに作成したい場合

Dockerを日常使いしていると、ストレージ容量が不足しがちになります。

そのため、WSL2 Linux ボリュームを外部ストレージ等に作成できると便利です。

ここでは、LxRunOfflineを使って、Ubuntu 20.04 システムボリュームをDドライブに作成してみます。

### LxRunOffline 導入
`Win + X` |> `A` => 管理者権限 PowerShell 起動

```powershell
# LxRunOffline インストール
> choco install -y lxrunoffline

# => C:\tools\lxrunoffline にインストールされる

# PATH等の環境変数を反映するために一度 PowerShell 再起動

# PowerShell再起動したらバージョン確認
> lxrunoffline version
LxRunOffline v3.5.0
```

### Setup
WSL2 用のディストロ appx (Windowsストアアプリ) をインストールした状態でないと別ドライブへのボリューム作成もできないため、あらかじめ Ubuntu 20.04 ディストロ appx はインストールして、初回起動を済ませておく必要があります。

`Win + X` |> `A` => 管理者権限 PowerShell 起動

```powershell
# LxRunOffline 用の Ubuntu 20.04 (focal) コアシステムファイルをダウンロード
## https://lxrunoffline.apphb.com/download/{distro}/{version}
## ダウンロード可能なディストロ一覧: https://github.com/DDoSolitary/LxRunOffline/wiki
> Invoke-WebRequest -Uri https://lxrunoffline.apphb.com/download/Ubuntu/focal -OutFile $env:HOME\Downloads\ubuntu-focal.tar.gz -UseBasicParsing

# D:\wsl\ubuntu-focal に ubuntu-focal というディストロ名で Ubuntu 20.04 ボリューム作成
> lxrunoffline i -n ubuntu-focal -d D:\wsl\ubuntu-focal -f $env:HOME\Downloads\ubuntu-focal.tar.gz -UseBasicParsing

# => ボリューム削除したい場合は
## > lxrunoffline ui -n <ディストロ名>

# インストール済みディストロ一覧確認
> wsl -l
Ubuntu-20.04 (既定) # <= appx でインストールした Ubuntu 20.04 本体
ubuntu-focal # <= D:\wsl\ubuntu-focal にインストールした Ubuntu 20.04 独自ボリューム

# ubuntu-focal ディストロを規定に設定し、WSL2にアップグレード
> wsl --set-default ubuntu-focal
> wsl --set-version ubuntu-focal 2

# 確認
> wsl -l -v
 NAME            STATE           VERSION
* ubuntu-focal    Stopped         2
  Ubuntu-20.04    Stopped         2

# ubuntu-focal 起動
> wsl
```

```bash
# -- root@ubuntu-focal(wsl2)

# システムアップグレード
% apt update && apt upgrade -y

# sudo コマンドインストール
% apt install -y sudo

# ログイン用ユーザ作成: user (任意名でOK)
% adduser user
New password: # <= ログインパスワード設定
Retype new password: # <= パスワード再入力
Enter the new value, or press ENTER for the default
        Full Name []: # <= そのままENTER
        Room Number []: # <= そのままENTER
        Work Phone []: # <= そのままENTER
        Home Phone []: # <= そのままENTER
        Other []: # <= そのままENTER
Is the information correct? [Y/n]  # <= そのままENTER

# user ユーザに sudo 権限付与
% gpasswd -a user sudo

# user ユーザが sudo コマンドを実行するときにパスワード不要にする（任意）
% echo 'user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# user ユーザの UID を確認
% id -u user
1000 # <= 環境により異なる（メモしておく）

# 終了
% exit
```

```powershell
# ubuntu-focal ディストロのデフォルトユーザを先ほど作成したユーザに設定
## -v <UID>: 先ほど確認した UID を設定（上記の場合 1000）
> lxrunoffline su -n ubuntu-focal -v 1000

# => 以降 ubuntu-focal on WSL2 ターミナルのログインユーザは user (UID: 1000) になる
```

後は普通に Ubuntu 20.04 のセットアップを行うだけです。

## WSL2 のボリューム容量を増やす

WSL2 の仮想ディスクは、デフォルトで 256GB となっています。

上記の手順で別ドライブに WSL2 ボリュームを作ったので、ついでにボリューム容量も増やしておくと良いです。

再び `Win + X` |> `A` => 管理者権限 PowerShell を起動します。

```powershell
# WSL2 をシャットダウン
> wsl --shutdown

# インストール済ディストロを確認
> lxrunoffline l
ubuntu-focal
Ubuntu-20.04

# 容量を増やしたいディストロ（今回の場合 ubuntu-focal）の仮想ディスク保存パスを確認
> lxrunoffline di -n ubuntu-focal
D:\wsl\ubuntu-focal

# DISKPART を起動
> diskpart

# 上記で確認した仮想ディスク保存先パスの ext4.vhdx を操作対象として選択
DISKPART> select vdisk file="D:\wsl\ubuntu-focal\ext4.vhdx"

# 仮想ディスクの詳細を確認しておく
DISKPART> detail vdisk

デバイスの種類 ID: 0 (不明)
ベンダー ID: {00000000-0000-0000-0000-000000000000} (不明)
状態: 追加済み
仮想サイズ:  256 GB
物理サイズ:  247 GB
ファイル名: D:\wsl\ubuntu-focal\ext4.vhdx
子: いいえ
親ファイル名:
関連付けられたディスク番号: 見つかりません。

# 仮想ディスクの最大サイズを 600GB に増やす
## expand vdisk maximum={最大サイズをMB単位で指定}
DISKPART> expand vdisk maximum=600000

# 確認
DISKPART> detail vdisk
 :
仮想サイズ:  585 GB
物理サイズ:  247 GB
 :

# DISKPART 終了
DISKPART> exit

# WSL2 を起動
> wsl

# -- user@ubuntu-focal

# Linux の ext4 ストレージを確認
$ sudo mount -t devtmpfs none /dev
$ mount | grep ext4
/dev/sdb on / type ext4 (rw,relatime,discard,errors=remount-ro,data=ordered)

# 上記で確認したストレージ（今回の場合 /dev/sdb）を最大サイズまで拡張
$ sudo resize2fs /dev/sdb

# ストレージサイズ確認
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb        576G  243G  308G  45% /
tools           461G  292G  169G  64% /init
none            3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
 :
```


## 既存の仮想ディスク（vhd）を WSL2 環境にインポートする

LxRunOffline を使って別ドライブに WSL2 Linux 環境を構築した場合、そのディスクを別のマシンに持っていて使いたくなることがあります。

そういった場合は、以下の手順で既存 Linux 仮想ディスクを WSL2 環境にインポートすることが可能です。
（※ なお WSL2 の環境構築は済んでいるものとします）

- まず前提として、インポートしたい仮想ディスク（vhd）環境が `D:\wsl\ubuntu-focal\` ディレクトリにあり、以下のようなディレクトリ構成になっているとします

```
D:\wsl\ubuntu-focal\
  |_ temp\
  |_ ext4.vhdx
  |_ fsserver
```

- 別の WSL2 環境で使われている仮想ディスクは、**アクセスが拒否されるため、必ず最初にコピーをとる**必要があります
    - コピーをとらずに直接 WSL2 環境にインポートすると、最悪の場合仮想ディスクが壊れる可能性があるため、要注意
- `Win + X` |> `A` => 管理者権限 PowerShell 起動

```powershell
# D:\wsl\ubuntu-focal ディレクトリを D:\wsl\ubuntu-2004 ディレクトリにコピーする
> copy -r D:\wsl\ubuntu-focal D:\wsl\ubuntu-2004

# 確認
> dir D:\wsl\ubuntu-2004\
    ディレクトリ: D:\wsl\ubuntu-2004
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021/01/21     12:14                temp
-a----        2021/02/23      9:04    83722502144 ext4.vhdx
-a----        2021/01/21      9:58              0 fsserver

# コピーした仮想ディスク（Linux環境）を WSL2 にインポート
## lxrunoffline register -n <WSL2登録名> -d <インポート元仮想ディスクのあるディレクトリ>
> lxrunoffline register -n ubuntu-2004 -d D:\wsl\ubuntu-2004\

# 登録されている Linux ディストロ名を確認
## 上記手順で ubuntu-2004 が登録されているはず
> lxrunoffline list
Ubuntu-20.04
ubuntu-2004 # <= New!

# インポートした ubuntu-2004 環境を起動
> lxrunoffline run -n ubuntu-2004

# -- root@ubuntu-2004

# ログイン用メインユーザのIDを確認
## 今回のインポート元 ubuntu-focal 環境では、メインユーザは user だったため、以下のようにしてユーザID確認
% id -u user
1000 # <= 環境により異なる（メモしておく）

# ログインユーザIDを確認できたら一旦終了
% exit

# -- powershell@localhost

# ubuntu-2004 のログインユーザを root から user (ログイン用メインユーザ: 環境により異なる) に変更
## 上記で確認したユーザIDを指定する
> lxrunoffline su -n ubuntu-2004 -v 1000

# ubuntu-2004 環境を規定のディストロに設定
> wsl --set-default ubuntu-2004

# 確認
> wsl -l -v
  NAME            STATE           VERSION
* ubuntu-2004     Running         2
  Ubuntu-20.04    Stopped         2

# => これで、以降 wsl コマンドで ubuntu-2004 の WSL2 Linux 環境を使うことができる
```

本稿は以上になります。
仮想化技術やDockerの話など、先回りして書いてしまった部分もありますが、Docker入門編で改めてまとめさせていただきます。

ありがとうございました。