ASP.NET Core をUbuntu, Nginx環境で動かす

2020-08-23 | コメント(0)

ASP.NET Core アプリケーションをデーモン化して、Ubuntu, Nginxリバースプロキシで動かす方法です。

ASP.NET Core の登場で、.NETのWebアプリケーションがWindows,IIS以外にLinux上でも動かせるようになりました。VPS等のWebホスティング料金も、WindowsよりLinuxのほうが値段が安い傾向があるので嬉しい限りです。

以下、Ubuntu 18.04とNginxで ASP.NET Core アプリケーションを動かすまでの設定です。
ASP.NET Core はVersion3.1です。

当エントリーの部分的な内容は、Microsoft Docsでも説明されています。
Ubuntu に .NET Core SDK または .NET Core ランタイムをインストールする
Nginx 搭載の Linux で ASP.NET Core をホストする
リンク先ではUbuntu 16.04の説明内容だったので、動作優先として実際にUbuntu 18.04で稼働確認をしながら私なりに理解し、後々VPS等でサービスをスタートする場合に備えるためと応用のために纏めています。

ASP.NET Core のバージョン確認は、dotnet --versionで確認できます。

dotnet --version

インストールされていなければ、次の様なメッセージが表示されます。

Command 'dotnet' not found, but can be installed with:

ASP.NET Core のインストールには3つのパターンがあります。

  • .NET Core SDK
  • .NET Core ランタイム
  • ASP.NET Core サポートを含まない .NET Core ランタイム

このうちインストールするものは、.NET Core SDKをインストールします。ASP.NET Core のランタイムも含まれますし、dotnetの各種コマンドを実行することが必要になる場合もあるかもしれないためです。

まずリポジトリの更新をしますが、packages-microsoft-prod.deb をダウンロードしてくるため作業用にtempフォルダで行います。

mkdir temp
cd temp

リポジトリ更新

wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

リポジトリの更新が済んだら、ダウンロードした packages-microsoft-prod.deb は削除して構いません。

rm packages-microsoft-prod.deb

apt update & upgrade をしておきます。

sudo apt update
sudo apt upgrade -y

apt-transport-httpsのインストール ※これは ASP.NET Core ではありませんが必要なパッケージ

sudo apt install apt-transport-https -y

念のため、再度update

sudo apt update

.NET Core SDK 3.1 インストール

sudo apt install dotnet-sdk-3.1 -y

これで、ASP.NET Core が動く環境になります。

バージョンを確認すると、3.1.401 となりました。

$ dotnet --version
3.1.401

以前、3.1.1あたりの時にインストールしたものが、apt updateにより、3.1.2, 3.1.3 とバージョンがアップしたことを確認しているので、3.1バージョンであればアップデートで最新になるものだと思います。今後メジャーバージョンが上がったものはどうなるか分かりませんが、このようなパッケージはおそらく常に最新が保たれると思います。

ASP.NET Core のアプリケーションを用意します。

当エントリーの説明のために、Visual Studio 2019 で「ASP.NET Core Webアプリケーション」C#, MVCプロジェクトテンプレートのアプリケーションをサンプルとします。

ソリューション / アプリケーション名は「SampleMvc」としています。

Nginxがホストする特定URLのパスとして稼働させたいため、アプリケーション側で UsePathBase で仮想フォルダで動く様に Startup.cs を少し修正します。
また HSTSの設定と、httpsリダイレクションも一旦無効にします。

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace SampleMvc
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                //app.UseHsts();
            }
            //app.UseHttpsRedirection();

            string pathBase = Configuration.GetValue<string>("PathBase");
            if (!string.IsNullOrEmpty(pathBase))
            {
                app.UsePathBase(pathBase);
            }
            
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

40,42 行目、コメント化。
44-48 行目、UsePathBaseが使用出来るように追加。

app.UsePathBase(..); は、アプリケーションが実行される仮想パスの指定になります。
例えば、app.UsePathBase("/app1"); とすれば、http://mydomain/app1/ がアプリケーションのルートになります。

汎用的に appsettings.json に値を持たせて、指定値があれば UsePathBase に渡るようにしています。

具体的なテスト例として appsettings.json は次の様に "/app1" にしておきます。
appsettings.Development.json のほうも同じ指定にすれば、開発時のデバッグ実行でも https://localhost:[port番号]/app1/ としてWebアプリケーションが動作します。

{
  "PathBase": "/app1",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

この状態で SampleMvc アプリケーションを発行します。

※Properties配下のlaunchSettings.jsonは何も変更しません。

UbuntuのNginxには、発行した ASP.NET Core アプリケーションのファイルをzipファイルで持っていきunzipで解凍することとして、発行先はフォルダー指定(場所は任意)にします。
ターゲットフレームワークは「netcoreapp3.1」を指定し
配置モードは「フレームワーク依存」にします。

Ubuntu側でzip解凍時にアプリケーション名のフォルダとして解凍されるようにしたいので、発行先のフォルダーをアプリケーション名の SampleMvc にするか、発行されたファイルを SampleMvc というフォルダーに纏めて、SampleMvc というフォルダーをzipファイルに纏めます。

これで ASP.NET Core アプリケーションのファイルは準備できたので、Ubuntu側でこのzipファイルを取得します。

※他のWebサイトに置いてwgetで取得しました。
wget http://172.24.0.31/temp/SampleMvc.zip

次に、アプリケーションを配置する場所を決めてファイルをコピーします。
配置する場所は任意ですが、Ubuntu上で ASP.NET Core アプリケーションは Kestrel のWebサーバーで動きますので、/var/www の下に、kestrel というフォルダを作ってそこに配置することにします。
※ /var/www/ はNginxをインストールしていれば存在しているはずです。今後、ASP.NET Core のアプリケーションはこのフォルダに纏めていくつもりのフォルダになります。

sudo mkdir /var/www/kestrel

zipファイルをコピー。

sudo cp SampleMvc.zip /var/www/kestrel/SampleMvc.zip

カレントを移動しunzipします。

cd /var/www/kestrel/
sudo unzip SampleMvc.zip

/var/www/kestrel/SampleMvc/ フォルダに解凍され各ファイルが展開されます。

カレントを移動しましょう。

cd /var/www/kestrel/SampleMvc

/SampleMvc/ には、アプリケーション実体の SampleMvc.dll も含まれています。ASP.NET Core のランタイムはインストール済みなので "dotnet [アプリケーションのdll]" で実行できますので実行してみましょう。

/var/www/kestrel/SampleMvc$ dotnet SampleMvc.dll
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /var/www/kestrel/SampleMvc

おそらくこの様に表示されて待機状態になるはずです。終了するには ctrl + c で終了出来ます。

実行時に "listening on: http://localhost:5000" と表示されている通り localhost:5000 で動いています。外部からUbuntuのIPアドレス指定で http://172.24.0.78:5000/app1/ を表示しようとしても表示は出来ません。これは、Kestrel がホストしているのは、localhost (=127.0.0.1) でリッスンしているため、物理IPアドレスではリッスンされていないためです。

外部からもアクセス可能にさせるには、appsettings.jsonでurlsを指定することで試すことが出来ます。urlsは待機ホストとポート番号を指定しますが、ホスト部分を物理IPアドレスにするか 0.0.0.0 にすることで外部へ公開させることが出来ます。

sudo nano /var/www/kestrel/SampleMvc/appsettings.json
{
 "urls": "http://172.24.0.78:5000",
 "PathBase": "/app1",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"

appsettings.jsonを保存して、再度アプリケーションを実行すると http://172.24.0.78:5000/app1/ で ASP.NET Core MVC のWeb画面が確認出来るはずです。

動作確認のため外部からアクセスできるようにurlsを物理IPアドレスにしましたが、Kestrelを外部へ直接公開するのは推奨されていないので、urlsのホスト部分はlocalhostにしておきましょう。

 "urls": "http://localhost:5000",

ASP.NET Core アプリケーションをデーモン化する前に、Nginxのリバースプロキシの設定をしておきます。(順番はどちらでも構いませんが)

Nginxの設定に、下記 52 - 59 行目を追加します。

sudo nano /etc/nginx/sites-enabled/default
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        #server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}

        location /app1/ {
            proxy_pass http://localhost:5000/app1/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection keep-alive;
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
}

/app1/ のパスに対してのlocation設定です。

/app1/ のリクエストは、proxy_passで指定されるアドレス http://localhost:5000/app1/ に転送されます。

保存したら、Nginxを再起動します。

sudo service nginx restart

これで、http://172.24.0.78/app1/ で ASP.NET Core MVC の画面が表示出来るようになります。

もし画面が出ない場合は、Nginxのproxy_passと、アプリケーション側のappsettings.jsonで指定したurlsとPathBaseが合っているか確認してみてください。Kestrelの外部アクセス確認をした場合はlocalhostに戻し忘れていないかなど注意してください。

SampleMvcアプリケーションを都度dotnetコマンドで実行するのは現実的ではないのでデーモン化します。Ubuntuが開始された時に自動的に実行されるようになります。

systemctlで行いますが、アプリケーション用の定義ファイルを新規に作成します。例として kestrel-app1.service という名前にしましょう。/etc/systemd/system/ の中に作成します。

sudo nano /etc/systemd/system/kestrel-app1.service

ファイルの内容は以下の通りです。

[Unit]
Description=App1 ASP.NET Core App running

[Service]
WorkingDirectory=/var/www/kestrel/SampleMvc
ExecStart=/usr/bin/dotnet /var/www/kestrel/SampleMvc/SampleMvc.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target

ExecStartのパス指定が重要ですが特に難しいものではありません。/usr/bin/dotnet が .NET Core の実行ファイルですね。
ファイルを保存したらsystemctlに登録します。

sudo systemctl enable kestrel-app1.service

systemctlに登録したら、start/stopで起動/停止が出来るようになります。

起動

sudo systemctl start kestrel-app1.service

停止

sudo systemctl stop kestrel-app1.service

デーモンをstartしたらUbuntuをリブートしても、自動的に ASP.NET Core のアプリケーションが起動されて、kestrelも自動的にホストされることが確認出来ます。

sudo reboot

デーモンで動作中は、dotnetコマンドでのアプリケーション実行は実行時のポートがバッティングしてエラーするので、dotnetコマンドで手動で実行する場合はデーモンはstopしておいてください。この逆にdotnetコマンドで動作中はデーモンの起動が失敗します。

ASP.NET Core のWebアプリケーションはKestrelでホストされるので、ポート番号を変えてNginx側で指定するパスに合わせることで複数同時に動かすことができます。

localhost:5001/app1
localhost:5002/app2
localhost:5003/app3
localhost:5004/app4
localhost:5005/app5
....の様に。

数十個とか動かす場合はメモリ使用量など気にすることもあると思いますが、2-3個程度の起動なら特別気にすることも無いと思います。

セキュリティ的な面など細かい部分は Microsoft Docs のサイトを参考にしますが、必要と思われる要素は当エントリーを修正していきたいと思っています。

カテゴリ:

コメントする

※HTMLタグは使えません

Author

あきちゃん

主に、.NETでWebシステムの設計と開発をしています。
(茨城県在住, 都内勤務)
プロフィール