2017-09-13

Dockerでnginx + ASP.NET Core 2.0のWebサーバ環境を構築する

Docker for WindowsでDockerの使い方がなんとなく分かってきたので、 nginxとASP.NET Core 2.0を使ったWebサーバとアプリケーションサーバの環境を構築してみたいと思います。

動作確認レベルなので基本的には難しいことには手を出さずに docker hubで提供されているテンプレートをベースにして ブラウザからDocker上に構築したサイトへアクセス可能かどうかだけを確認します。

使用するイメージとコンテナの数について

使用するイメージはdocker hubで公開されている 「nginx:latest」と「aspnetcore:latest」の2つです。

コンテナ数については、Webサーバ(nginx)とアプリケーションサーバ(ASP.NET Core)で構成されたシステムにするので、 それぞれコンテナを1つずつの計2つ作成します。

本当はDBサーバを追加して3つにしたかったのですが、 一度にやろうとすると上手くいかないことが多いので今回は除外しています。

ASP.NET CoreプロジェクトやDocker関連ファイルの構成

大体、以下のような感じです。

c
├─ docker
│  └─ test
│    ├─ app
│    │ ├─ publish
│    │ └─ Dockerfile
│    ├─ nginx
│    │ └─ nginx.conf
│    └─ docker-compose.yml
│
└─ work
  └─ vs2017projects
    └─ WebApplicationTest1
      ├─ WebApplicationTest1.sln
      └─ WebApplicationTest1
        ├─ WebApplicationTest1.csproj
        └─ bin
          └─ Release
            └─ PublishOutput

ASP.NET CoreのWebアプリケーション作成から配置まで

Visual Studio 2017を起動してプロジェクトの新規作成を選択します。

ASP.NET Core Webアプリケーションを選択してOKボタンをクリックすると使用するCoreフレームワークのバージョンや初期テンプレートが選択できるので、 今回は「Webアプリケーション(MVC)」を選んでプロジェクトを作成します。

作成されたプロジェクトの構成は以下のようになっていると思います。

まず、デフォルトではlocalhostからのアクセスからしか許可されていないので、Webアプリケーションを外部に公開する設定を追加します。

上記、エクスプローラーの「Program.cs」を開いて、「UseUrls()」を追記します。 ポート番号は任意の番号でOKですが、以降の手順は「5000」番で行っていますので変更する場合は適宜読み替えてください。


public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseUrls("http://*:5000/")
        .UseStartup<Startup>()
        .Build();

単純な動作確認であれば上記の外部公開設定だけでいいのですが、 今回は特定のサブディレクトリにアクセスされた時のみASP.NET CoreのWebアプリケーションを実行させるようにするので、 リリースビルド(公開)時のみbaseタグによるURLの設定を追加して、 ライブラリやCSS、画像などの外部ファイルのパスの先頭に付加されているチルダとスラッシュを削除して単純な相対パスでの指定に変更します。 (ソースの修正はサブディレクトリでアクセスする場合のみ必要。 ホスト名やIPアドレスだけ、またはサブドメインでアクセスして動作確認をする際は発行処理まで読み飛ばしてください。)

「Views/Shared/_Layout.cshtml」を以下のように修正。


...
<environment exclude="Development">
    <base href="/wwwroot-aspnetcoretest/" target="_self"/>
    ...
    <link rel="stylesheet" href="css/site.min.css" asp-append-version="true" />
</environment>
...
<environment exclude="Development">
    ...
    <script src="js/site.min.js" asp-append-version="true"></script>
</environment>
...

続いて「Views/Home/index.cshtml」のSVGファイル参照パスからもチルダとスラッシュを削除します。


...
<img src="images/banner1.svg" alt="ASP.NET" class="img-responsive" />
...
<img src="images/banner2.svg" alt="Visual Studio" class="img-responsive" />
...
<img src="images/banner3.svg" alt="Package Management" class="img-responsive" />
...
<img src="images/banner4.svg" alt="Microsoft Azure" class="img-responsive" />
...

今回はサブディレクトリ名として「aspnetcoretest」を指定するので、「Startup.cs」のapp.UseMvc()を以下のように修正します。


app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "aspnetcoretest/{controller=Home}/{action=Index}/{id?}");
});

ついでにデバッグ実行時にも同様な動作とするため、「プロジェクトのプロパティ > デバッグ > Webサーバーの設定 > アプリURL」の設定を以下のように変更します。


http://localhost:62443/aspnetcoretest

デバッグ実行の動作確認で問題がなければ、発行処理を行います。(プロジェクトを右クリック > 発行 > フォルダー選択 > 発行開始)

発行が終了すると出力先のフォルダにファイルが作成されています。

最後に出力されたファイルを移動します。(Dockerでマウントするフォルダの場所を発行処理の出力先フォルダにするのであればファイルの移動は不要)

今回は「WebApplicationTest1 > bin > Release > PublishOutput」の全ファイルを「docker > test > app > publish」配下にすべてコピーして以降の作業を行います。

Docker Composeファイルの作成(docker-compose.yml)

複数のコンテナを作成・管理する場合、通常のdockerだけでは不便なのでdocker-composeを利用します。

Windowsの場合、Docker for Windowsをインストールしていれば最初からdocker-composeコマンドが使えるので、新たにインストールする必要はありません。

使い方も簡単でdocker-composeコマンド実行するフォルダ配下に「docker-compose.yml」を用意します。

今回の動作確認で使うdocker-compose.ymlファイルの中身は以下の通りです。

version: '3'
services:
  app:
    build:
      context:  ./app
      dockerfile: Dockerfile
  web:
    image: nginx:latest
    ports:
      - 8080:80
    links:
      - "app"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./app/publish/wwwroot:/usr/share/nginx/html/wwwroot-aspnetcoretest

versionは指定する番号によって書き方が異なります。(Compose file version 3 reference | Docker Documentation

ASP.NET Coreで構成されたアプリケーションサーバはちょっと指定が複雑なので、 コンテナの作成に「app」フォルダ配下にある「Dockerfile」を参照するように指定しています。

nginxについてはローカルのブラウザからアクセスする際のポート(posts)の指定とリバースプロキシでアプリケーションサーバにアクセスしないといけないので、 生成されたコンテナのnginx.confを事前にこちらで修正したものに置き換える(volumes)ようにしています。

Volumesの2つ目に指定しているコマンドは、ASP.NET Coreプロジェクトを発行した際に「wwwroot」配下に出力されるファイルを、 Webサーバ(nginx)側で参照できる階層にマウントしています。 (このコマンドはサブディレクトリでアクセスした際にcss、js、svgといったwwwroot配下のファイルが参照できなかったので、Webサーバ側(nginx)に配置したものです。 暫定対応に近いのでアプリケーションサーバから参照できるなら必要ありません。 サブディレクトリでのアクセスを止めたら正常に参照できたので、VisualStudioの設定やURLの記述に問題があるものと思います。)

Dockerfileファイルの作成(Dockerfile)

FROM microsoft/aspnetcore:latest
#RUN apt-get update && apt-get install -my wget gnupg
#RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
#RUN apt-get install -y build-essential nodejs

WORKDIR /app
COPY publish .

ENV ASPNETCORE_URLS http://+:5000
EXPOSE 5000

ENTRYPOINT ["dotnet", "WebApplicationTest1.dll"]

動作確認用に作成したプロジェクトでは問題ありませんでしたが、 Node.jsが使われているWebアプリケーションプロジェクト(AngularやReactベースのテンプレート)を使っていた場合、 上記のコマンドではNode.jsがインストールされていないせいかエラーが出ます。

その場合は、コメントにしているコマンドを有効にして実行してください。(centos系であればgnupgのインストールはおそらく不要)

nginx.confファイルの作成

user  nginx;
worker_processes  1;

events {
    worker_connections 1024;
}

http {
#    include /etc/nginx/conf.d/*.conf;

    upstream app_servers {
        server app:5000;
    }

    server {
        listen 80;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }

        location /aspnetcoretest {
            proxy_pass         http://app_servers/aspnetcoretest;
            proxy_redirect     off;
        }
    }
}

見やすいようにログやその他の細かい設定は省いています。

サブドメインの設定を行う場合、「server_name」にサブドメインを含んだドメインを指定します。 詳しくは「nginxのサブドメインでrailsの複数アプリを運用 - mikami's engineer diary」を参照。

Dockerの実行

作成したdocker-compose.ymlファイルを実行するには、 コマンドプロンプトやWindows PowerShellを起動して該当ファイルが置かれているフォルダへ移動した後に以下のコマンドを実行します。

docker-compose up -d --build

コマンドが正常に終了するとコンテナが稼働します。

以下はKitematicで稼働後のコンテナを表示したものです。

稼働中のコンテナへ接続するコマンド。

docker-compose exec web /bin/bash

停止するコマンド。

docker-compose stop

その他のdocker-compose用のコマンドはこちら(docker-compose コマンドまとめ - Qiita)を参照。

動作確認

「http://localhost:8080/ascnetcoretest」にアクセスすると、 ASP.NET Coreで作成したWebアプリケーションが表示されます。

「http://localhost:8080」にアクセスすると、nginxの既定のページが表示されているので、 サブディレクトリにアクセスされた時のみASP.NET Coreアプリケーションが実行されているのが分かります。

最後に

サブディレクトリでアクセスするサーバを切り替えるようにしたせいでかなりハマってしまいましたが、 最終的にはサブドメインで切り替えると思うので、ちょっとした徒労感があります。

この設定で問題ないのかかなり不安なので、最終的には色々と見直しをするかもしれませんが、 とりあえず動作したので今回はよしとします。

参考リンク

その他」の記事

最新のプログラミング記事