Knowlbo 開発者ブログ

株式会社Knowlboの開発者ブログです。

自作の ASP.NET アプリを乗せた Windows コンテナをビルドする

開発部の本橋です。

Windows コンテナで ASP.NET (4.6) Web アプリを実行しようとしてみたら意外と簡単にできたのでメモっておきます。

Web アプリを用意

簡単な Web アプリを用意します。今回は以下のような仕様としました。

  • ASP.NET MVC 5 (.NET Framework 4.6.2) で実装。
  • テキストエリアに任意の文字列を入力可能。
  • “POST” ボタンをクリックするとサーバー上にテキストファイルを出力。
  • 出力したテキストファイルはブラウザからアクセス可能。

コード

コントローラーとビューのコードは以下のようにしました。(ASP.NET MVC プロジェクトの作り方は省略します。)

// コントローラー
using System.IO;
using System.Web.Mvc;

namespace MyWebApp.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home/Index
        // POST: Home/Index
        public ActionResult Index()
        {
            if (this.Request.HttpMethod.ToLower() == "post") {
                var text = this.Request.Form["text"];
                var path = Path.Combine(this.Request.PhysicalApplicationPath, "out.txt");

                using (var sw = new StreamWriter(path, false)) {
                    sw.Write(text);
                }
                var appPath = this.Request.ApplicationPath.EndsWith("/")
                    ? this.Request.ApplicationPath
                    : this.Request.ApplicationPath + "/";
                this.ViewBag.file = appPath + "out.txt";
            }
            return this.View();
        }
    }
}
// ビュー
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <div>
        <form action="~/" method="post">
            <textarea name="text"></textarea>
            <button type="submit">POST</button>
        </form>
        @if (this.ViewBag.file != null) {
            <a href="@this.ViewBag.file" target="_blank">@this.ViewBag.file</a>
        }
    </div>
</body>
</html>

コンテナイメージを作る

Web アプリを発行

上記のコードをどこかのフォルダに発行します。今回は “C:\_work\MyWebApp\app” フォルダに発行したとして進みます。

Dockerfile

dockerhub のマイクロソフト公式アカウントに microsoft/aspnet というコンテナイメージがあるのでこれを使うと簡単です。

以下の内容を “C:\_work\MyWebApp\Dockerfile” として保存します。

FROM microsoft/aspnet:4.6.2

RUN mkdir c:\inetpub\wwwroot\MyWebApp
COPY app\* c:/inetpub/wwwroot/MyWebApp
RUN powershell -Command "New-WebApplication -Site 'Default Web Site' -Name 'MyWebApp' -PhysicalPath 'c:\inetpub\wwwroot\MyWebApp' -ApplicationPool 'DefaultAppPool'" \
    && icacls c:\inetpub\wwwroot\MyWebApp /grant IIS_IUSRS:F

やってることは簡単で、

  1. 発行した Web アプリをコンテナにコピー
  2. Web アプリケーションを作成
  3. フォルダにアクセス権限を与える

です。COPY 命令のコピー先フォルダのパス区切り文字は \ でなく / にする必要があるので気をつけます。

また、今回はコンテナ上にファイルを出力するため Web アプリケーションの物理フォルダに書き込めるようアクセス権限を設定します。

注:通常、コンテナ上に永続的な目的のファイルを出力するべきではありません。今回は実装の簡略化のためにコンテナ上に出力していますが、本来はホストのファイルシステムをマウントしたり、データボリュームコンテナを利用したりします。

コンテナイメージをビルド

docker build コマンドでイメージをビルドします。ベースイメージである microsoft/aspnet の容量が10GBほどあるため、キャッシュがない場合は結構時間がかかります。

cd c:\_work\MyWebApp
docker build -t mywebwpp .

実行

docker run コマンドでビルドしたコンテナを実行します。

docker run -d mywebapp

ブラウザでアクセスしてみましょう。

f:id:knowlbodev:20170217174201p:plain

“POST” ボタンをクリック↓

f:id:knowlbodev:20170217174202p:plain

“/MyWebApp/out.txt” のリンクをクリック↓

f:id:knowlbodev:20170217174203p:plain

ちゃんと動いている事が確認できます。これで自作の Web アプリケーションを Windows コンテナで実行できました!

Docker for Windows 1.13.0 が出たので Linux コンテナと Windows コンテナの切り替え機能を試してみる

開発部の本橋です。

Docker 1.13.0 の安定版がリリースされました。それに伴って Docker for Windows 1.13.0 の安定版もリリースされています。

Docker for windows

従来 Linux コンテナと Windows コンテナの共存は「出来なくはないがとても面倒」な状況でしたが、Docker for Windows 1.13.0 では簡単に共存、そして切り替えが出来るようになりました。ということで早速使ってみました。

コンテナの切り替え

タスクトレイの Docker アイコン右クリックすると「Switch to Windows Containers…」 または 「Switch to Linux Containers…」 というメニューが出て来るのでクリックします。

f:id:knowlbodev:20170125174820p:plain

数秒時間がかかりますが、「Docker is switching…」のアイコンが消えれば完了です。

f:id:knowlbodev:20170125174829p:plain

どっちのコンテナを使っているか?

docker version の出力の Server の OS/Arch でわかります。

Linux コンテナの場合は「linux/amd64」となっています。

> docker version
Client:
 Version:      1.13.0
 API version:  1.25
 Go version:   go1.7.3
 Git commit:   49bf474
 Built:        Wed Jan 18 16:20:26 2017
 OS/Arch:      windows/amd64

Server:
 Version:      1.13.0
 API version:  1.25 (minimum version 1.12)
 Go version:   go1.7.3
 Git commit:   49bf474
 Built:        Wed Jan 18 16:20:26 2017
 OS/Arch:      linux/amd64
 Experimental: true

Windows コンテナの場合は「windows/amd64」となっています。

>docker version
Client:
 Version:      1.13.0
 API version:  1.25
 Go version:   go1.7.3
 Git commit:   49bf474
 Built:        Wed Jan 18 16:20:26 2017
 OS/Arch:      windows/amd64

Server:
 Version:      1.13.0
 API version:  1.25 (minimum version 1.24)
 Go version:   go1.7.3
 Git commit:   49bf474
 Built:        Wed Jan 18 16:20:26 2017
 OS/Arch:      windows/amd64
 Experimental: true

Linux コンテナ

ごく普通に使えます。

> docker pull debian
> docker run -it debian /bin/bash
(ここからコンテナ)
# uname -a
Linux 9c4b258fd680 4.9.4-moby #1 SMP Wed Jan 18 17:04:43 UTC 2017 x86_64 GNU/Linux

Windows コンテナ

こちらもごく普通に使えますね。

> docker pull microsoft/windowsservercore
> docker run -it microsoft/windowsservercore cmd
(ここからコンテナ)
> systeminfo
Host Name:                 E631F61C1E29
OS Name:                   Microsoft Windows Server 2016 Datacenter
OS Version:                10.0.14393 N/A Build 14393
OS Manufacturer:           Microsoft Corporation
OS Configuration:          Standalone Server
(略)

images とか ps

docker imagesdocker ps で見えるイメージやコンテナの一覧は現在有効な方しか表示されないようです。

> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
debian              latest              e5599115b6a6        8 days ago          123 MB

(ここで切り替え)

> docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
microsoft/aspnet              latest              e761eca2f8df        6 days ago          10.1 GB
microsoft/windowsservercore   latest              4d83c32ad497        2 weeks ago         9.56 GB

コンテナが動いている時に切り替えると?

ステータスが「Up」のコンテナがあるときに切り替えても「Exited」になったりはしないようです。ただし docker ps してもコンテナは見えません。

> docker run -d -p 8081:80 nginx

(ブラウザで http://localhost:8081 にアクセスすると nginx がちゃんと返してくれる)

(ここで切り替え)

(切り替え後も nginx はちゃんと返してくれる)
(ただし ps しても nginx は見えない)
> docker ps
CONTAINER ID        IMAGE                         COMMAND             CREATED             STATUS              PORTS               NAMES
e631f61c1e29        microsoft/windowsservercore   "cmd"               11 minutes ago      Up 10 minutes                           festive_boyd

Linux コンテナも Windows コンテナも両方気軽に使えるようになりました!

C# REPL とスクリプト

開発部の本橋です。

Visual Studio 2015 Update 1 より csi.exe なるものが付いてくるようになりました。これは C# の REPL であり、スクリプトインタープリターのようです。ちょっと使ってみました。

REPL

スタートメニューより 開発者コマンドプロンプト for VS2015 を実行すると csi.exe のあるフォルダにパスが通っています。私の環境では以下の場所にありました。

> where csi
C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe

csi.exe を実行するとインタープリターと対話できます。

> csi
Microsoft (R) Visual C# インタラクティブ コンパイラ バージョン 1.2.0.60317
Copyright (C) Microsoft Corporation. All rights reserved.

詳細については、「#help」と入力します。
>

ここからはいつも通り C# のコードが書けます。関数や try ステートメントもいけますね。

> private int AddOne(int i)
. {
.     return i + 1;
. }
> AddOne(5)
6
>
> try {
.     throw new Exception("an error");
. }
. catch (Exception ex) {
.     Console.WriteLine(ex.Message);
.     Console.WriteLine("--------------------");
.     Console.WriteLine(ex.StackTrace);
. }
an error
--------------------
   場所 Submission#12.<<Initialize>>d__0.MoveNext()
>

スクリプト

C# スクリプトは拡張子 .csx とします。まずは簡単なファイルを用意します。

// test1.csx
using System;

private DateTime AddOneHour(DateTime dt)
{
    return dt.AddHours(1);
}

Console.WriteLine("Hello csi.");
Console.WriteLine("--------------------");
Console.WriteLine(AddOneHour(DateTime.Now))

スクリプトの実行は csi.exe にファイル名を渡してあげます。

> csi test1.csx
Hello csi.
--------------------
2017/01/17 19:04:48

クラス定義や MessageBox もいけますね。

// test2.csx
// アセンブリの参照は #r で始める
#r "System.Windows.Forms"

using System;
using System.Windows.Forms;

// メッセージボックスもいける
MessageBox.Show("Hello csi.");

// クラスもいける
public class MyClass
{
    public DateTime AddOneDay(DateTime dt)
    {
        return dt.AddDays(1);
    } 
}
MyClass myClass = new MyClass();
Console.WriteLine(myClass.AddOneDay(DateTime.Now));
> csi test2.csx
2017/01/18 18:08:44

f:id:knowlbodev:20170117181103p:plain

使いどころ

現在のところ Visual Studio に付属しているプログラムなので本番環境で利用することは難しいと思いますが、開発時にちょっとした確認が手軽にできるのは良いですね。 (「この数字、16進数だと何だっけ?」とか・・・ありますよね)

> 123.ToString("X")
"7B"

more

今回は csi.exe の本当にシンプルな使い方を試してみました。より深く知るにはこちらのページなどが足がかりになりそうです。

Essential .NET - C# スクリプト