CentOS 7.2上に.NET Core 1.1をインストールして、Visual StudioでビルドしたASP.NET Core Webアプリをデプロイしてサービス化する

# やること

.NET Core 1.1をCentOS上にインストールして、Visual Studio 2015で作ったASP.NET Core Webアプリケーションを配置して実行します。

# 対象とする読者

主に、インフラにお金をかけない小規模なシステム開発で、Linux + Javaでやっているけど、本当はC#が使いたいような人を想定しています。


よって、本記事では以下のあたりがポイントになっています。

* Dockerは使わず.NET CoreはCentOSに直接インストールしたものを使用する
* 対象がCentOSなのは、なんだかんだでRed Hat系ディストリビューションを使っている人が多いことを想定して
* 開発自体はあくまでWindows上でVisual Studioで行う
* ついでに、よくある例のようにLinux上でdotnet restoreするのではなく、Visual Studioで作った成果物を実行環境へデプロイする形にする
* とりあえず簡単にLinux環境を対象としてASP.NET開発を感じられるように、nginxとの連携などはせず、.NET単体でWebアプリを実行し、サービス化までを行う

# 前提条件

## 開発環境

* Visual Strudio 2015 Update 3
* .NET Core 1.1.0 Runtime (dotnet-win-x64.1.1.0.exe)
* .NET Core 1.0.1 tools Preview 2 (DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe)

## 動作環境

* CentOS 7.2 (CentOS-7-x86_64-DVD-1511.isoからインストールしたもの)
* OSインストール後、yum updateしたところまで

# .NET Core 1.1インストール

CentOS上の実行環境構築手順について記述します。
なお、CentOS以外の手順については[.NET Core installation guide](https://www.microsoft.com/net/core#linuxcentos)を参照してください。

## 必要ライブラリインストール

```console
[root@centos ~]# yum install -y libunwind libicu
読み込んだプラグイン:fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ libicu.x86_64 0:50.1.2-15.el7 を インストール
---> パッケージ libunwind.x86_64 2:1.1-5.el7_2.2 を インストール
--> 依存性解決を終了しました。

依存性を解決しました

======================================================================================================================================================================================================
 Package                                        アーキテクチャー                            バージョン                                             リポジトリー                                  容量
======================================================================================================================================================================================================
インストール中:
 libicu                                         x86_64                                      50.1.2-15.el7                                          base                                         6.9 M
 libunwind                                      x86_64                                      2:1.1-5.el7_2.2                                        updates                                       56 k

トランザクションの要約
======================================================================================================================================================================================================
インストール  2 パッケージ

総ダウンロード容量: 6.9 M
インストール容量: 24 M
Downloading packages:
(1/2): libunwind-1.1-5.el7_2.2.x86_64.rpm                                                                                                                                      |  56 kB  00:00:00
(2/2): libicu-50.1.2-15.el7.x86_64.rpm                                                                                                                                         | 6.9 MB  00:00:03
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
合計                                                                                                                                                                  1.9 MB/s | 6.9 MB  00:00:03
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : 2:libunwind-1.1-5.el7_2.2.x86_64                                                                                                                                      1/2
  インストール中          : libicu-50.1.2-15.el7.x86_64                                                                                                                                           2/2
  検証中                  : libicu-50.1.2-15.el7.x86_64                                                                                                                                           1/2
  検証中                  : 2:libunwind-1.1-5.el7_2.2.x86_64                                                                                                                                      2/2

インストール:
  libicu.x86_64 0:50.1.2-15.el7                                                                    libunwind.x86_64 2:1.1-5.el7_2.2

完了しました!
```

## ダウンロード及び展開

```console
[root@centos ~]# curl -sSL -o dotnet.tar.gz https://go.microsoft.com/fwlink/?LinkID=835019
[root@centos ~]# mkdir -p /opt/dotnet && sudo tar zxf dotnet.tar.gz -C /opt/dotnet
[root@centos ~]# ln -s /opt/dotnet/dotnet /usr/local/bin
```

## 動作確認

```console
[root@centos ~]# dotnet

Microsoft .NET Core Shared Framework Host

  Version  : 1.1.0
  Build    : 928f77c4bc3f49d892459992fb6e1d5542cb5e86

Usage: dotnet [common-options] [[options] path-to-application]

Common Options:
  --help                           Display .NET Core Shared Framework Host help.
  --version                        Display .NET Core Shared Framework Host version.

Options:
  --fx-version <version>           Version of the installed Shared Framework to use to run the application.
  --additionalprobingpath <path>   Path containing probing policy and assemblies to probe for.

Path to Application:
  The path to a .NET Core managed application, dll or exe file to execute.

If you are debugging the Shared Framework Host, set 'COREHOST_TRACE' to '1' in your environment.

To get started on developing applications for .NET Core, install the SDK from:
  http://go.microsoft.com/fwlink/?LinkID=798306&clcid=0x409
[root@centos ~]# dotnet --info
.NET Command Line Tools (1.0.0-preview2-1-003177)

Product Information:
 Version:            1.0.0-preview2-1-003177
 Commit SHA-1 hash:  a2df9c2576

Runtime Environment:
 OS Name:     centos
 OS Version:  7
 OS Platform: Linux
 RID:         centos.7-x64
```

## Firewall設定

後で作成するWebアプリケーションに対して、接続するためにFirewallを設定しておきます。

```console
[root@centos ~]# firewall-cmd --zone=public --add-service=http --permanent
success
[root@centos ~]# firewall-cmd --reload
success
```

# Webアプリケーション作成

## プロジェクト作成

Visual Studioで「ASP.NET Core Web Application (.NET Core)」を作成します。

## 1.1へ更新

Visual Studioで作成されるひな形は1.0用の構成であり、CentOSには1.1をインストールしているので、プロジェクトを1.1用に更新します。

まずはNuGetですべてのパッケージを更新します。

次に、project.jsonの次の箇所を更新します。

* 変更前

```js:project.json
  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },
```

* 変更後

```json:project.json
  "frameworks": {
    "netcoreapp1.1": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },

```

また、次の箇所も変更します。

* 変更前

```json:project.json
    "Microsoft.NETCore.App": "1.1.0",
```

* 変更後

```json:project.json
    "Microsoft.NETCore.App": {
      "version": "1.1.0",
      "type": "platform"
    },
```

project.json全体は以下のような内容になります。

```json:project.json
{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.1.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Diagnostics": "1.1.0",
    "Microsoft.AspNetCore.Mvc": "1.1.0",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    },
    "Microsoft.AspNetCore.Routing": "1.1.0",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
    "Microsoft.AspNetCore.StaticFiles": "1.1.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
    "Microsoft.Extensions.Configuration.Json": "1.1.0",
    "Microsoft.Extensions.Logging": "1.1.0",
    "Microsoft.Extensions.Logging.Console": "1.1.0",
    "Microsoft.Extensions.Logging.Debug": "1.1.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.1.0",
  },

  "tools": {
    "BundlerMinifier.Core": "2.2.306",
    "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
  },

  "frameworks": {
    "netcoreapp1.1": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "**/*.cshtml",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "prepublish": [ "bower install", "dotnet bundle" ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}
```

## URL設定

デフォルトではlocalhostからのアクセスのみ許可されるので、外部からも接続できるようにUseUrls()の記述を追加します。

```csharp:Program.cs
public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseUrls("http://*:80/")    // ここを追加
        .UseStartup<Startup>()
        .Build();

    host.Run();
}
```

なお、この時点でVisual Studio上でもデバッグ動作できることを確認しておきます。

デバッグ動作の確認ができない場合、なにか作業が不足しています。

## デプロイ

Visual Studioの[ビルド]メニューからアプリケーションの発行を実施し、デプロイ用のファイルを作成します。

発行方法は「ファイルシステム」で、ターゲットの場所はデフォルトだと「.\bin\Release\PublishOutput」になると思いますが、そのフォルダに生成される内容がCentOS上へコピーするファイルの一覧となります。

なお、発行と同じことをコマンドラインからMSBuildを使用して行う場合、以下のコマンドで同様のことが行えますので、CIに組み込む場合等には参考にしてください。

```bat:
MSBuild src\Xxx\Xxx.xproj /p:Configuration=Release /t:PackagePublish /p:PublishOutputPathNoTrailingSlash=bin\Release\PublishOutput
```

## 動作確認

CentOS上の「/opt/webapp」にファイルをコピーしたとして、そこに移動してdotnetコマンドを実行して動作確認を行います。

```console
[root@centos ~]# cd /opt/webapp
[root@centos webapp]# dotnet WebApplication.dll
Hosting environment: Production
Content root path: /opt/webapp
Now listening on: http://*:80
Application started. Press Ctrl+C to shut down.
```

この状態で、外部からWebブラウザで接続して動作確認を行います。

問題がなければ画面が表示され、コンソールにもログが表示されます。

## サービス化

動作確認ができたら、Webアプリケーションのサービス化を行います。

まず、起動用のスクリプトとして以下のような/opt/webapp.shを作成します。

```sh:/opt/webapp.sh
#!/bin/bash

cd /opt/webapp
dotnet WebApplication.dll
```

また、このファイルに対して実行権限を設定します。

```console
[root@centos ~]# chmod 755 /opt/webapp.sh
```

次に、サービスの起動スクリプトとして以下のような/etc/systemd/system/webapp.serviceを作成します。

```ini:/etc/systemd/system/webapp.service
[Unit]
Description = webapp daemon

[Service]
ExecStart = /opt/webapp.sh
Restart = always
Type = simple

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

以下のコマンドで正しく設定ができていることを確認します。

```console
[root@centos ~]# systemctl list-unit-files --type=service | grep webapp
webapp.service                              enabled
```

サービスの開始を行います。

```console
[root@centos ~]# systemctl start webapp
```

これで、OSを再起動しても、Webアプリケーションが自動起動してきます。

なお、これはあくまで最低限の設定なので、実際にプロダクションに導入する際には、もう少しスクリプトの内容を検討してください。

# うさコメ

…っということで、これで実行環境がLinuxという理由でJava + Tomcat + Eclipseみたいな開発していたｴｯｽｱｲﾔｰの中の人達なんかも、.NET Core + Visual Studioを選択肢とすることが可能になったわけです٩(๑>◡<๑)۶

また、開発に使用されるデータベースも、将来的にはSQL Server on Linux(のExpress版？)なんかも対象になってくるでしょうし、そうなればSQL Server Management Studioを使ったり、C# + SQL Serverの強力さを実行環境問わずに得られるようになるわけです。

っということで、みんなもこれからはWebアプリをASP.NET Coreで作るのじゃ(｀・ω・´)

# 参考文献

* [.NET Core 1.1.0 Runtime 及び .NET Core 1.0.1 tools Preview 2のダウンロード](https://www.microsoft.com/net/download/core) - Currentを選択してWindowsの.NET Core 1.1 SDK - InstallerとVisual Studio 2015 Tools (Preview 2)をダウンロード
* [.NET Core installation guide](https://www.microsoft.com/net/core#linuxcentos) - .NET CoreのCentOS公式インストール手順