Windows Server 2019でWindowsコンテナを動かすまでのチュートリアル


この記事の目的

Windows Server 2019環境で、とにかくてっとり早くdockerコンテナを使えるようにするための手順をお伝えします。

MSDNの公式ページにもチュートリアルがあり、こちらも併せて参照いただくと理解が深まるかと思います。1

https://docs.microsoft.com/ja-jp/virtualization/windowscontainers/quick-start/quick-start-windows-server


Windows Server 2019のインストール~dockerが使えるまでの手順


インストール手順


  1. Windows Server 2019をインストール (下記はすべてデスクトップエクスペリエンスで動作を確認)

  2. 管理者モードでPowerShell起動 (以降すべてのPowerShellコマンドは管理者モードで実行)


  3. コンテナ機能の有効化

    Install-WindowsFeature containers
    



  4. サーバーの再起動

    Restart-Computer -Force
    



  5. Dockerのインストール

    # パッケージ管理のプロバイダを追加
    
    Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
    # dockerをインストール
    Install-Package -Name docker -ProviderName DockerMsftProvider



  6. サーバーの再起動

    Restart-Computer -Force
    



  7. Proxyの設定

    setx HTTP_PROXY http://proxy.example.com:8888/ /M
    
    setx HTTPS_PROXY http://proxy.example.com:8888/ /M



動作確認



  1. dockerのバージョン確認コマンドを実行

    docker version
    



  2. 下記のように結果が返ってくればOK

    Client: Docker Engine - Enterprise
    
    Version: 18.09.5
    API version: 1.39
    Go version: go1.10.8
    Git commit: be4553c277
    Built: 04/11/2019 06:44:52
    OS/Arch: windows/amd64
    Experimental: false

    Server: Docker Engine - Enterprise
    Engine:
    Version: 18.09.5
    API version: 1.39 (minimum version 1.24)
    Go version: go1.10.8
    Git commit: be4553c277
    Built: 04/11/2019 06:43:04
    OS/Arch: windows/amd64
    Experimental: false




  3. 使ってみよう

    docker container run hello-world:nanoserver
    

    hello-world:nanoserverという名前のイメージをコンテナとして起動する命令です。

    ただし、初回は当該の環境にhello-world:nanoserverという名前のイメージがまだ無いので、最初にDocker Hubからのダウンロードが行われます。

    Unable to find image 'hello-world:nanoserver' locally
    
    nanoserver: Pulling from library/hello-world
    b22999bfb02f: Pull complete
    c8353a1aacae: Pull complete
    8c51178a6c10: Pull complete
    Digest: sha256:347e0c5e55751d2ee2470f1a57463c26e81515583838286b67ae7d140d0480da
    Status: Downloaded newer image for hello-world:nanoserver

    上記の後、コンテナが起動し、コンソールに下記の文字列を返します。

    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.
    (windows-amd64, nanoserver-1809)
    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 a Windows Server container with:
    PS C:\> docker run -it mcr.microsoft.com/windows/servercore powershell

    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/




ちょっと解説



  • コンテナのライフサイクル

    上記のDockerイメージは、起動すると文字列を上記のようにコンソールに印字するプロセスが立ち上がり、このプロセスが終わるとコンテナそのものも停止します。Dockerでは、コンテナ化したときにコンテナ内で開始されるプロセスとライフサイクルを共にしています。




  • イメージの取得

    上記では、イメージのダウンロードが暗黙的に実行されましたが、docker image pullコマンドを実行すると、イメージのダウンロードのみを実行することも可能です。

    イメージは、Docker Hub上のものだけではなく、これらのイメージをベースにして自分で独自のイメージを作成することができます。この作業をBuildと呼びます。(作り方は後述)




  • イメージの名前

    イメージの名前には命名規則があります。先ほどの例では、hello-world:nanoserverという名前でしたが、これはイメージ名:タグ名という2つの要素で一意にイメージを特定しています。

    タグの役割は、イメージのバージョンを明示したり、ベースとなるOSを区別する目的などで使われています。




  • LinuxコンテナとWindowsコンテナ

    コンテナ技術は、OSの仮想化と表現されます。1つしかないOSを、まるで独立した複数のOSが存在しているかのように振舞わせることで分離された環境でコンテナのプロセスを立ち上げています。

    このとき、コンテナ上のカーネルのプロセスはホスト側となるOSのカーネルを共有します。このため、Windows Serverをコンテナのホストとした場合、基本的にはWindows Serverのイメージしかコンテナとして実行することができません。加えて、Windows Server 20191は、OSのメジャー・マイナー・ビルドの3つのバージョンまでがホスト側と一致している必要があります(参照:コンテナのバージョンの互換性




自分のイメージを作ってみよう


一般的なイメージの作成について

開発したアプリケーションおよび依存モジュールをまとめて一つのDockerイメージを作成することができます。

アプリケーションおよび依存モジュールを一つにパッキングできるため、アプリケーションはコンテナのホスト側の環境に依存せずに動作することができます。

イメージをビルドするためにはDockerfileというdockerに対するイメージ作成の指示書のようなものを作成します。

概ね次のような手順でDockerfileを記述していきます。


  1. 空のテキストファイルを作成しDockerfileという名前で保存。

  2. ベースとなるイメージを決定する。
    補足)Windows Server 2019をコンテナホストにする場合、純粋にOSのみをベースにしたいときは下記が選択肢になります。


    • Windows Server 2019 Server Coreイメージ (mcr.microsoft.com/windows/servercore:ltsc2019)

    • Windows Server 2019 Nano Serverイメージ (mcr.microsoft.com/windows/nanoserver:1809)



  3. ベースとなるイメージに、アプリケーションやその依存モジュールなどを配置。

  4. ソフトウェアのインストール、ファイルとディレクトリの作成、環境構成の作成など必要に応じて実施。


Dockerfileの作成

本チュートリアルでは、MSDNのドキュメント(前述)に倣って、IISで静的なページを返すイメージを作成します。


  1. 空のテキストファイルを作成しDockerfileという名前で保存。

  2. 同じフォルダにindex.htmlという名前のファイルを作成し、HellowWorldと記述して保存する。


  3. Dockerfileに次のように記述。

    FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
    
    ADD index.html c:/inetpub/wwwroot



Dockerfileのビルドとコンテナ化

Dockerfileができたらビルドを行いイメージを作成します。


  1. Dockerfileと同じディレクトリに移動。



  2. 下記のコマンドを実行しビルド実行。

    下記の例では、tutorial:latestという名前でイメージが生成される。

    docker build -t tutorial:latest .
    



  3. ビルドが成功したイメージをコンテナとして実行する。

    先の例と同じように、イメージ名としてtutorialを指定して実行する。(コンテナの実行を指示する時は、どのディレクトリからでも可能)

    docker container run -d -p 8888:80 tutorial:latest
    

    上記は、コンテナホスト側のポート8888番をコンテナ側のポート80にフォワードするように指定しています。



  4. ブラウザでlocalhost:8888にアクセスし、Hello Worldと表示されれば成功。

    HelloWorld_Browser.png



  5. コンテナホストで、docker container ls を実行し、コンテナが実行されていることを確認してみる。

    C:\Users\Administrator\Desktop\tutorial>docker container ls
    
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    d2527cb41c15 tutorial:latest "C:\\ServiceMonitor.e…" 11 minutes ago Up 11 minutes 0.0.0.0:8888->80/tcp hopeful_yalow

    このとき、上記のCONTAINER IDとNAMESは勝手に付与されます。(NAMESについては明示的に指定することも可能)

    はじめに起動させたhello-world:nanoserverは、コンソールに文字を打刻してすぐに停止してしまいました。しかし、上記のコンテナは、ずっと起動したままになっています。

    この理由は、コンテナ化したときに起動するプロセスが、停止しないようなプロセス(ping /t localhostのように)であるためです。

    今回の例では、ServiceMoniter.exeというプロセスが生存し続けているために、コンテナが起動したままになっています。




  6. docker execで動いているコンテナに入ってみる。

    docker exec -it %実際のCONTAINER ID値% cmd
    

    コンテナでシェルが起動し、次のような画面が返ってくる。

    Microsoft Windows [Version 10.0.17763.503]
    
    (c) 2018 Microsoft Corporation. All rights reserved.

    C:\>

    色々、打ち込んでみる。試しにdirコマンドを実行すると、コンテナのシステムドライブ(C:)直下のファイル・ディレクトリが返ってくる。

    C:\>dir
    
    Volume in drive C has no label.
    Volume Serial Number is 4084-DF79

    Directory of C:\

    05/16/2019 06:20 AM <DIR> inetpub
    09/15/2018 06:42 PM 5,510 License.txt
    05/13/2019 01:25 PM <DIR> Program Files
    05/13/2019 01:24 PM <DIR> Program Files (x86)
    05/16/2019 06:21 AM 172,328 ServiceMonitor.exe
    05/13/2019 01:26 PM <DIR> Users
    05/16/2019 06:20 AM <DIR> Windows
    2 File(s) 177,838 bytes
    5 Dir(s) 21,092,855,808 bytes free

    inetpub\wwwrootに移動し、index.htmlを見てみると、、あたりまえだがDockerfileで格納(ADD)したindex.htmlがちゃんと入っている。

    C:\>cd inetpub\wwwroot

    C:\inetpub\wwwroot>dir
    Volume in drive C has no label.
    Volume Serial Number is 4084-DF79

    Directory of C:\inetpub\wwwroot

    06/06/2019 02:39 PM <DIR> .
    06/06/2019 02:39 PM <DIR> ..
    05/16/2019 06:20 AM 703 iisstart.htm
    05/16/2019 06:20 AM 99,710 iisstart.png
    06/06/2019 02:38 PM 11 index.html
    3 File(s) 100,424 bytes
    2 Dir(s) 21,092,855,808 bytes free

    C:\inetpub\wwwroot>type index.html
    Hello World
    C:\inetpub\wwwroot>




  7. コンテナの状態を変更してみる。

    docker execコマンドの続きで、下記のように実行。

    C:\inetpub\wwwroot>echo %COMPUTERNAME%
    
    D2527CB41C15

    C:\inetpub\wwwroot>echo %COMPUTERNAME% > index.html

    この状態で、もう一度ブラウザでlocalhost:8888にアクセスしてみると、次のようにコンピュータ名が表示されるようになります。

    COMPUTERNAME_Browser.png




  8. コンテナを止めてみる。

    docker execコマンドを一旦終了するため、exitを入力します。

    C:\inetpub\wwwroot>exit

    C:\Users\Administrator\Desktop\tutorial>

    ホスト側のシェルに戻ってきました。ここでdocker container stop %実際のCONTAINER ID値%と入力すると、起動していたコンテナが停止します。(ブラウザでlocalhost:8888にアクセスし、ページが返ってこないことを確認します。)

    この後、docker container start %実際のCONTAINER ID値%と入力すると、停止したコンテナが再び起動します。(ブラウザでlocalhost:8888にアクセスし、再びページが表示されることを確認します。)

    この状態で、次のコマンドを実行してみます。

    docker container run -d -p 8889:80 tutorial:latest
    

    今度は、別のコンテナがイメージから新たに生成されます。ブラウザでlocalhost:8889にアクセスすると「Hello World」と表示されるはずです。先ほどindex.htmlをCOMPUTERNAMEで置き換えた変更は、当該のコンテナのみに反映されますが、イメージとしては「Hello World」のままなので、新たにコンテナを立ち上げるとこのようになります。

    docker container run -d -p 8890:80 tutorial:latest
    
    docker container run -d -p 8891:80 tutorial:latest
    docker container run -d -p 8892:80 tutorial:latest
    docker container run -d -p 8893:80 tutorial:latest
    docker container run -d -p 8894:80 tutorial:latest
    ...
    docker container run -d -p 8899:80 tutorial:latest

    調子に乗っていくつもコンテナを立ち上げることができます。上記のように実行すると10台のコンテナが立ち上がります。

    それぞれのコンテナのリソース消費量は、docker stats %実際のCONTAINER ID%コマンドで確認できます。

    docker stat 32755b73a12c        

    CONTAINER ID NAME CPU % PRIV WORKING SET NET I/O BLOCK I/O
    32755b73a12c silly_mestorf 0.00% 58.06MiB 12.7kB / 5.09kB 17MB / 5.84MB

    上記の通り、このコンテナの例では、1台あたり60MB程度のメモリーしか消費していないことがわかります。10台立ち上げても600MB程度で済みます。

    また、環境にもよりますが、上記のコマンドを実行してコンテナが起動するのに掛かる時間は1秒~数秒程度です。

    ESXiやHyper-Vで仮想マシンを立ち上げる際に必要となるリソースや起動時間と比較して、少ないメモリー量かつ高速に起動することを体感することができます。




ここまでできた方へ

まとまった規模のシステムになると、複数のコンテナ(DB/Web/Cache/SearchEngine...etc)を組み合わせて一つのシステムを作り上げたくなります。このようなときにもDocker-Composeというツールを使うと、複数のコンテナをまとめて立ち上げたり、落としたりすることができ、コンテナ間の名前解決もよろしくしてくれますので、Docker-Composeについて慣れていくと良いかと思います。

詳しい手順は下記が参考になります。

https://docs.docker.com/compose/install/#install-compose

かなりざっくりしていますが、ざあっと、下記のコマンドでDocker-Composeが使えるようになります。(Powershell)

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Invoke-WebRequest "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-Windows-x86_64.exe" -UseBasicParsing -OutFile $Env:ProgramFiles\docker\docker-compose.exe

# docker Serviceの再起動
Restart-Service Docker





  1. 記事を書いた後で気づきましたが、前に閲覧したときよりかなり充実していて、本記事要らないかもとも思いました。。