Windows 用 Docker image を macOS で動かす

f:id:lambdalisue:20181029145429g:plain

どうも有末です。 「仕事で使ってない技術でもいいからブログ書いてくれると嬉しい」と言われ、嬉しさのあまり涙で画面が曇ったので、情報を探すのに苦労した Windows 用 Docker image を macOS で走らせる方法に関して書きます。

僕は弊社プログラマの中で最も多くの Vim プラグインを開発1しています(たぶん)。 基本的にプラグインを開発する際は対象の OS を絞らないように作っているのですが、どうしても普段利用していない Windows や Linux では環境依存のバグが発生することが有ります。 そのため TravisCI (Linux/macOS) や AppVeyor (Windows) を用いて他の OS でも CI テストを回すようにしているのですが、テストが落ちたときに原因特定するのはとても面倒です。

このような場合、テスト環境を Docker にて準備するとローカルでも同じ環境でテストが出来るため色々と捗るのですが Windows 用 Docker image は Windows container でしか動作しません(当たり前ですが)。 ただ、今回みたいなケースはサクッとテスト用に走らせたいだけなので Virtual Box とか駆使したら何とかならないかな?と色々探したところ StefanScherer/windows-docker-machine というお手軽なシステムを見つけたのでご紹介します。 ちなみにパフォーマンスとかライセンスとか諸々あるので運用利用する場合は素直に Windows ホストでやったほうが無難です。

やりたいこと

macOS のターミナルから Windows container を要求する Docker image (例: microsoft/dotnet-samples:dotnetapp-nanoserver)を可能な限りサクッと動かす。

ちなみに Docker for Mac の状態では上記 Docker image の実行は image operating system "windows" cannot be used on this platform. とか言われて動きません。

$ docker run --rm microsoft/dotnet-samples:dotnetapp-nanoserver
Unable to find image 'microsoft/dotnet-samples:dotnetapp-nanoserver' locally
dotnetapp-nanoserver: Pulling from microsoft/dotnet-samples
bce2fbc256ea: Pulling fs layer
b0b5e40cb939: Pulling fs layer
347417e0cada: Pulling fs layer
1e5ea1830940: Pulling fs layer
081f63111c09: Pulling fs layer
94504ab92c8e: Pulling fs layer
4b830cd70a22: Pulling fs layer
c09304867c0b: Pulling fs layer
04d6c936784e: Pulling fs layer
5897a999aef6: Pulling fs layer
b33a195869b0: Pulling fs layer
081f63111c09: Waiting
94504ab92c8e: Waiting
4b830cd70a22: Waiting
04d6c936784e: Waiting
b33a195869b0: Waiting
5897a999aef6: Waiting
1e5ea1830940: Waiting
c09304867c0b: Waiting
docker: image operating system "windows" cannot be used on this platform.
See 'docker run --help'.

依存アプリのインストール

Vagrant および Virtual Box を利用するので Homebrew Cask を利用してインストールしておく。

$ brew cask install vagrant virtualbox

Docker machine の構築

Docker がインストールされた Windows Server 2016 をベースに Windows container を動かすための Docker machine を構築します。 作業内容としては StefanScherer/windows-docker-machine をクローンしてきて vagrant up 2016-box 2 するだけです。 なお、初回構築時はダウンロード等で結構時間がかかります。

$ git clone https://github.com/StefanScherer/windows-docker-machine
$ cd windows-docker-machine
$ vagrant up 2016-box  # 初回は珈琲飲むくらいの時間はかかる

Docker machine の確認

上記で構築が完了すると以下のように docker-machine ls2016-box が出てきます。

$ docker-machine ls
NAME       ACTIVE   DRIVER    STATE     URL                        SWARM   DOCKER          ERRORS
2016-box   -        generic   Running   tcp://192.168.99.90:2376           v18.03.1-ee-3   

このように Windows container を動かすための環境が Docker machine 化したので、以下のように簡単に利用できます。

$ eval $(docker-machine env 2016-box)

なお、上記コマンド実行後に docker version を見ると ServerOS/Archwindows/amd64 になっています。

$ docker version
Client:
 Version:           18.06.1-ce
 API version:       1.37 (downgraded from 1.38)
 Go version:        go1.10.3
 Git commit:        e68fc7a
 Built:             Tue Aug 21 17:21:31 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.03.1-ee-3
  API version:      1.37 (minimum version 1.15)
  Go version:       go1.10.2
  Git commit:       b9a5c95
  Built:            Thu Aug 30 18:56:49 2018
  OS/Arch:          windows/amd64
  Experimental:     false

動かしてみる

この状態であれば Windows container が必要な microsoft/dotnet-samples:dotnetapp-nanoserver がちゃんと動作して、よくわからないキャラクターっぽいものが表示されます。

$ docker run --rm microsoft/dotnet-samples:dotnetapp-nanoserver

        Dotnet-bot: Welcome to using .NET Core!
    __________________
                      \
                       \
                          ....
                          ....'
                           ....
                        ..........
                    .............'..'..
                 ................'..'.....
               .......'..........'..'..'....
              ........'..........'..'..'.....
             .'....'..'..........'..'.......'.
             .'..................'...   ......
             .  ......'.........         .....
             .                           ......
            ..    .            ..        ......
           ....       .                 .......
           ......  .......          ............
            ................  ......................
            ........................'................
           ......................'..'......    .......
        .........................'..'.....       .......
     ........    ..'.............'..'....      ..........
   ..'..'...      ...............'.......      ..........
  ...'......     ...... ..........  ......         .......
 ...........   .......              ........        ......
.......        '...'.'.              '.'.'.'         ....
.......       .....'..               ..'.....
   ..       ..........               ..'........
          ............               ..............
         .............               '..............
        ...........'..              .'.'............
       ...............              .'.'.............
      .............'..               ..'..'...........
      ...............                 .'..............
       .........                        ..............
        .....


**Environment**
Platform: .NET Core 2.0
OS: Microsoft Windows 10.0.14393 

なお Docker machine なので docker-machine env --unset を eval すれば戻ります。

$ eval $(docker-machine env --unset)

$ docker version
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        e68fc7a
 Built:             Tue Aug 21 17:21:31 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.1-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       e68fc7a
  Built:            Tue Aug 21 17:29:02 2018
  OS/Arch:          linux/amd64
  Experimental:     true

おわりに

macOS からサクッと Windows container を動かすことが出来るようになったため Dockerfile 書くための試行錯誤とかも比較的楽にできるようになりました。 ここまで出来てひとまず満足してしまったので、まだ CI に活かすとかは出来てませんが…… とりあえず、Docker 便利。


  1. 残念ながら弊社の仕事として Vim プラグインを開発しているわけではありません。あくまでも趣味です。

  2. 簡略化のために 2016-box を指定していますが、通常は README の通り StefanScherer/packer-windows を使って自前構築する方が良いと思います(びっくりするくらい時間かかりますが)。