ASP.NET Core アプリを Linux サーバーで公開(project.json 版)

以下は、project.json プロジェクト形式の ASP.NET Core アプリを Linux サーバーで公開する方法についてのメモです。MsBuild版については。「ASP.NET Core アプリを Linux サーバーで公開」の方にメモしています。

このメモを書くために、次の資料を参考にしました。

公式ドキュメント

Publish to a Linux Production Environment

Scott Hanselman 氏のブログ

Publishing an ASP.NET Core website to a cheap Linux VM host

Step 0 - Web アプリの作成

ASP.NET Core のアプリケーションの開発は、テキストエディターのみでも可能です。特に、Visual Studio Code が公開されたことで、C# を容易に編集できるようになりました。

.NET Core をインストール後に、以下のコマンドで新たな ASP.NET Core のプロジェクトを作成できます。

$ dotnet new -t web

それをテキストエディターで編集して、それが完了したら、以下のコマンドで発行します。

dotnet restore
dotnet run
dotnet publish

規模の大きなアプリケーションでは、デバッグやメンテナンスのことを考えると、Visual Stdio を使うのが便利です。個人開発者であれば、Visual studio community 2015 を無償で利用できます。また、Mac の愛好者には、Visual Studio for Mac が発表され現在プレビュー版が公開されています。Visual Studio for Mac では、Xamarin と ASP.NET Core アプリを同時に開発することができます。

Step 0.1 - Linux サーバー及び SSH クライアントの準備

「さくらの VPS」を使う場合については、低価格 Linux VPS を使うにメモしました。Amazon EC2、Azure VM を利用する場合は、「さくらの VPS」を使うより簡単です。SSH接続の公開鍵認証については、事前に公開鍵を登録すればデプロイ時に自動で設定してくれるし、ファイアーウォールについてはコントロールパネルで設定できます。

Step 1 .NET Core のインストール

.NET Core のインストールは、以下の公式ページを参考にします。

.NET Core installation guide

Ubuntu 16.04 の場合も、リポジトリーを登録するだけなので、インストールは簡単で、次の4行でできてしまいます。

sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
sudo apt-get update

どのバージョンをインストールしたいかによってコマンドが変わります。

最新版((.NET Core 1.1))をインストールしたい場合は

sudo apt-get install dotnet-dev-1.0.0-preview2.1-003177

LTS版(.NET Core 1.0.3)をインストールしたい場合は

sudo apt-get install dotnet-dev-1.0.0-preview2-003156

MSBuild版(VS 2017 RC4 に含まれるもの)をインストールしたい場合は

sudo apt-get install dotnet-dev-1.0.0-rc4-004771

これらのバージョンは複数インストールしても問題はなく、global.json ファイルがあるかどうかによってMSBuild版かどうかを判断し、従来のものは global.json に記載しているバージョンによって、どのバージョンのものが使われるか判断されます。(参考: Scott Hanselman 「Working with Multiple .NET Core SDKs - both project.json and msbuild/csproj」

Ubuntu 14.04 の場合は、2行目で xenial を trusty に変更するだけです。Xenial は Ubuntu 16.04 のコードネームで、Trusty は Ubuntu 14.04 のコードネームです。

Step 2 アプリの発行

Ubuntu サーバー用のアプリの発行は、Windows PC でも、Ubuntu サーバーのどちらでも可能です。また、発行の方法として、Portable Apps にする方法と Self-contained Apps にする方法の2種類があります。既定では Portable Apps になっていて、その場合は、総てのプラットフォームで共通のバイナリーです。公開環境で使う場合は以下のコマンドで発行します。

dotnet publish --configuration Release

Self-contained Apps のことについては、最後の補足に書きました。

発行する場合に注意をしておかないといけないのは、発行したいファイルを以下のように project.json に記述する必要があります。

"publishOptions": {
  "include": [
    "wwwroot",
    "Views",
    "appsettings.json",
    "web.config"
  ]
}

Step 3 - Kestrel サーバーで公開

発行後は、アプリケーションのdll ファイルをdotnet app.dll のように直接指定して実行させます。

また、Linux サーバーで使う場合に少し注意をしておかないといけないのは、コンソールアプリケーションと違ってWebアプリケーションでは、起動するときにルートディレクトリをカレントディレクトリにしておく必要があることです。Program.cs ファイルを見ると分かるのですが、UseContentRoot(Directory.GetCurrentDirectory())と指定されています。

public static void Main(string[] args)
{
  var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

  host.Run();
}

Step 4 - Nginx でリバースプロキシサーバーをたてる

ASP.NET Core は、Kestrel サーバーで動作しますが、Kestrel は単機能のWebサーバーなので、公開時にはWindows サーバーであれば IIS を使いますが、Linux では Nginx をリバースプロキシサーバーにして使用します。

Nginx をインストールする一番簡単な方法は、sudo apt-get install nginx ですが、かなり古いバージョンがインストールされます。

新しいバージョンを使いたい場合には、Nginx が提供しているパッケージを使用します。自分の場合は HTTP/2 を使ってみたかったので、最新のMainline バージョンをインストールすることにしました。

Mainline バージョンを使用する場合は、以下のようにリポジトリを追加することで、Ubuntu のパッケージを利用するのと同じ sudo apt-get install nginx でインストールできるようになります。詳しい説明は、Nginx の公式ドキュメントを見てください。

curl http://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo sh -c "echo 'deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx' >> /etc/apt/sources.list"
sudo sh -c "echo 'deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx' >> /etc/apt/sources.list"

ASP.NET Core アプリケーションへのリバースプロキシの設定は基本的には、ASP.NET Core の公式ドキュメントのとおりにしています。ただし、クライアントの IP アドレスを取得したいので、proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; を追加しています。

creativeweb.conf

  server {
  listen 80;
  server_name creativeweb.jp;
  location / {
  proxy_pass http://localhost:5001;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection keep-alive;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
  }
}

ASP.NET Core のポート番号を変更したい場合は、以下のように Main 関数の WebHostBuilder で、UseUrls を使って変更します。

Program.cs

public static void Main(string[] args)
{
  var host = new WebHostBuilder()
  .UseKestrel()
  .UseContentRoot(Directory.GetCurrentDirectory())
  .UseIISIntegration()
  .UseStartup<Startup>()
  .UseUrls("http://127.0.0.1:5001")
  .Build();

  host.Run();
}

そこで、アドレスを localhost ではなくて、127.0.0.1 と指定しているのは、Bash on Ubuntu on Windows で使用するためです(参照)。

nginx の停止と起動は以下のように行います。

sudo service nginx stop
sudo service nginx start

nginx の設定を変更したときには、以下のコマンドで設定の検証をして

sudo nginx -t

結果がOKであれば、以下のコマンドで設定を読み込ませます。そうすれば、nginx を再起動する必要はありません。

sudo nginx -s reload

Nginx に関しては、サーバ証明書をインストールして、HTTPSを設定しました。また、HTTP/2 も有効にしています。これらのことを書いていると長くなるので別の機会に書きたいと思います。

Step 5 - Supervisor でデーモン化してモニタリングする

IIS と違って Nginx には、Kestrel を管理する機能がありません。そのため、安定した運用していくためには、エラーでサーバーが停止したときに自動的に起動するような仕組みが必要になります。そのためのツールはいろいろあるようですが、今回は、マニュアルのとおり supervisor を利用しました。

supervisor のインストールは、普通に sudo apt-get install supervisor でおこないました。また、設定ファイルは、以下のようにマニュアルを参考にして設定しています。

/etc/supervisor/conf.d/ecitzen.conf

[program:ecitizen]
directory=/var/aspnet/ecitizen
command=dotnet /var/aspnet/ecitizen/ecitizen.dll
autostart=true
autorestart=true
stderr_logfile=/var/log/ecitizen.err.log
stdout_logfile=/var/log/ecitizen.out.log
environment=Hosting__Environment=Production
user=www-data
stopsignal=INT

supervisor の停止と起動は以下のように行います。

sudo service supervisor stop
sudo service supervisor start

特定のサービスのみを停止、起動したい場合も多いと思いますが、その場合は、上の例だと以下のコマンドでできます。

sudo supervisorctl stop ecitizen
sudo supervisorctl start ecitizen

supervisorctl には他にも便利なコマンドがあります。'supervisorctl help' でコマンドの一覧が表示され supervisorctl help <topic> で各コマンドの説明が表示されます。

補足 - Self-contained Apps を使う

Step2 で説明したように .NET Core アプリには、Portable Apps と Self-contained Apps の2種類があり、Self-contained Apps の方は .NET Core ランタイムをアプリ内に含むため独立した環境で動作させることができます。

Vusial Studio の新規作成でプロジェクトを作成すると Portable Apps になります。Self-contained Apps に変更数するのは簡単で、コンソールアプリケーションであれば、初期状態では project.json は次のようになっています。

project.json

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.1"
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

それを、次のように、"type": "platform" を消して、その代わりに runtimes を定義します。

project.json

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.1"
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  },

  "runtimes": {
    "win7-x64": {},
    "win10-x64": {},
    "ubuntu.16.04-x64": {},
    "centos.7-x64": {},
    "osx.10.10-x64": {}
  }
}

Self-contained Apps の場合は、実行ファイルが生成されるので、そのまま実行することができますが OS によって実行ファイルが異なります。.NET Core は、マルチラットフォーム対応なので、例えば Windows PC で Ubuntu 用の Self-contained Apps を作成することができます。

現状では,Self-contained Apps は、native コンパイルではないので、ファイル数が多く容量も最低でも50MBを超えます。しかし、単体で動作するので、AWS Lambda でも動かすことが可能です。