WSL2 + Ubuntu18.04 + Docker環境構築

# はじめに
アドベントカレンダー11日目の記事です。
今回は、WindowsでWSL2環境のセットアップ方法を紹介します。

巷に記事はたくさんありますが、情報が古くエラーが発生して上手く動作しなかったり。。。
記事とターミナルを行ったり来たりで時間がかかったりと、セットアップに煩わしさを感じることが多いのではないでしょうか

本記事では、そんな大変な設定をシェルスクリプトで大部分を構築する方法を紹介します。

# 環境
- Ubuntu18.04LTS(64bit)を想定しています。<br>
※ Ubuntu 20.04の場合は、適宜置き換えてください。
- Windows OSビルド 19041以上

# Ubuntu 18.04のインストール
Microsoft StoreからUbuntu 18.04LTSをインストールします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130670/5929d677-3e69-5874-8070-5a7b36176437.png)


# WSL1の設定
インストールしたUbuntu 18.04をスタートメニューから開いてユーザ名やパスワードを設定します

```bash:Ubuntu18.04ターミナル
# 初回起動時は初期設定が必要
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 18.04 ディストロが追加される
# ここで一旦終了する
$ exit
```

# WSL2有効化
PowerShellを管理者権限で開いて下記コマンドを入力します。

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

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

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

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

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

WSL2の有効化が完了しました。
次は、WSL2を開いて基本設定を行います。

# 基本設定
Ubuntu環境を一括で設定するためのシェルスクリプトを用意しました。
記事を見ながらコピペすることなく基本的なものは一括でインストールすることができます。

## やること
- ミドルウェアのインストール
    - **動作に必要**な基本ミドルウェアのインストール
    - **PythonやNode.js**をビルドするために必要なミドルウェアをインストール
- カラーテーマの設定
    - **ColorTool**を利用して`OneHalfDark`テーマを適用します
    - ![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130670/92f8d7f2-ca8b-5e68-6713-453097e5b4ea.png)

- Linuxbrewのインストール
    - Macでおなじみの**HomebrewのLinux版**です
- Dockerのインストール
    - Windows版Dockerクライアントをインストールすることなく**WSL内で完結する**Docker環境を構築します
    - メリットとしては、Dockerクライアントを**常時起動しなくても良い**のでリソースの節約に繋がります

## シェルスクリプトで実行

`~/init.sh`を作成し、下記の全文をコピー
スクリプト中の`[USER_NAME]`を自身のWindowsのユーザ名に変更します

```bash:~/init.sh
#!/bin/bash


install_colortool()
{
  echo 'カラーテーマの適用'
  # ColorToolのダウンロード先
  ## [USER_NAME]はWindowsのユーザ名に適宜変更
  DOWNLOAD_PATH="/mnt/c/Users/[USER_NAME]/Downloads"
  # 適用したいカラーテーマ（今回はOneHalfDarkを選択）
  # 変えたい場合は、ColorTool.exe -s で選択可能なテーマを確認して置き換える
  COLOR_THEME="OneHalfDark.itermcolors"

  echo 'ColorTool.zipのダウンロード'
  wget -P "${DOWNLOAD_PATH}" https://github.com/microsoft/terminal/releases/download/1904.29002/ColorTool.zip

  echo 'zipファイルの解凍'
  unzip "${DOWNLOAD_PATH}/ColorTool.zip" -d "${DOWNLOAD_PATH}/ColorTool"

  echo 'zipファイルの削除'
  rm "${DOWNLOAD_PATH}/ColorTool.zip"

  echo 'カラーテーマの種類'
  `echo ${DOWNLOAD_PATH}/ColorTool/ColorTool.exe -s`

  echo 'OneHalfDarkテーマの選択'
  # その他のテーマを変更したい場合は-dオプションの後を置き換える
  `echo ${DOWNLOAD_PATH}/ColorTool/ColorTool.exe -d ${COLOR_THEME}`

  echo '解凍したフォルダの削除'
  rm -r "${DOWNLOAD_PATH}/ColorTool"
}


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

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


install_anyenv()
{
  echo 'Linuxbrewでanyenv導入'
  brew install anyenv
  anyenv install --init

  source ~/.bashrc

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

  echo 'バージョン確認'
  anyenv -v
}


install_docker()
{
  echo '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

  # /sbin/mount -a 実行時に rc ファイルシステムをマウントするように設定
  echo 'none none rc defaults 0 0' | sudo tee -a /etc/fstab
  # /sbin/mount.rc ファイルを実行可能スクリプトとして作成
  echo '#!/bin/bash' | sudo tee /sbin/mount.rc
  sudo chmod +x /sbin/mount.rc

  echo 'WSL2再起動時dockerデーモンをスタートアップに登録するための設定'
  echo 'service docker start を /sbin/mount.rc に追記'
  echo 'service docker start' | sudo tee -a /sbin/mount.rc

  # WSL2にはcgroup用ディレクトリがデフォルトで作られていないため、以下もスタートアップスクリプトに登録しておく
  ## これをしておかないと Docker でプロセスのグループ化が必要になったときにエラーが起きる
  echo 'mkdir -p /sys/fs/cgroup/systemd && mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd' | sudo tee -a /sbin/mount.rc


  # dockerデーモン起動
  sudo service docker start
  # WSL2 には cgroup 用ディレクトリがデフォルトで作られていないため作成しておく
  sudo mkdir -p /sys/fs/cgroup/systemd
  sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

  echo '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

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

  echo '設定を反映するためにターミナル再起動'
  exec $SHELL -l
}


echo 'Linuxシステムアップデート'
sudo apt update && sudo apt upgrade -y
# ビープ音を鳴らさないように設定
echo "set bell-style none" >> ~/.inputrc
echo 'Linuxbewの動作に必要なパッケージのインストール'
sudo apt install -y vim curl git ruby
echo 'openjdkは個人開発で必要なため一応インストール'
sudo apt install -y openjdk-11-jdk
echo 'Pythonビルドに必要なミドルウェアのインストール'
sudo apt install -y zlib1g-dev libssl-dev libbz2-dev libsqlite3-dev libffi-dev \
    build-essential libreadline-dev tk-dev liblzma-dev libgdbm-dev libdb-dev
echo 'https通信を可能にするためのミドルウェアをインストール'
sudo apt install -y software-properties-common apt-transport-https ca-certificates


echo '==========linux brewのインストール========='
install_linuxbrew
echo '==========linux brewのインストール完了========='
echo '==========anyenvのインストール========='
install_anyenv
echo '==========anyenvのインストール完了========='
echo '==========Dockerのインストール========='
install_docker
echo '==========Dockerのインストール完了========='
```

実行属性を与えて実行する。

```bash
# 実行属性の付与
$ chmod +x init.sh

# シェルの実行
$ ./init.sh
```

# PythonやNode.jsのインストール
基本設定でインストールした`anyenv`を使ってPythonとNode.jsをインストールします。
これらをシェルスクリプトに含めなかった理由としては、`exec $SHELL -l`を実行するときプログラムが終了してしまうためです。
ステップは多くないので、必要であればインストールしましょう。

## Pythonのインストール
pyenvを使って`Python 3.9`をインストールします

```bash
# anyenv を使って pyenv 導入
$ anyenv install pyenv
$ exec $SHELL -l

# pyenvでPython 3.9をインストール
$ pyenv install 3.9.0

# pyenvによるPythonのバージョン指定
$ pyenv global 3.9.0

# 現在選択されているバージョンを確認
$ pyenv versions

# Pythonのバージョン確認
$ python --version

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

# pipのバージョン確認
$ pip3 --version
```

### （追記）エラー回避
pyenvの仕様変更でこのままだとWarningがWSL起動のたびに出るので
`.bashrc`の`eval "$(anyenv init -)"`の上に下記を追記します

```bash:.bashrc
export PYENV_ROOT="$HOME/.anyenv/envs/pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
if command -v pyenv 1>/dev/null 2>&1; then
  eval "$(pyenv init -)"
fi
eval "$(anyenv init -)"
```

## Node.jsのインストール
nodenvを使って`Node.js 15.2.0`をインストールします。

```bash
# anyenvを使ってnodenv導入
$ 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"
$ export PATH="$HOME/.yarn/bin:$PATH" >> ~/.bashrc

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

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

# 現在選択されているバージョンを確認
$ nodenv versions

# シェルの再起動
$ exec $SHELL -l

# Node.jsバージョン確認
$ node -v

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

# おわりに
今回は、WSL2のセットアップを紹介しました。
Docker環境がWSL内で完結するので、本当にLinuxに近い形で利用できそうです。
明日は、@tadahayaさんによる記事です。お楽しみに🎉