.NetCoreのWebアプリ新規作成〜Appサービス（Linuxコンテナ）にデプロイするまでの手順（SSH有効化も含む）

通常ではAzure Appサービス（Linuxコンテナ）へのSSHアクセスが無効になっている。
デバッグや他の目的でLinuxコンテナの中に入って調査することがあると思う。
本手順では簡単なアプリ作成、アプリをコンテナ化、コンテナにSSHサーバーをインストール、Azureにデプロイするまでカバーする。

###アジェンダ
1.簡単なアプリを作ろう
2.コンテナ化（モダナイズ）しよう
3.SSH対応（Appサービス/home/LogFiles/フォルダーを使用する場合、このステップを飛ばしてください）
4.APPサービスの作成とデプロイ

###前提条件
対象者：レベル200以上
下記のソフトは事前にインストールされていることを前提とする
1.DotNet SDK
2.VSCode
3.Docker Desktop
4.Azureアカウント(無い場合は無償のアカウントを作成)

###1.簡単なアプリを作ろう
・アプリ作成手順：[VisualStudio for Mac](https://docs.microsoft.com/ja-jp/visualstudio/mac/asp-net-core?view=vsmac-2019)
・アプリ作成手順：[ASP.NET Coreのチュートリアル（Windows・MAC・VScode）](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/razor-pages/razor-pages-start?view=aspnetcore-5.0&tabs=visual-studio)

この記事でmacのターミナルを使用して簡単なアプリを作成する。
WEBアプリ作成コマンド

```zsh:code
dotnet new webapp -o webapp --no-https
```
実行してみましょう

```zsh:code
cd webapp
dotnet watch run
```
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/d0ad49aa-6fc0-6913-dde9-80b70147bd84.png)
デフォルトブラウザが立ち上がり、下記の画面が表示される。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/6cdebe0d-bdad-27ee-f27b-893ea3cc0ceb.png)

コードを編集してみましょう

```zsh:code
code .
```
VSCodeが立ち上がる。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/77a98f71-5ee1-8c34-4f73-cc3542a50d87.png)

Pages/Index.cshtmlを下記のように編集する。

```csharp:Index.html
@page
@using System
@using System.IO
@model IndexModel
@{
    ViewData["Title"] = "Home page";
    // なお、出力先を/home/LogFiles/に設定するとSSHなしでログファイルを閲覧できる。
    string path = "/tmp/webapp.log";
    // ログファイルに出力する３行の文字列
    string[] lines = { "１行目のデータ", "２行目のデータ", "３行目のデータ" };

    // webapp.logファイルに適当な情報を書き込む。
    using (StreamWriter outputFile = new StreamWriter(path))
    {
        foreach (string line in lines)
            outputFile.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.ffffff") + " " + line);
    }
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
    @* 画面にも出力しよう *@
    @foreach (string line in lines){
      <p>@DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.ffffff")   @line</p>
    }
  
</div>
```

実行してみましょう
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/0bdf4e1a-a4cb-0e66-5155-b48b88eababf.png)
ログファイルの内容：

```zsh:code
view /tmp/webapp.log
```
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/ab2a8d6d-282c-3692-442c-0119d3b065b8.png)


###2.コンテナ化（モダナイズ）しよう
Dockerファイルを追加
下記をプロジェクトフォルダのDockerfileというファイル名で保存する。
※VisualStudioの場合、プロジェクト右クリック→追加→Dockerサポート


```docker:Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["webapp.csproj", "."]
RUN dotnet restore "./webapp.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "webapp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "webapp.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "webapp.dll"]

```
<font color="red">プロジェクトファイルのDotNetのバージョンをLTS ３.１に設定する</font>

```xml:webapp.csproj
<TargetFramework>netcoreapp3.1</TargetFramework>
```

下記Dockerコマンドでビルドしてから、アプリをコンテナ内に実行する。

```zsh:command
docker build -t webapp .
docker run --rm --name webapp -d -p 8080:80 webapp
```

ブラウザーを立ち上げて「http://localhost:8080/」をアドレスバーに入力する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/087f0e6a-4ef0-4787-69af-3527eb80e2b3.png)



###3.SSH対応
コンテナ内にSSHDを実行するため、下記のコンフィグファイルが必要。
プロジェクトフォルダに下記の「sshd_config」を追加する。

```config:sshd_config
Port 			        2222
ListenAddress 		    0.0.0.0
LoginGraceTime 		    180
X11Forwarding 		    yes
Ciphers                 aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr
MACs                    hmac-sha1,hmac-sha1-96
StrictModes 	       	yes
SyslogFacility 	        DAEMON
PasswordAuthentication 	yes
PermitEmptyPasswords 	no
PermitRootLogin 	    yes
Subsystem sftp internal-sftp
```

Dockerfileには単一のENTRYPOINTしか記述出来ないため, ２つ以上のプロセスを実行するには
シェルもしくはsupervisordが使える。
今回supervisordで２つのプロセスを実行する。

プロジェクトフォルダに下記の「supervisord.conf」を追加する。

```config:supervisord.conf
[supervisord]
nodaemon=true

[program:api]
command=dotnet /app/webapp.dll

[program:ssh]
command=/usr/sbin/sshd -D
```

次にDokerfileを下記のように編集する。

```docker:Dokerfile
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80 2222

# ------------------------
# SSH Server support
# ------------------------
RUN apt-get update \
     && apt-get install --yes --no-install-recommends supervisor openssh-server vim tcptraceroute openrc \
     && apt-get update \
     && echo "root:Docker!" | chpasswd

RUN mkdir -p /var/log/supervisor /run/sshd
WORKDIR /src
COPY ./sshd_config /etc/ssh/
COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["webapp.csproj", "."]
RUN dotnet restore "./webapp.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "webapp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "webapp.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
#ENTRYPOINT ["dotnet", "webapp.dll"]
ENTRYPOINT ["/usr/bin/supervisord"]

```
<font color="red">本番環境で使用する場合、SSHログイン情報"root:Docker!"を必ず変更する。</font>
下記Dockerコマンドを実行する。

```zsh:command
docker stop webapp
docker build -t webapp .
docker run --rm --name webapp -d -p 8080:80 -p 2222:2222 webapp
```

ブラウザーで「 http://localhost:8080/ 」確認する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/86a28690-c4d4-4abe-f2c7-f8c0cd3dfe07.png)

SSHでコンテナにログインする。

```zsh:command
 ssh root@localhost -p 2222 
```
パスワードはDockerfileに設定したDocker!です。 
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/13df5fd8-493a-d476-091a-f4dcc1e50f42.png)
ファイル内容を確認する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/88547a63-0fe1-eb5e-6750-4872163f63a2.png)



###4.APPサービスの作成とデプロイ
商用ではAzure Container Registryがおすすめだが、今回はデモアプリなので、
Docker Hubにプッシュして、Auzre Appサービスにデプロイする。
下記のコマンドでイメージにタグを付けて、Docker Hubにプッシュする。

```zsh:command 
docker tag webapp:latest リポジトリ名/webapp:latest
docker push リポジトリ名/webapp:latest
```

Appサービス作成手順：
Webアプリを選択
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/7c0eca23-3fc8-9ecf-cb77-27a8300cd270.png)
情報入力
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/1457dd99-729b-95ee-9f7e-7529d4c921ae.png)
イメージとタグに「リポジトリ名/webapp:latest」を入力する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/7c7fa4c5-fb7c-546f-964a-e538e20474fc.png)
作成ボタンを押して作成する。
展開後にリソースに移動してURLをクリックして確認する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/c0c49361-dd3b-72a2-c32b-ca4826d7594a.png)
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/a9d19706-91fc-e66a-eec5-e88eb47c5823.png)

SSHの確認
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/ee41fa42-e465-cf11-d5c1-60303fb79fef.png)

![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/71d1bde9-907a-9b2f-45b7-a73c686730d1.png)


上記でも述べているが、ログファイルのみの出力の場合、出力先を/home/LogFiles/にすれば、SSHなしでログを閲覧できる。
その場合、下記のコマンドをCLI又はCloudShellにて実行する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/cb43d71f-5dee-d68b-f227-37b69d0ebc36.png)

```shell:command
az webapp config appsettings set --resource-group リソースグループ名 --name APPサービス名 --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true

az webapp log config --resource-group リソースグループ名 --name APPサービス名 --docker-container-logging filesystem
```
今回はSSHを利用しているので、下記のコマンドでファイルをコピーしてAzureポータルからも閲覧できる。

```shell:command
cp /tmp/webapp.log /home/LogFiles
```
その後高度なツールを起動して、Bashでファイルを確認する。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/d3c375fb-b95b-b4ea-7030-745c6535648c.png)
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/967700/5fca1d4c-ae2d-34c8-5fa8-33808cf2d606.png)

Happy Coding!