CentOS7で始めるASP.NET Core 2.x

# Linuxで始めるASP.NET Core!!

いつのまにやら、dotnet core 2.2 がリリースされてしまいました。勉強しながらどのようにすればいいかというのを調査していると、あっという間にバージョンアップされて、どんどん離されていきます。恐ろしい世界です...

さて、ASP.NET Coreを始めた理由として、PHP捨てたい！という点が一つでしたが、結局の所、言語を捨てられてもその環境で使えなければ意味がありません。そして、PHP捨てたいのはやまやまですが､レンタルサーバーでASP.NET Coreを動かすという願いは、ほぼ叶えられません。

だからといって、いきなりAWSやAzure に手を出すには勇気が足りません。学習コストがかかりすぎます。

そこで、まずはVPSを使ってASP.NET Coreを運用してみようかと思います。その次ですね、クラウドサービスの利用は...。

# CentOSVPSでASP.NET Coreの環境を構築

Ubuntuとかありますけど、猫は仕事柄RHELやCentosを利用することが多いですので、今回は、Centos＋VPSで構築します。Ubuntuにしなかった理由はその点で、そもそもデストリビューション変わるだけでも学習コストがかなり増え、ストレスマックスになります。ただでさえ、短い時間でいろいろ学習することがあるので、ある程度は知っている環境で問題点などの洗い出しをするのがベストだと判断しました。なので、早くWSL(Windows Subsytem for Linux)にも、Fedora系来てください...。さて、ここはテストに、Dellでー（ぇ

ということで、適当にVPSを借りて、Centos7を選択して放り込みまで行いました。

* Kagoya Open VZ VPS 3Core CPU/Memory 1GB:2GB HDD 200GB 月:864円
* Centos7.6 x64
* Nginx mainline(独自コンパイル)
* Letsencrypt(certbot)
* vsftpd
* PHP7.3
* MariaDB 10.3
* dotnet core 2.2

ASP.NET Core を本運用しているようなところが、どのようなデプロイ方法を利用しているのか、凄く興味があります。構築していたときに気になったのが、デプロイをどうすればいいか？という課題でした。
今回は、Dockerを使っていません。というより、メモリ容量の少ないプランにDockerなんて使えるもんじゃありません。コンテナ仮想化するだけでも、相当のメモリを消費されるので、今回の場合は直に実装する方が安心できます。

# dotnet のインストール

契約したときに dotnet core 2.2 がちょうど出ましたので、こちらをインストールしました。
ここでは、開発を行わず、運用面のみを考えているので、ランタイムだけのインストールにしました。

```bash
rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
yum update
yum install aspnetcore-runtime-2.2
```

気になるのは、複数のランタイムを入れたときにどのように切り替えて実行するのでしょうか？今回はdotnet core 2.2でコンパイルし直しているので、バイナリが2.2用しかないのでいいのですが、2.2や3.0とごっちゃになる時の設定が気になりますが、とりあえずここでは考えないようにしておきます。

# dotnet をサービス化

ASP.NET Core は、Webサーバーとして立ち上がるので、直接立ち上げてHTTPサーバーとして利用することも出来るのですが、そうするとドメイン切り分けや個々のアプリとして提供することも踏まえ、今回はNginxからリバースプロキシ経由で通信を行わせることにしました。
毎回、サーバー再起動がかかるたびに手動で実行するのも手間なので、サービス化します。

`vi /etc/systemd/system/hogehoge.service` というファイルを作り、中身にサービス化するポイントを記述します

```ini
[Unit]
Description=hogehoge web application

[Service]
WorkingDirectory=/var/web/hogehoge/app/
ExecStart=/usr/bin/dotnet /var/web/hogehoge/app/hogehoge.jp.dll --urls=http://localhost:9000/
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=hogehoge_aspnet
User=hogehoge
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target
```

基本的に修正する部分は、

* Description=[概要]
* WorkingDirectory=[アプリ設置場所のディレクトリ]
* ExecStart=/usr/bin/dotnet [アプリの絶対パス.dll] [オプション]
* SyslogIdentifier=[わかりやすく適当]
* User=[useraddで作って、共有でもいいけど]

作成したり、修正したら `systemctl daemon-reload` を行います。これを結構忘れる。

# nginx で プロキシ設定

最近、TLSv1.3がリリースされたのですが、そのままNginxのリポジトリからyumでインストールしても、TLSv1.3が対応されませんでした。というのも、OpenSSL1.1.1が必要なようですが、yumのリポジトリにはそれが入っていないから。ということで、苦戦しながらソースコードから入れてコンパイルして、なんとか環境を用意できました。まぁ、今回は関係ないんですけどね。あと、猫はApache嫌いだったので、Nginxの設定しやすさは神ですね。CGI環境作ろうとするとApacheより死にますが（PHP-FPMを除く）。

以下のように、`location / { ... }` に、サービス化した時の --urls=`http://localhost:9000/` を入れてあげればいいだけです。

```nginx
location / {
    proxy_request_buffering off;
    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 Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    break;
}
``` 

一応、Nginxとdotnet core 2.2 のライブラリだけでもあれば、一応公開までは行けると思います。

# デプロイに悩む

さて、今回はdockerもGitによるデプロイツールも使わずに、ftpで転送してのやりとりをしています。GitLabを猫は愛用しているので、勉強がてらCI/DIやデプロイ関係もやってみたいのですが、そこまで手が回っていない状況なので、取りあえず、Visual Studio 2017の「発行」を行うことで、FTP経由で転送してデプロイ！というのをやってみました。

そこで問題になった点は

> サービスの再起動をどうしよう...

　Windowsでは、利用しているアプリのファイルを上書きできません。そういう仕組みです。ところが、Linuxでは動作しているアプリのファイルを上書きすることが出来ます。基本、プログラムというのはメモリで動作します。プロセスを立ち上げたときに、すべてプログラムはメモリに乗りますから、ファイルを上書きしても見に行かなければなんてことはないわけです。単に、LinuxとWindowsのプロセス管理の違いからの要因で、このような結果になってるわけ（と思われる？）です。
　つまり、サービスが動いていてもLinuxでは新しいアプリをサーバーにデプロイできますが、ファイルが変わったからといって参照（更新）してくれるわけではないので、結果が変わりません。新しいアプリの機能を反映させるには、サービス自身を再起動しなければなりません（アプリの再起動）。そのコマンドは `systemctl restart hogehoge` なのです。
　そして、この `systemctl restart hogehoge` は、特権権限が必要です。でも、VS2017の発行ではそれを連携して行うことが出来ません...。さて、どうしたものか。

# 他に良い方法があれば教えてください（ぁ

インストールした環境に、`PHP7.3` というのがあります。今回、このサーバーでPHPを使う目的はあまりなかったので入れないつもりでしたが、ちょっと工夫をしてこいつに噛ませることにしました。
ちなみに、本格使用はしないけど、わざわざ使わないのに PHP7.3 という最新版を入れてみたのは、なんとなくです。

```bash
yum -y install epel-release
cd /etc/yum.repos.d/
curl http://rpms.famillecollet.com/enterprise/remi.repo > remi.repo
```

まだ、PHP7.3出たばかりで remi.repo にPHP7.3が明記されていなかったので、7.0 のURLを7.3に変更してインストールさせてます。

```bash
yum -y install php php-fpm php-mbstring
```

必要に応じてライブラリを...
php-opcacheをインストールすると、初期設定ではメモリをめちゃ喰ったので（128MBぐらい）、設定変更で8M程度にしました。それならば、入れなくてもいいんじゃないかな？と思ってしまった。

今回、PHPを入れたのは、PHPを通してサービスを再起動させようということでした。とあるURLにアクセスすると、サービスを再起動するようにした、ということですね。ただ、上にもあるように、特権権限が必要です。本来ならば、PHPに特権で動かすように設定するのは鬼門ですが、今のところ方法を思いつかなかったので、これにしてみました。

ちなみに、シェルプログラムを動かすPHPは

```php
shell_exec('systemctl restart hogehoge');
```

とすればできます。
続いてPHP-FPMを作ります
PHP-FPM、複数作ることが出来るので（上のインストールではそのような設定になってる） `/etc/php-fpm.d/` の中身にサービス再起動コマンド専用のPHP-FPMを作って、それをさせます。

```bash
cd /etc/php-fpm.d/
cp www.conf cmd.conf
mv www.conf www.conf.back
```

[cmd.conf]

```ini
user = root
group = nginx
listen = /var/run/php-fpm.cmd.sock
listen.owner = root
listen.group = nginx
listen.mode = 0660
php_admin_value[error_log] = /var/log/php-fpm/cmd.error.log
php_admin_value[memory_limit] = 2M
```

ここでルート設定を行っていくのですが、1点だけ。php-fpmをここで `system start php-fpm` としても立ち上がりません。既定ではphp-fpmは、rootでの実行を禁止しているようで、実行オプションをつけないとrootで立ち上がらない設定になっています。なので、php-fpmのサービス起動もいじる必要があります。

`vi /usr/lib/systemd/system/php-fpm.service` を開き

```bash
ExecStart=/usr/sbin/php-fpm --nodaemonize -R
```

と `-R` オプションを添えます。

```bash
systemctl daemon-reload
systemctl enable php-fpm
systemctl start php-fpm
```

で、登録と起動を行います。

NginxのWebサーバー設定に

```nginx
location = /hogehoge/restart {
    fastcgi_pass unix:/var/run/php-fpm.cmd.sock;
    fastcgi_param SCRIPT_FILENAME [絶対パスでPHPプログラムを指定];
    include fastcgi_params;
    break;
}
```

として、リスタートコマンドを実行させます。なお、猫は直接PHPプログラムを指定しました。一応認証などを取り入れ、簡単に再起動できないようにしてます（VS2017の発行のURLから指定できるよう、GETパラメータで認証できるようにしています）。これで、コンパイル->転送->再起動というデプロイ動作ができました。

今のところ、この方法がいいとは思ってないので、何か良い方法があれば！ご教授ください。

* Linuxでは、Windowsのサービスと同等の機能を「デーモン(daemon)」と言いますが、面倒なので「サービス」で統一してます。