これは無宗教ななびの :construction_worker: が書くDocker Advent Calendar 2017用記事です。前日はinductorさんの「Docker Meetupの中身まとめ」でした :whale: (写真はクリスマスを日本にひろめた明治屋 :christmas_tree:

-

PROBLEM

  • macOSとWindowsでWebアプリケーション開発をする際に
    • 環境が異なって管理しづらい
      • それならDockerで
        • と思ったが、macOSはBashでWindowsはPowerShellなのか
          • せめてPowerShellではなくBash…
            • となると、いまWindowsでLinux環境をつかうならWSLか
              • ただ、実際どこまで開発ができるかわからんしなあ

-

SOLUTION

というわけで、この記事ではmacOSとWindowsによるWebアプリケーション開発について、どこまで共有できるか書いていきます。

前提条件として、当該WebアプリケーションはmacOSというより、Bash/Ubuntu14.04~のLinux環境で動くことを想定しています。macOSはHFS+やAPFSのUnicode正規化以外はおおよそLinux環境に適応できているという判断によります。

要は、WSLでDockerをつかったWebアプリケーション開発ができるかどうかという点に焦点をしぼります。

対象環境

  • Windows 10 Pro 1709 16299.64
    • Hyper-V 10.0.16299.15
    • Docker for Windows 17.09.0-ce-win33
    • Ubuntu 16.04 (Linux 4.4.0-43-Microsoft)
      • Docker Client 1.12.6

Windowsの開発環境を構築する

まず、Windowsの開発環境の構築ですが、既知の情報をふまえつつTIPSを順次紹介します。

WSLのインストール

WSLのパッケージ管理は下記3つを押さえておけば問題ないでしょう。

  1. apt
    • WSLではデーモンがつかえないのでDockerクライアントを入れましょう、Dockerデーモンの詳細は後ほど言及します
  2. anyenv
    • プログラミング言語をバージョンごとにわけて使いたい場合はこちらをつかいましょう
    • exenvがビルドで失敗するためElixirインストールできないほかは、各言語問題なくビルドできます
  3. nix
    • ElixirやHaskellのようにanyenvでインストールできない、あるいは、扱われいないパッケージはnixをつかいましょう
    • また、aptのバージョンが古すぎるパッケージもnixが最適です

ターミナルのインストール

WSLttyかConEmuをおすすめします。各々の特徴は下記のとおりですが、通常のWebアプリケーション開発であればWSLttyがいいでしょう。

  • WSLtty
    • Pros
      • ConEmuとくらべてファイルの読込速度が速い (VMよりは遅い)
      • EmacsでCtrl-SPC set-mark が機能する
      • 画面サイズの変更が柔軟
    • Cons
      • PowerShellなどほかのコンソールの呼び出しが面倒
  • ConEmu
    • Pros
      • PowerShellなどほかのコンソールの呼び出しが楽
    • Cons
      • ファイルの読込速度がおそい
      • EmacsでCtrl-SPC set-mark が機能しない
      • 画面サイズの変更に制限がある

WSLtty

WSL用ターミナルとしてのMinttyです。操作はMinttyとかわらず、元Cygwinづかいにはうれしい操作感です。というわけで、いつものごとく起動用ショートカットのターゲットを準備します。WSLは chsh がつかえないのでログイン時につかいたいシェルを指定します。もし、 screen をつかいたい場合は /run/screen ディレクトリを作成してからコマンド指定します。

%LOCALAPPDATA%\wsltty\bin\mintty.exe --wsl -o Locale=C -o Charset=UTF-8 /bin/wslbridge -t /bin/bash -c 'sudo mkdir /run/screen && sudo chmod 775 $_ && sudo chown root:utmp $_ && SHELL=/usr/bin/zsh screen'

ConEmu

WSL上で日本語を表示するため、また、WSLのLinux環境とWindows環境でターミナルをわけるため、ConEmuをつかいましょう。ConEmuをスマートにしたCmderはWSLとの相性がわるい1のでおすすめしません。

ConEmuの設定「Startup-Tasks」では、WSL用にパラメータ、コマンドを下記のように指定しています。

# task parameters
/icon "C:\Program Files\WindowsApps\CanonicalGroupLimited.UbuntuonWindows_1604.2017.922.0_x64__79rhkp1fndgsc\images\icon.ico"

# task command
bash -c 'sudo mkdir /run/screen && sudo chmod 775 $_ && sudo chown root:utmp $_ && SHELL=/usr/bin/zsh screen' -new_console:d:%USERPROFILE%

Docker for Windowsのインストール

WSLではDockerデーモンがつかえないのでNTFS (WSLからみるとdrvfs) 側で用意します。インストールはDockerのダウンロードページから手順通りおこないます。

構成は下記のようになります。

DockerクライアントからDockerデーモンにつなぐには、セキュリティリスクはありますが、 DOCKER_HOST をつかうのが簡易的です。Docker for WindowsとDockerクライアント、各々設定します。

  1. Docker for WindowsよりDockerデーモンを「Expose daemon on tcp://localhost:2375 without TLS」として設定
  2. WSL上のDockerクライアントに DOCKER_HOST=tcp://0.0.0.0:2375 を設定

WSLには下記のようなaliasを用意しておくといいでしょう。

export DOCKER_HOST=tcp://0.0.0.0:2375
alias docker="DOCKER_HOST=${DOCKER_HOST} docker"
alias docker-compose="docker-compose -H ${DOCKER_HOST}"

さて、WSLからDocker for Windowsはどの程度つかえるのか

WSLがlxfs、Docker for WindowsがNTFS (drvfs) 上で動いていることからわかるように、ファイルシステム上の制約があります。具体的には下記4点です。

  1. Docker for WindowsはNTFS (WSLからみるとdrvfs /mnt/) 上のファイルしかVolumeマウントできません
  2. WSLはLinux形式のパスしか扱えません、C:\Dev のようなドライブ名にコロンをつけたURIスキーマは扱えません
  3. WSL上のdocker-composeはパスを絶対参照しかできません、相対参照できません2
  4. WSL上のnpm/yarnによるJSビルドをNTFS (drvfs)上でおこなうとエラーになります3

ひとつずつ解決方法を見ていきましょう。

1. Docker for WindowsはNTFS (WSLからみるとdrvfs /mnt/) 上のファイルしかVolumeマウントできません

開発用ディレクトリをNTFS上につくりましょう。普段からWindowsで開発されている方はCドライブ直下につくっているとおもいます。

2. WSLはLinux形式のパスしか扱えません、ドライブ名にコロンをつけたURIスキーマは扱えません

NTFSからのパス参照とWSLからのパス参照を共通化するために、WSLに各ドライブのシンボリックリンクをはりましょう。

$ ln -s /mnt/c /C

# 開発ディレクトリはこんな感じで参照できます
$ ls -al /C/Dev
total 0
drwxrwxrwx 0 root root 512 Oct 27 00:54 .
drwxrwxrwx 0 root root 512 Dec  8 07:49 ..
drwxrwxrwx 0 root root 512 Jul 14 03:06 app-test-1
drwxrwxrwx 0 root root 512 Oct 25 00:38 app-test-2

3. WSL上のdocker-composeはパスを絶対参照しかできません、相対参照できません

各OS間での違いを吸収するため、プロジェクトに PRJ_ROOT のような環境変数を用意しましょう。

services:
  app-front:
    image: 561534604247952616898.dkr.ecr.amazonaws.com/test/front
    volumes:
      - ${PRJ_ROOT}/front:/var/www/front

4. WSL上のnpm/yarnによるJSビルドをNTFS (drvfs)上でおこなうとエラーになります

こちらはFall Creators Updateのデグレですが、更新プログラム (KB4051963) でこの問題が修正されました :tada:

もし更新プログラムが適用できない場合は、シンボリックリンクでNTFS上のnode_modulesディレクトリをWSLに移しましょう。

$ mkdir /home/foo/tmp/app-test-1/front/node_modules
$ ln -s /home/foo/tmp/app-test-1/front/node_modules /C/Dev/app-test-1/front/node_modules

-

WRAPUP

まだ未検証な部分はのこっていますが、ひととおりmacOSとWindowsによるWebアプリケーション開発は共有できるところまできている、と言えそうです。

随時、気になる課題が出てきたら追記します。

-

以上 :santa:

-