【ASP.NET MVC】LinuxサーバーにWebアプリをデプロイしてみた②【Nginxインストール編】

技術

前回の続き

ということで、前回やったのは Ubuntu 20.04.3 LTS の Docker イメージに .NET 5 のランタイムをインストールして、ASP.NET MVC として作成した OnlineBingoのWebアプリを実行するところまででした。

前回の最後にも少し書きましたが、ASP.NET MVC が動作するKestrelというサーバーはそのまま公開するとセキュリティ的によろしくないらしいです。

詳しくは「ASP.NET Core での Web サーバーの実装」というマイクロソフトのページを読んでみてください。

ASP.NET Core での Web サーバーの実装
ASP.NET Core の Web サーバー Kestrel と HTTP.sys を検出します。 サーバーを選択する方法と、リバース プロキシ サーバーを使用するタイミングについて説明します。

そんなわけで今回はNginxをリバースプロキシサーバーにしてバックエンドでKestrelが動いている環境を作ります。

今回もMicrosoftのページを参考にすすめました。

Nginx 搭載の Linux で ASP.NET Core をホストする
Ubuntu、RHEL と SUSE でリバース プロキシとして Nginx を設定し、Kestrel で実行している ASP.NET Core Web アプリに HTTP トラフィックを転送する方法について学習します。

NGINX をインストール

何はともあれ、まずは Nginx をインストールしないと何も始まりません。

前回の続きでDockerコンテナ上で作業をします。

Nginxはapt-getでインストールできます。
ただし、Ubuntuのデフォルトのリポジトリには少し古いバージョンが登録されているらしく、最新バージョンは NGINX の公式ページで用意されているリポジトリからインストールする必要があります。
2023年8月時点の最新バージョンは「1.29.0」らしいです。

今回は無理に最新バージョンにするつもりはないので、デフォルトのリポジトリからインストールしました。

apt-get install -y nginx

何の問題もなくインストールできました。
バージョンを確認してみます。

nginx -v

nginx version: nginx/1.18.0 (Ubuntu)

バージョンは1.18.0のようです。

では早速起動してみます。

service nginx start

起動できたら http://localhost にアクセスすると Nginx の welcomページが表示されるはずです。

次はNginxをリバースプロキシサーバーとして、Kestrel上で動くOnlineBingoアプリ(ASP.NET MVC) と連動させてみます。

Nginxのサーバー設定は/etc/nginx/sites-available/defaultです。
このファイルを開いてファイルの末尾に下記のように追記します。

server {
  listen 80;
  server_name localhost;
  location / {
    proxy_pass http://localhost:5000;
    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;
  }
}

server_nameに「localhost」と指定してるため、「http://localhost」でアクセスした時のみこの設定が適用されるはずです。

proxy_passでKestrelで動いている ASP.NET にリクエストが転送されるようにしています。
上記の例ではKestrelはポート5000で待ち受けている想定です。

設定を反映させるにはnginxを一旦停止してから再起動させるか

service nginx stop
service nginx start

起動したまま、下記のコマンドで設定を読み込ませればOKです。

nginx -s reload

続いてKestrelで OnlineBingoアプリ を開始します。
前回の続きであれば、/usr/src/publishにOnlineBingo.dllがあるはずです。
前回の手順の通り OnlineBingoアプリを開始してみます。

cd /usr/src/publish
dotnet OnlineBingo.dll

再び http://localhost にアクセスしてみると OnlineBingoアプリ が表示されました。

アプリを監視して自動的に再起動させる仕組み

WindowsのIISと違って、NginxにはASP.NETを監視する仕組みがないらしいです。

今のままでは、もしOnlineBingoアプリが停止してしまった時、誰かが再起動させなければ止まったままです。
それでは不便なので、自動で再起動する仕組みを組み込みます。

Microsoftのページによるとアプリを監視して自動で起動する仕組みをLinuxに組み込むにはsystemdという initシステム を使うらしいです。

/etc/systemd/system/onlinebingo.serviceとファイルを下記の通りに作成します。

[Unit]
Description=Example .NET Web API App running on Ubuntu

[Service]
WorkingDirectory=/usr/src/publish
ExecStart=/root/.dotnet/dotnet /usr/src/publish/OnlineBingo.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target

あとは、systemctlでサービスをstartするだけのはずが・・・。

systemctl enable onlinebingo
systemctl start onlinebingo

失敗しました。

systemctl: command not found

Dockerコンテナ上ではsystemdが使えないんだった。。。

Dockerは1コンテナで1サービスを運用するのが基本なので、複数サービスを管理するためのinitシステム=systemd が必要ないってわけ。

とはいえ、実際はDockerではなくVMWare の仮想マシンで運用する想定なので、systemdを使ったサービス監視は必要な手順だし、Dockerでも試しておきたい。

そんなわけでDockerでsystemdが使えるようにします。

Dockerでsystemdを使いたい

デフォルトで使えないなら、systemdを使えるコンテナイメージを作っちゃえばいいんです。
こちらの 【Docker】Ubuntuコンテナで「systemctl」を使うために「/sbin/init」で起動する方法 という記事がわかりやすくて参考になりました。

【Docker】Ubuntuコンテナで「systemctl」を使うために「/sbin/init」で起動する方法 | インフラエンジニアの技術LOG
DockerでUbuntuコンテナを構築したのですが、DockerはデフォルトでLinux系コンテナに対して制

まず、一度ホストOSに戻って、コンテナを作成するところから始めます。

docker run -it --privileged --name ubuntu_test ubuntu:22.04

そのまま、コンテナに入れているはずなので、コンテナ内で続けます。

まずはaptのupdate。

apt -y update
apt -y upgrade

続けて、initsystemctlのインストール。

apt-get install -y init
apt-get install -y systemctl

※ systemd もインストールしようとしましたが、「systemd is already the newest version (249.11-0ubuntu3.9).」と言われちゃいました。

一旦コンテナを抜けます。

exit

今作業したDockerコンテナをイメージとしてコミットします。

docker commit ubuntu_test ubuntu.systemd

Dockerイメージの名前は何でもいいのですが、私は「ubuntu.systemd」としました。

念のためDockerイメージができているか確認しておきます。

docker images -a

Dockerイメージができたら最後にこのイメージをベースにしてDockerコンテナを作り直します。

docker run -itd --name nginx_test2 --privileged -p 80:80 -v $PWD\src:/usr/src ubuntu.systemd /sbin/init

前回、Dockerコンテナを作った時とよく似たコマンドです。
違いは下記の通り。

  1. コンテナ名が前回とかぶらないように「nginx_test2」としました。
    前回作ったコンテナを削除してから、同じ名前で作り直してももちろんかまいません。
  2. ベースとなるコンテナイメージは「ubuntu.systemd」
    つまり、先ほど作ったDockerイメージを指定します。
  3. 「–privilege」オプションを指定しています。
    コンテナを「特権モード」として動作させるものです。
  4. 起動時のエントリポイントを「/sbin/init」としました。
    これでinitシステムを起動させることになるわけです。

ちなみに、前回作ったコンテナはいったん削除するか停止(docker stop nginx_test)しておかないと 80番ポートのポートフォーワードが被ってしまってうまく動かないので注意してください。

VSCode の Remote Development で アクセスして確認してみます。

systemctl --version

systemd 249 (249.11-0ubuntu3.9)

あとは、前回の手順に従って ASP.NET MVC の Webアプリ を動かせる環境と 今回の前半で説明した Nginx をインストールする手順に従って、再度環境を作り直してみてください。

/etc/systemd/system/onlinebingo.serviceファイルを作成するところまで出来たら、もう一度サービスの開始にトライしてみます。

systemctl enable onlinebingo
systemctl start onlinebingo

今度はうまく動いたようです。
先ほどと同じように http://localhost にアクセスしてみると、ちゃんと OnlineBingoアプリ が表示されました。

https にする

最近のWebアプリではSSL対応が必須ですね。
ということで、OnlineBingoアプリでもSSL対応してhttpsでアクセスできるようにします。

SSLにはサーバー証明書が必要になるわけですが、前回インストールした OpenSSL1.1 を使って証明書を発行します。
いわゆる「自己署名証明書 = オレオレ証明書」ってやつです。
本来は認証局に発行してもらわないと意味がないですが、お試しなので今回は自己署名証明書を使います。

サーバー証明書には秘密鍵 CSR(証明書署名要求) CRT(SSLサーバー証明書)の3つを用意する必要があります。
1つづつ作っていきましょう。

まずは秘密鍵です。

mkdir /etc/nginx/ssl
openssl genrsa -out /etc/nginx/ssl/server.key 2048

openssl genrsaで秘密鍵を作成しています。
出力先は特に決まりがないので、任意のディレクトリにしてください。

次に CSR(証明書署名要求) の作成です。

openssl req -new -key /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.csr

openssl req -newでCSRを作成しています。
先ほど作成した秘密鍵を-keyに指定しています。

CSRの作成は途中でいくつか入力が要求されます。
下記は必須項目なので任意の値を設定してください。
参考までに私は下記のように設定しました。

  • Country Name (2 letter code) [AU]:JP
  • State or Province Name (full name) [Some-State]:Osaka
  • Locality Name (eg, city) []:Osaka
  • Organization Name (eg, company) [Internet Widgits Pty Ltd]:systemcraft
  • Organizational Unit Name (eg, section) []:system
  • Common Name (e.g. server FQDN or YOUR name) []:systemcraft

上記以外は空白でもOKらしいです。

最後に CRT(SSLサーバー証明書) を作成します。

openssl x509 -days 3650 -req -signkey /etc/nginx/ssl/server.key -in /etc/nginx/ssl/server.csr -out /etc/nginx/ssl/server.crt

-signkeyに作成した秘密鍵を、-inに作成したCSRを指定して、openssl x509でCRTを作成します。
-daysで有効期限を指定するみたいです。ここでは10年(3650日)を設定しました。

正常に作成できていたら /etc/nginx/ssl に下記の通り3つのファイルが作成されているはずです。

ls /etc/nginx/ssl/

server.crt server.csr server.key

サーバー証明書が作成できたら、NginxにSSLの設定を入れてみます。

/etc/nginx/sites-available/defaultの末尾に先ほど追加した設定を下記のように編集します。

server {
  listen 80;
  server_name localhost;
  location / {
    proxy_pass http://localhost:5000;
    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;
  }
  # 追加(ここから)
  listen 443 ssl;
  ssl_certificate /etc/nginx/ssl/server.crt;
  ssl_certificate_key /etc/nginx/ssl/server.key;
  # 追加(ここまで)
}

設定を反映させます。

nginx -s reload

これで https://localhost にアクセスできるはずですが・・・、接続できません。

おっと、443ポートをポートフォーワードするのを忘れていました。
Dockerコンテナを作る時に 80番と同時に443番もフォーワードしておかないといけませんでした。

docker run -itd --name nginx_test3 --privileged -p 80:80 -p 443:443 -v $PWD\src:/usr/src ubuntu.systemd /sbin/init

dockerコンテナを作りなおす場合は、すでに作ってあるコンテナを停止(もしくは削除)しないと80番ポートが被っちゃいますから、ご注意を。

コンテナを作りなすのは面倒という場合、もし VS Code の Remote Development 拡張を使ってコンテナにアクセスしているのなら、VS Codeでポートフォーワードを追加することができます。

443番ポートが開いたら、再度 https://localhost にアクセスしてみます。
今度はアクセスできました。

こんな画面が表示されるのは自己署名証明書を使っているからですね。
「localhost に進む(安全ではありません)」といういかにも危なそうなリンクをクリックすれば、OnlineBingoアプリが無事表示されました。

まとめ

前回と今回の二回に渡って .NET 5 の ASP.NET MVC を Linux にデプロイする手順を試してみました。

正直な話、Dockerイメージを使うなら最初からRuntimeやNginxがインストール済みのイメージを pullしてしまえばいろいろと手順をショートカットできると思います。

ただ、今回はあくまでも「Debianに自らの手で環境を作っていく方法を試す」必要があったので、1つ1つセットアップすることになりました。
(「会社で用意できた環境がそれだった」って、ただそれだけのことなんですけどね)

とはいえ、自分の手であれこれと試してみるのはいい経験になるし、いろいろと勉強にもなります。

今回作ったDockerイメージはASP.NET MVCアプリケーションのdllだけ入れ替えれば、使い回しもできるので個人用にDockerHubにプッシュしとこうかなぁ。

そんなわけで、今回はこの辺で。

コメント

タイトルとURLをコピーしました