Windows10+WSL(Ubuntu)+Docker サーバサイド開発環境

WSL(Windows Subsytem for Linux)とDocker Desktopでカンタンにサーバサイド開発環境の準備をします。

# 内容

* WindowsにおけるLNMP(Linux+Nginx+MySQL+Python)開発環境を構築したい
* 今どきはコンテナ型仮想化だ
* だが、未だにWSLではDockerは満足に動かない
* `WSL`に`Ubuntu18.04 LTS`を入れて、Docker Desktopに接続して解決する

# 前提

## なぜWSLなのか？

WSLを使えば強力なLinux CLI環境が簡単に導入できます。
VirtualBox等のVMよりも軽く、Cygwin,MinGW等の互換レイヤーとは違ってLinuxディストリビューションを導入できる強みがあります。

## なぜDockerなのか？

| 年代 | 技術 |
|---|---|
| ～2004年 | オンプレ |
| 2005年～ | ハードウェア仮想化(Xen) |
| 2013年～ | コンテナ型仮想化(Docker-Kubernetes) |

* コンテナ型仮想化 / [Dockerの歴史から紐解く、コンテナ型仮想化の「今まで」と「これから」][10]
* Docker/ さくらのナレッジ様 - [Docker入門（第一回）～Dockerとは何か、何が良いのか～][1]
* Kubernetes/ ThinkIT - [いまこそ始めよう！ Kubernetes入門][2]

## なぜDocker Desktopなのか？

WSL(Ubuntu)にDockerを入れても動作しません。
WSLが提供してないカーネルサービスを要求するためです。
WindowsでDockerを使うためにはDocker Desktopが必要です。
※2019年2月時点。WSL＋Ubuntu16.04＋Docker(17系)ならば動くようですが古いのでオススメしません。

Dockerのほか、`NGINX Unit`も`memfd_create()`未対応で動作しません。
参考: [WSLのシステムコール実装/未実装を確認する][11]

## VirtualBox+Dockerで良くないか？

Docker Desktopは仮想Linuxマシン(MobyLinuxVM)を起動し、そのマシン内でDockerを実行します。

* VirtualBox＋Docker： 仮想マシン上のLinux CLI環境＋仮想マシン上のDocker
* WSL＋Docker Desktop： WSL Linux CLI環境＋仮想マシン上のDocker

CLI環境が軽いというメリットがあります。
WSLでは提供していないカーネルサービスがあるため、動作しないソフトが多くあります。そこはDockerが解決します。

## Ubuntu？

* Debian系でDockerと相性がよい
* [シェア][9]
* 今後必須となるAI/BCパッケージサポートの厚さ

## Macでよくね？

(･ω･)


# Docker Desktop

## インストール

[Docker公式][3]からインストーラーをダウンロードして実行する

## 設定

Docker DesktopのdockerデーモンにWSLから接続できるようにし、ホスト(Windows)ドライブとの共有設定をおこなう。

タスクアイコン→`Setting`

|タブ|設定名||
|---|---|---|
| General | `Expose deamon on tcp://localhost:2375 without TLS` | ON |
| Shared Drives | 共有したいドライブレター | ON |
| Network | DNS Server | Fixed |

# WSL＋Ubuntu環境の構築

## WSL(Windows Subsystem for Linux)を有効にする

1. Powershellを管理者で起動する
2. WSLを有効にする

~~~shell
> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
~~~

3. Microsoft StoreからUbuntu18.04をインストールする (検索⇒Ubuntu)

## Wslttyをインストールする

WSLの標準コンソールは色々と制約が多いため、WSL向けMinttyの`Wsltty`を使う

1. [Wsltty][4]をインストールする
2. スタートメニューから`Configure WSL Shortcuts`を実行する
3. 作成したショートカットからWslttyを起動する
4. 右クリック→settingから最低限の設定をおこなう。

|タブ|項目|設定|
|---|---|---|
| text | フォント | お好みで |
|      | ロケール | ja_JP |
|      | 文字セット| UTF-8 |
|window|スクロール行数|60000とか|
||UI言語|ja|
|terminal|タイプ|xterm-256color|

## aptの初期設定

1. WslttyまたはUbuntuを起動する
2. リポジトリを[国内ミラー][5]に変更する

~~~shell
$ sudo perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://ubuntutym.u-toyama.ac.jp/ubuntu/%g' /etc/apt/sources.list
~~~

cat /etc/apt/sources.list | grep "^deb"

~~~
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic main restricted
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates main restricted
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic universe
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates universe
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic multiverse
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-updates multiverse
deb http://ubuntutym.u-toyama.ac.jp/ubuntu/ bionic-backports main restricted multiverse universe
deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted
deb http://security.ubuntu.com/ubuntu/ bionic-security universe
deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse
~~~
- bionicの部分はUbuntuバージョンによって適宜読み替えてください
- securityリポジトリはデフォルトのままにしておく

3. アップデート/アップグレード

~~~shell
$ sudo apt -y update
$ sudo apt -y upgrade
~~~

## 日本語環境

1. 日本語/タイムゾーン設定

~~~shell
$ sudo apt -y install language-pack-ja
$ sudo update-locale LANG=ja_JP.UTF-8
$ sudo timedatectl set-timezone 'Asia/Tokyo'
$ sudo apt -y install manpages-ja manpages-ja-dev
~~~

2. [Ubuntu Japanese Teamリポジトリ][6]を追加する

~~~shell
$ wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add -
$ wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | sudo apt-key add -
$ sudo wget https://www.ubuntulinux.jp/sources.list.d/$(lsb_release -cs).list -O /etc/apt/sources.list.d/ubuntu-ja.list
$ sudo apt -y update
$ sudo apt -y upgrade
~~~

# Docker

## WSL(Ubuntu)にDockerをインストールする

[Docker公式の手順][7]通り

1. 開発(基本)パッケージを取得する

~~~shell
$ sudo apt update
$ sudo apt -y install build-essential git apt-transport-https ca-certificates curl gnupg2 software-properties-common
~~~

2. Dockerリポジトリの追加

~~~shell
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt update
~~~

3. Dockerのインストール

~~~shell
$ sudo apt install docker-ce
~~~

4. Docker Desktopのデーモンに接続するように設定を追加

~~~shell
$ echo "export DOCKER_HOST=tcp://localhost:2375" >> ~/.bashrc
$ echo 'alias docker="DOCKER_HOST=${DOCKER_HOST} docker"' >> ~/.bashrc
$ source ~/.bashrc
~~~

5. Hello world実行

~~~shell
$ docker run --rm hello-world
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/
~~~

# (参考) 各コンテナの起動とコンテナ間通信

Nginx、MySQL、おまけにRedisのコンテナを実行して相互通信する例です。

## Dockerネットワークの作成

コンテナ間を繋ぐネットワークを作成する

~~~shell
$ docker network create development-network
$ docker network ls
~~~

## コンテナの取得と起動

コンテナを取得して起動する。
各コンテナの説明は[DockerHub][8]を参照すること

~~~shell
$ docker run -d --name nginx --network development-network -p 8080:80 nginx
$ docker run -d --name mysql --network development-network -e MYSQL_ROOT_PASSWORD=hoge mysql
$ docker run -d --name redis --network development-network redis
~~~

|オプション||
|---|---|
| --name | コンテナ名 |
| --network | コンテナネットワーク名の指定 |
| -p | ホスト側ポート:コンテナ側ポート |
| -d | バックグラウンド実行(detach) |

Dockerネットワークを設定することで、
例えばredisコンテナから`http://nginx:80/`のような形でnginxコンテナのNGINXにコンテナ間通信できる。

# (参考) ホスト(Windows)ドライブをマウント

コンテナ起動の際、`-v`オプションを指定してホスト(Windows)ドライブをマウントできる

```shell
$ docker run --rm -v /c/project:/project ubuntu ls /project
```

|オプション||
|---|---|
| -v | マウントするホスト側パス:コンテナ側パス |

* ホスト側パスはDocker Desktopが解釈できるパスで記述する
* WSL(Ubuntu)ディレクトリをマウントすることはできない

# 次の記事

| ベースイメージ | 用途 |
|---|---|
| NGINX Unit | APIサーバ |
| mysql | MySQLサーバ |
| redis | Redisサーバ |

* 上記イメージをDockerfileを記述してビルド
* Docker Composeで取りまとめる

[1]:https://knowledge.sakura.ad.jp/13265/
[2]:https://thinkit.co.jp/article/13289
[3]:https://hub.docker.com/editions/community/docker-ce-desktop-windows
[4]:https://github.com/mintty/wsltty
[5]:https://launchpad.net/ubuntu/+mirror/ubuntutym.u-toyama.ac.jp-archive
[6]:https://www.ubuntulinux.jp/japanese
[7]:https://docs.docker.com/install/linux/docker-ce/ubuntu/
[8]:https://hub.docker.com/
[9]:https://w3techs.com/technologies/history_details/os-linux
[10]:https://www.sbbit.jp/article/cont1/34350
[11]:https://xdigit.hatenablog.com/entry/2018/11/21/164559