ASP.NET Core MVC アプリケーションの Docker コンテナを Heroku で動かしてみた

以前、最小の ASP.NET Core アプリケーションの Docker コンテナを Heroku で動かせた。

ただ、ASP.NET Core MVC アプリケーションでは、Heroku 上で NuGet パッケージの復元を実行しようとしていて失敗。

NuGet パッケージの復元で失敗するのなら、復元の必要が無いように、発行したアプリから Docker イメージを作ってしまえばいいかもしれない。

再挑戦してみる。

Heroku CLI をインストール

新しい MacBook Pro に Heroku CLI が入ってなかったので、Homebrew でインストールする。

brew install heroku/brew/heroku

Heroku にログイン

heroku login
heroku container:login

ASP.NET Core MVC プロジェクトを作成

最小の ASP.NET Core MVC アプリケーションの Docker イメージを作りたいので、dotnet new コマンドで生成された雛形をそのまま使う。

mkdir HelloAspNetCore
cd HelloAspNetCore
dotnet new mvc

ASP.NET Core MVC プロジェクトを発行

dotnet publish -c Release

を実行すると、./bin/release/netcoreapp2.0/ 下に実行に必要なファイルがすべて出力される。

Docker ファイルを作成

ファイル一式が出力されたディレクトリに Docker ファイルを作成する。

cd ./bin/release/netcoreapp2.0/publish

ディレクトリの中身をコピーして、ASP.NET Core アプリケーションを起動する Docker ファイルを書く。 ポートは Heroku から環境変数で渡される値を使う。

FROM microsoft/aspnetcore

WORKDIR /app

COPY . .

CMD ASPNETCORE_URLS=http://*:$PORT dotnet HelloAspNetCore.dll

Docker イメージを作成

docker build -t <イメージ名> .

Docker イメージに Heroku 用のタグを設定

Heroku アプリは事前に作成しておいたものを使っておく。

docker tag <イメージ名> registry.heroku.com/<Heroku アプリ名>/web

Heroku にデプロイ

Heroku が用意したレジストリにプッシュすると、Heroku 上で実行される。

docker push registry.heroku.com/<Heroku アプリ名>/web

確認

Web ブラウザでアプリの URL にアクセスすると、今回はちゃんとおなじみのページを表示できた。

f:id:griefworker:20180114061917p:plain

ASP.NET Core MVC アプリケーションの Docker コンテナを Heroku で動かすのはこの方法で良さそうだ。次の課題は Heroku Postgres の利用だな。

ASP.NET Core アプリケーションの Docker コンテナを Heroku で動かしてみた

はじめに

ASP.NET Core アプリケーションを Docker で動かすことができた。

tnakamura.hatenablog.com

そういえば、Heroku で任意の Docker コンテナを動かすことができるようになっていたな。 まだベータ版な上、制限もあるみたいだけど。

もしかしたら ASP.NET Core アプリケーションの Docker イメージを Heroku にデプロイして動かせるかも? やってみた。

いきなり結論

結論として Empty Web Application は動かすことができた。 Hello World を HTTP レスポンスのボディに書き込むだけのやつ。 ASP.NET Core MVC は動かせなかった。

なので、以下では Empty Web Application を動かした手順を書いていく。

Docker をインストール

Docker は正式版になったばかりの Docker for Mac を使う。

docs.docker.com

Empty Web Application を作成

yo でプロジェクトを新規作成する。

$ yo aspnet

     _-----_     ╭──────────────────────────╮
    |       |    │      Welcome to the      │
    |--(o)--|    │  marvellous ASP.NET Core │
   `---------´   │        generator!        │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? What type of application do you want to create? Empty Web Application
? What's the name of your ASP.NET application? HelloAspNetCore
   create HelloAspNetCore/.gitignore
   create HelloAspNetCore/Program.cs
   create HelloAspNetCore/Startup.cs
   create HelloAspNetCore/project.json
   create HelloAspNetCore/web.config
   create HelloAspNetCore/Dockerfile
   create HelloAspNetCore/Properties/launchSettings.json
   create HelloAspNetCore/README.md


Your project is now created, you can use the following commands to get going
    cd "HelloAspNetCore"
    dotnet restore
    dotnet build (optional, build will also happen when it's run)
    dotnet run

生成された Dockerfile を修正

Heroku の Docker 対応はまだ制限があって、Dockerfile には CMD が必須。 また、ポートは Heroku が割り当てるやつを使わないといけない。

yo で生成したプロジェクトの Dockerfile を次のように修正した。

FROM microsoft/dotnet:latest

COPY . /app

WORKDIR /app

RUN dotnet restore

RUN dotnet build

#EXPOSE 5000/tcp

CMD dotnet run --server.urls http://0.0.0.0:$PORT

Heroku プラグインをインストール

heroku toolbelt をインストールしていなければ、インストールしておく。

toolbelt.heroku.com

heroku-container-registry プラグインを使う。

$ heroku plugins:install heroku-container-registry

Heroku にデプロイ

<app_name> には Heroku 上で作成したアプリケーション名を指定する。 自分はテスト用に作ったアプリケーションを使いまわすことが多いのもので。

$ heroku container:login
$ heroku container:push web --app <app_name>

を実行すると、Dockerfile をもとに Docker イメージが作成され、 それが Heroku の Docker レジストリにプッシュされているように見える。

$ heroku open --app <app_name>

でブラウザを起動すると、Hello world が表示された。

f:id:griefworker:20160824001313p:plain

ちなみに ASP.NET Core MVC

yo で ASP.NET Core MVC のプロジェクトを生成して、 同じように Dockerfile を修正してデプロイしてみたが、 アプリケーションの起動に失敗していた。

heroku logs でログを見たところ、NuGet パッケージのリストアで失敗していた模様。 パッケージを resolve できてないみたいだった。

Empty Web Application から1つずつパッケージを増やしていって、 どのパッケージが原因か特定しようとしたけど、 さすがに数が多くて時間切れ。

そもそも、なんでリストアが実行されたんだろう? NuGet パッケージダウンロード済みの Docker イメージをデプロイしたんじゃないのか? Docker の勉強が足りてないんで、自分のやり方が悪かった可能性も十分ある。

時間が空いたときに再挑戦、するかもしれない。

Heroku で最小の ASP.NET 5 アプリを動かす

Heroku が .NET サポートを予定しているのか知らないけど、 Github には heroku/dotnet-buildpack という ASP.NET 5 用のビルドパックがある。

github.com

これを使えば ASP.NET 5 アプリを Heroku で動かせると思いきや、 Mac OSX で作成したプロジェクトをデプロイすると、 コンパイル時に「アーキテクチャx64x86 にしろゴルァ」ってエラー発生。

このエラーに関しては、既にプルリクエストが存在するので、 問題無ければさっさとマージしてほしいところ。 プルリクエストを送っている側の dotnet-buildpack を使うと上手くいった。

github.com

手順は次の通り。

# DNVM を入れてなければインストール
brew tap aspnet/dnx
brew update
brew install dnvm

# dotnet-buildpack は Mono なので
# 最新の DNX for Mono をインストールする
dnvm upgrade -r mono

# Node を入れてなければインストール
brew install node

# yo と ASP.NET 5 のジェネレーターをインストール
npm install -g yo generator-aspnet

# ASP.NET 5 のプロジェクトを作成
# タイプは Empty Application を選択
# 名前は HerokuAspNet にでもしておく
yo aspnet

# 依存パッケージをインストール
cd HerokuAspNet
dnu restore

# 最小の ASP.NET 5 アプリを動かすのでそのまま Git でコミット
git init .
git add .
git commit -m "Initial commit"

# Heroku Toolbelt を入れてなければインストール
brew install heroku-toolbelt

# Heroku にアプリを作成してデプロイ
# ビルドパックは ASP.NET 5 用のものを使う
heroku create --buildpack https://github.com/jincod/dotnet-buildpack.git
git push heroku master

# ブラウザで表示
heroku open

Heroku で ASP.NET 5 アプリが動いているぞ!

f:id:griefworker:20160105200803p:plain

データベースは使っていないけど、Entity Framework 7 や Dapper に Npgsql を組み合わせれば、 Heroku Postgres も使えるはず。

最小の Owin アプリケーションを Heroku にデプロイしてベンチマーク計測してみた

はじめに

heroku-buildback-mono を使えば C# で書いた WEB アプリを Heroku で動かすことができる。 ランタイムは .NET Framework じゃなくて Mono だけど。

Heroku + Mono のパフォーマンスが知りたくなったので 最小の Owin アプリケーションをデプロイして計測することにした。

コンソールアプリケーションのプロジェクトを作成

セルフホストなのでコンソールアプリケーションのプロジェクトを作成する。

NuGet パッケージを追加

NuGet パッケージマネージャーで

を追加。

Visual Studio の場合は、NuGet Package の復元を有効化しておく。 MonoDevelop(Xamarin Studio) の場合は不要だった。

最小の Owin アプリケーションを作成

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Owin;
using Microsoft.Owin.Hosting;
using Microsoft.Owin.Host.HttpListener;

namespace OwinSample
{
  using AppFunc = Func<IDictionary<string, object>, Task>;

  public class SampleApp
  {
    public SampleApp(AppFunc next)
    {
    }

    public Task Invoke(IDictionary<string, object> environment)
    {
      var stream = (Stream)environment["owin.ResponseBody"];
      using (var writer = new StreamWriter (stream))
      {
        return writer.WriteAsync ("Hello World");
      }
    }
  }

  public class Startup
  {
    public void Configuration(IAppBuilder app)
    {
      app.Use(typeof(SampleApp));
    }
  }

  class MainClass
  {
    private static readonly ManualResetEvent _quitEvent = new ManualResetEvent(false);

    public static void Main (string[] args)
    {
      var port = 5000;
      if (0 < args.Length)
      {
        int.TryParse (args [0], out port);
      }

      Console.CancelKeyPress += (sender, e) =>
      {
        _quitEvent.Set();
        e.Cancel = true;
      };

      using (WebApp.Start<Startup>(string.Format("http://*:{0}", port)))
      {
        Console.WriteLine("Started");
        _quitEvent.WaitOne();
      }
    }
  }
}

Procfile を作成

Procfile には、コンソールアプリケーションを実行するコマンドを記述する。

web: mono OwinSample.exe $PORT

ビルドするとプロジェクトルートに exe が出力されるので、mono コマンドでそいつを起動している。

Heroku にデプロイ

Heroku のユーザー登録と SSH キー登録は済んでいる前提。

$ heroku create <your-app-name>
$ heroku config:add BUILDPACK_URL=https://github.com/friism/heroku-buildpack-mono
$ git push heroku master

を実行し、 Mono のビルドパックを使うようにしておく。

デプロイに成功したら Webブラウザでアクセスしてみる。

f:id:griefworker:20141231143512p:plain

Apatch Bench でベンチマーク計測

Ruby スタックと比較したいので、同じ合計リクエスト発行数と同時接続数で計測した。

合計リクエスト発行数&同時接続数 Requests per second[#/sec] Time per request(mean, across all concurrent requests)[ms]
-n 100 -c 100 29.40 34.015
-n 1000 -c 100 55.60 17.985
-n 1000 -c 1000 42.18 23.710
-n 3000 -c 1000 47.78 20.930
-n 2500 -c 2500 39.52 25.305
-n 3000 -c 2500 43.83 22.815

Mono スタックよりも Ruby スタックの方が性能が良かった。 今回の計測では、ともにアプリケーションサーバーを使っていないから、参考にしかならないけど。

最小のRackアプリケーションをHerokuにデプロイしてベンチマークを計測してみた

Heroku の Ruby スタックの性能が知りたかったので、 下記ような最小の Rack アプリケーションを Heroku にデプロイし、 Apatch Bentch でベンチマークを計測してみた。 Rack アプリケーションは unicorn とか使わず rackup コマンドで動かしている。

require "rack"

class SampleApp
  def call(env)
    [200, { "Content-Type" => "text/html" }, ["Hello World"]]
  end
end

run SampleApp.new

計測結果は下表の通り。すべてのリクエストが成功したものだけを載せている。

合計リクエスト発行数&同時接続数 Requests per second[#/sec] Time per request(mean, across all concurrent requests)[ms]
-n 100 -c 100 33.51 29.838
-n 1000 -c 100 57.03 17.536
-n 1000 -c 1000 36.64 27.924
-n 3000 -c 1000 45.05 22.199
-n 2500 -c 2500 41.33 24.193
-n 3000 -c 2500 45.79 21.838

同時接続数は 2500、合計リクエスト数 3000 あたりが上限だった。

プロフェッショナルのための実践Heroku入門

趣味レベルで Heroku にはさわってきたけど、 公式のドキュメントを隅から隅まで読んだわけではないので、 知らない情報を本書から得ることができた。

例えば Herokuのアーキテクチャ、特に Slug。 .slugignore に Slug に含めないファイルを指定して、 Slug コンパイル高速化&サイズ縮小する手法なんかは即試してみた。

本番環境へ移行するときに役立つであろう情報がまとまっているのもありがたい。 SSL の導入手順や、 Heroku Postgres でのデータベースのバックアップ方法と移行方法は、 「なんとなく知ってるけどきちんと調べなきゃいけないな」と思っていただけにタイムリーだった。

欲を言えば、アドオンの解説がもっと欲しいところ。 Logging 系のアドオンは必要って何度も書いてあるから、 Papertrail とか紹介してもよかったと思う。 あと NewRelic はもはや必須アドオンといっても過言じゃないので、 他の説明のついでではなく、ちゃんと専用にページを割いて欲しかった。

最初、買おうかどうか迷っていたけど、 新しい情報を得ることができたし、本番環境へ移行するための方法もゲットできたので、 今では買ってよかったと思っている。

Heroku Postgres のデータベースをリセットする方法メモ

忘れて毎回ネットで検索しているので、ブログにメモしておく。

heroku pg:reset DATABASE

上記コマンドをそのままターミナルにコピペして実行すればいい。DATABASE のところを置き換える必要なし。

ステージング用とプロダクション用に複数のアプリがある場合は

heroku pg:reset DATABASE --app <your_app_name>

を実行する。<your_app_name> は Heroku アプリの名前で置き換える。