【ASP.NET Core Web】Windowsで開発したアプリをUnuntuで公開する

ネコニウム研究所

PCを利用したモノづくりに関連する情報や超個人的なナレッジを掲載するブログ

【ASP.NET Core Web】Windowsで開発したアプリをUnuntuで公開する

Windowsで開発したASP.NET Core WebのアプリをUnuntuで公開したい!

概要

今回の記事では、Windowsで開発したASP.NET Core WebのアプリをUnuntuで公開する手順を掲載する。

仕様書

環境

  • Ubuntu 20.04 LTS
  • Apache 2.4.41-4ubuntu3.14
  • .NET 7.0

手順書

開発(Windows)側と公開(Ubuntu)側で分けて説明する。

開発(Windows)側

  1. Program.csに使用するポート番号を設定する。DEBUG時はWindowsでIISサーバーを起動したいのでRELEASE時のみポート番号の設定が行われるようプリプロセッサーで設定する。

    public class Program
    {
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureLogging(logging =>
            {
                ...
            })
    
            .ConfigureWebHostDefaults(webBuilder =>
            {
    #if DEBUG
                // デバッグ時の処理
    #else
                // ここで使用するポート番号を設定する
                webBuilder.UseKestrel(options =>
                {
                    options.Listen(IPAddress.Loopback, 5000);
                });
    
                // UseUrlsでも設定できる
                //webBuilder.UseUrls("http://127.0.0.1:5000");
    #endif
                webBuilder.UseStartup<Startup>();
            });
    }

    UseKestrelUseUrlsを設定した場合はUseKestrelが優先される。
    1.「発行」で「ターゲット ランタイム」を「linux-x64」に設定して発行する。デフォルトだと<ProjectDir>\bin\Release\net7.0\publishに必要なファイルが発行されるのでpublishの中身を全部、WinSCPなどを使って公開サーバーにアップロードする。この記事では例としてvar/www/dotnetappにアップロードしたとして話を進める。

公開(Ubuntu)側

  1. aspnetcore-runtime-7.0が必要となるので無ければインストールする。
    
    wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
    sudo dpkg -i packages-microsoft-prod.deb
    rm packages-microsoft-prod.deb

sudo apt-get install -y apt-transport-https
sudo apt-get install -y aspnetcore-runtime-7.0

公開のみであればSDKは必要ないがSDKも欲しい場合はインストールする。
```bash
sudo apt-get install -y dotnet-sdk-7.0</code></pre>
<ol>
<li>今回はApacheからリバースプロキシで5000番のポートに接続する。Apacheの設定は下記のような感じ。例として<code>app.example.com</code>にアプリを公開する設定。
<pre><code class="language-text">
<VirtualHost *:*>
ServerName app.example.com
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost></code></pre></li>
</ol>
<p><IfModule mod_ssl.c>
<VirtualHost *:443></p>
<p>ProxyPreserveHost On
ProxyPass / <a href="http://127.0.0.1:5000/">http://127.0.0.1:5000/</a>
ProxyPassReverse / <a href="http://127.0.0.1:5000/">http://127.0.0.1:5000/</a>
ServerName app.example.com</p>
<h1>SSLやログの設定</h1>
<p>...</p>
<p></VirtualHost>
</IfModule></p>
<pre><code>1. アプリは<code>var/www/dotnetapp</code>に移動して<code>dotnet dotnetapp.dll</code>で実行できるんだけども、これだと使いにくいのでサービスとして起動できるようにする。
<code>/etc/systemd/system/dotnetapp.service</code>を作って、下記のような感じにする。
```text
[Unit]
Description=dotnetapp version 0.0.1

[Service]
ExecStart=/usr/bin/dotnet /var/www/dotnetapp/dotnetapp.dll
Restart=always
RestartSec=10
SyslogIdentifier=dotnetapp
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
WorkingDirectory=/var/www/dotnetapp

[Install]
WantedBy=multi-user.target
  1. ファイルを保存したら、下記のコマンドでサービスの設定ファイルを読み込み直す。
    sudo systemctl daemon-reload
  2. アプリを起動する。
    sudo systemctl dotnetapp
  3. Apacheを再起動する。
    sudo systemctl restart apache2
  4. ブラウザでapp.example.comにアクセスしてみてアプリの画面が表示されるか確認する。

アプリが起動しない場合

指定したポート番号が既に他のサービスなどで使われてる場合はアプリが起動しない。

下記のコマンドでサービスのエラーログを確認する。

sudo journalctl -fu dotnetapp

ログの内容は下記のような感じ。(長いので一部抜粋)

Unhandled exception. System.IO.IOException: Failed to bind to address http://127.0.0.1:5000: address already in use.
 ---> Microsoft.AspNetCore.Connections.AddressInUseException: Address already in use
 ---> System.Net.Sockets.SocketException (98): Address already in use
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress                                                    socketAddress)

...

at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)

...

指定したポート番号が既に他のサービスなどで使われてるとリバースプロキシに失敗する。
下記のコマンドで5000が何に使われてるか確認できる。

sudo lsof -i:5000

使われないポートを使うようにアプリとApacheの設定を変更する。下記は5001番を使う例。

webBuilder.UseKestrel(options =>
{
    options.Listen(IPAddress.Loopback, 5001);
});
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:5001/
ProxyPassReverse / http://127.0.0.1:5001/

503になった場合

ブラウザでアプリの画面にアクセスしても503になってしまう場合はApacheの設定ファイルが間違ってて、リバースプロキシが失敗してる可能性が高い。

Apacheのエラーログで下記のように出力される。

[<datetime>] [proxy:error] [pid xxxxxxx] (111)Connection refused: AH00957: HTTPS: attempt to connect to 127.0.0.1:5000 (127.0.0.1) failed
[<datetime>] [proxy_http:error] [pid xxxxxxx] [client <ipaddress>] AH01114: HTTP: failed to make connection to backend: 127.0.0.1, referer: https://app.example.com/

下記のようにProxyPassProxyPassReverseの項目でhttpsを設定してる場合


ProxyPreserveHost On
ProxyPass / https://127.0.0.1:5000/
ProxyPassReverse / https://127.0.0.1:5000/
...

アプリ側でSSL証明書の設定が必要になるんだけども、この設定がされてないとリバースプロキシに失敗してる。

Apacheまでが`https`通信してればセキュリティ的には問題ないので下記のように`http`通信になるように設定し直す。

```text
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
...

## まとめ(感想文)

Windowsで作ったアプリがほとんどそのままUbuntuで動くって凄い!