Windows上でWSLを用いてDockerを動かしてみました

## 1. 記事の概要

Windows上でDockerを無料で利用する方法を模索し、実際に試してみました。
企業の開発での利用を想定し、会社のプロキシからDocker Hubへの接続、在宅勤務で会社のVPNへ接続した状態での利用という環境で検証しました。

WSL(Windows Subsystem for Linux)を用いて、Windowsマシン上にDockerをインストールしたLinux仮想マシンを用意することにより、可能です。
WSLは、Windowsマシン上でLinux仮想マシンを実行する機能です。

## 2. 前提条件

今回前提としている環境は、以下の通りです。

* ネットワーク
  * 会社内で基幹ネットワークに接続、または、
  * 在宅勤務でVPNを利用して基幹ネットワークにリモート接続

* ホストOS
  * Windows 10 バージョン2004以上
  * Windows 11 全般

* WSL
  * バージョン2（2024/7時点の最新版）

## 3. 構築手順

以降の説明では、ホストOSのWindowsマシンで実行するコマンドとゲストOSのLinuxディストリビューションで実行するコマンドは、下記のように区別して表記します。

`PS > <Windowsマシンで実行するPowerShellコマンド>`

`$ <Linuxディストリビューションで実行するBashコマンド>`

### 3.1. VPN設定の確認

VPNを利用して基幹ネットワークにリモート接続する状況で、接続用のClientソフトウェアの設定によってはインターネットにアクセスできない場合があります。
その場合、インターネット経由でDocker関係のパッケージを更新しようとしてもできないため、事前の確認が必要です。

例えばCisco AnnyConnect VPNを使用している場合は、Cisco Secure Clientの詳細ウィンドウを開いて「環境設定」タブの「VPN使用時にローカル(LAN)アクセスを許可する(構成されている場合)」を有効化することにより、正常にインターネットに接続できました。

![CiscoSecureClientの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630028/3f808f70-a2b0-037b-6542-1aadc56839ad.png)
<center>CiscoSecureClientの設定</center>

### 3.2. WSL有効化

ホストOSのWindowsマシンの「設定」で、検索ボックスから「Windowsの機能の有効化または無効化」を検索して実行します。
表示されたダイアログで、「Linux用Windowsサブシステム」「仮想マシンプラットフォーム」の2つにチェックをつけて有効化します。
再起動が求められる場合があります。

![Windows機能の有効化.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630028/34f797a5-0aa5-24bd-34c4-381ae2c632ec.png)

<center>Windows機能の有効化</center>

### 3.3 Linuxディストリビューションのインストール

Windows Powershellを管理者権限で起動します。最初に、WSLのアップデートを行います。

`PS > wsl --update`

次に、WSLで利用可能なゲストOS（Linuxディストリビューション）を確認します。

`PS > wsl --list --online`
または
`PS > wsl -l -o`

表示された一覧からLinuxディストリビューションを選んでインストールします。指定するLinuxディストリビューションを変えて、複数インストールすることも可能です。

`PS > wsl --install -d <Distribution Name>`

今回は、Oracle Linux 9を指定してみました。

`PS > wsl --install -d OracleLinux_9_1`

また、WSLにデフォルトのLinuxディストリビューションを指定しておくと、コマンドでディストリビューションを指定する部分（`-d <Distribution Name>`）を省略できます。デフォルトのLinuxディストリビューションを指定するには、下記コマンドを実行します。

`PS > wsl --set-default <Distribution Name>`
または
`PS > wsl -s <Distribution Name>`

例えば、デフォルトのLinuxディストリビューションをインストールする場合は下記のようになります。

`PS > wsl --install`

インストール完了後に、インストールされているLinuxディストリビューションを確認します。

`PS > wsl -l -v`

コマンドの出力結果は以下のようになります。ここで表示されるバージョンは、WSLのバージョンです。

```
  NAME               STATE           VERSION
* OracleLinux_9_1    Running         2
  Ubuntu             Stopped         2
```

WSLのバージョンが1だった場合には、エラーでインストールできない場合があります。
[WSL の手動インストール手順](https://learn.microsoft.com/ja-jp/windows/wsl/install-manual)を参考にすると、WSL2をインストールし直すことができます。


### 3.4 WSL起動

指定したLinuxディストリビューションでWSLを起動します。

`PS > wsl --distribution <Distribution Name>`
または
`PS > wsl -d <Distribution Name>`

初回起動時にはゲストOS（Linuxディストリビューション）にユーザーを作成するため、ユーザー名とパスワードの入力が求められます。

```
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:
```

ログイン後、インストール済みのパッケージのアップデートを行います。
Linuxディストリビューションによってパッケージ管理の方法は異なります。例えばOracle LinuxではまずLinuxディストリビューション上のdnfの設定ファイルを書き換えておきます。

`$ sudo vi /etc/dnf/dnf.conf`

下記を追記します。proxy.example.com:8080の部分は、会社で公開しているプロキシサーバのアドレスを設定します。
```
[main]
...
proxy=http://proxy.example.com:8080
```


その後、下記コマンドでアップデートできます。

`$ sudo dnf update -y`

このコマンドは、インターネットに接続できない場合は失敗します。
その場合は、「3.1. VPN設定の確認」に記載した項目など、ネットワーク設定の確認が必要となります。

### 3.5 systemdの起動設定

WSLの初期設定ではsystemdが起動されていないため、dockerやsystemctlの実行時などに、下記エラーが表示されます。

```
System has not been booted with systemd as init system (PID 1). Can't operate
```

対処方法として、Linuxディストリビューション上のWSLの設定ファイルを書き換えておきます。

`$ sudo vi /etc/wsl.conf`

下記を追記します。
```
[boot]
systemd=true
```

設定ファイルを編集後にLinuxディストリビューションを再起動します。

```
$ exit
PS > wsl -t <Distribution Name>
PS > wsl -d <Distribution Name>
```
または
```
$ sudo reboot
PS > wsl -d <Distribution Name>
```

### 3.6 ネットワーク設定

ネットワーク設定は、ホストOSのWindowsマシン側の設定を元に、自動で設定されます。
curlやwgetを使ってインターネットに接続できるか確認し、プロキシ設定ができているかを確認します。

~/.bashrcへの設定例を下記に示します。

```
export http_proxy=http://proxy.example.com:8080
export https_proxy=http://proxy.example.com:8080
export no_proxy=127.0.0.1,example.com,docker
```

また、sudo利用時にもプロキシ設定が有効になるように、/etc/sudoersのDefaults env_resetの後に下記を追記します。

```
Defaults env_keep="http_proxy"
Defaults env_keep+="https_proxy"
Defaults env_keep+="ftp_proxy"
Defaults env_keep+="no_proxy"
```

### 3.7 Dockerインストールと実行

LinuxディストリビューションがOracle Linuxの場合のDockerのインストール手順は、[Dockerの公式サイト](https://docs.docker.com/engine/install/rhel/)で確認できます。

インストール後に、プロキシを経由してコンテナイメージをpullする場合の設定を/etc/docker/daemon.jsonに追記します。

```
{
  "proxies": {
      "http-proxy": "http://proxy.example.com:8080",
      "https-proxy": "http://proxy.example.com:8080",
      "no-proxy": "localhost,127.0.0.1,example.com,docker"
  }
}
```

一方、コンテナイメージのpullリクエストに対するレート制限回避などの理由でDocker HubからpullしたDockerイメージを保持するプロキシサーバがある場合は、上記設定の代わりに、プロキシサーバのURLを/etc/docker/daemon.jsonに記載します。

```
{
  "registry-mirrors": [
                "https://dockerhub-proxy.example.com"
        ],
  "insecure-registries": [
                "dockerhub-proxy.dockerhub-proxy.example.com"
        ]
}
```

レート制限についての詳細は[Dockerの公式サイト:Understanding Your Docker Hub Rate Limit](https://www.docker.com/increase-rate-limits/)を参照ください。

プロキシの設定後、設定を反映させるためDockerを再起動します。
```
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
```

以上の一連の手順により、企業のプロキシを介してインターネットに接続している環境で、Docker（dockerコマンド）を利用できるようになりました。

```
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:53cc4d415d839c98be39331c948609b659ed725170ad2ca8eb36951288f81b75
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```

同様の課題をお持ちの方のお役に立てましたら幸いです。