Windows Container on Fargateで遊ぶ準備が終わってなかった

本投稿はAWS Containers Advent Calendar 2021の16日目の記事です。投稿時間は16日の34時過ぎでしょうか。



9日での[記事](https://qiita.com/isleys/items/14913405908cb030d9ce)で書いた内容で、Windows on FargetでWindowsコンテナを動かす準備が終わった！と言いたい所ですが、前回の内容だと1つ大きな問題点が残ってます。

そう、それはログ


## ログが出力されてないんです
前回のサンプルをベースにWindowsコンテナを起動してみると、`docker container logs`でログを確認することができません。標準的なIISのアクセスログの出力先は、ファイル出力になりますし、Windowsのメジャーなログ出力先にはEventLogがありますよね。STDOUT/STDERRにログを出力することができれば、これまでのLinuxコンテナ流でのログ取得ができそうです。

また、Windows Container on Fargateは logging driverとしてawslogsをサポートしているので、STDOUT/STDERRに出力できれば、Cloudwatch Logsにログを出力することもできるでしょう。

さて、どうしたものかと調べていると Microsoftからコンテナ向けのロギングツールの[blog記事](https://techcommunity.microsoft.com/t5/containers/windows-containers-log-monitor-opensource-release/ba-p/973947)が公開されていました。ロギングツールであるWindows Container Log Monitorのソースは[Github上](https://github.com/microsoft/windows-container-tools)で確認することができます。

Log MonitorはLinuxコンテナでのメジャーなログ出力方法を Windows 環境でも実現するためのツールです。
先ほどのblog記事の中にあるアーキテクチャ図をみて頂けると、どんな雰囲気が理解しやすくなると思います。

Log Monitorは、以下のログをサポートしており、IIS周りであれば、ひとまずこれを使えばよさそうですね

* ETW (Event Tracing for Windows)
* Event Logs
* Log files


では、早速、やってみましょう。


## Log Monitorの導入
実行ファイルは[GitHubのrelease](https://github.com/microsoft/windows-container-tools/releases/tag/v1.1)からダウンロードしてきます。ダウンロードした実行ファイルと設定ファイルを準備するだけでOKです。前回のサンプルのDockerfileをベースに以下の様な形に修正してみました。
ほとんどはオリジナルなサンプル通りなのですが、今回は、Dockerfile全体を載せてみます。

```Dockerfile
# escape=`

# https://hub.docker.com/_/microsoft-dotnet
FROM mcr.microsoft.com/dotnet/sdk:6.0.100-windowsservercore-ltsc2019 AS build
WORKDIR /source

# copy csproj and restore as distinct layers
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore

# copy everything else and build app
COPY aspnetapp/. ./aspnetapp/
WORKDIR /source/aspnetapp
RUN dotnet publish -c release -o /app --no-restore

# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:6.0.0-windowsservercore-ltsc2019

# Only needed for this sample because the sample project is targeting .NET 5
ENV DOTNET_ROLL_FORWARD=LatestMajor

RUN powershell -Command `
        $ErrorActionPreference = 'Stop'; `
        $ProgressPreference = 'SilentlyContinue'; `
        `
        # Install IIS
        Add-WindowsFeature Web-Server; `
        Remove-Item -Recurse C:\inetpub\wwwroot\*; `
        `
        # Acquire ServiceMonitor
        Invoke-WebRequest -OutFile C:\ServiceMonitor.exe -Uri https://dotnetbinaries.blob.core.windows.net/servicemonitor/2.0.1.10/ServiceMonitor.exe; `
        `
        # Install the ASP.NET Core Module
        Invoke-WebRequest -OutFile c:\dotnet-hosting-win.exe https://aka.ms/dotnet/6.0/preview/dotnet-hosting-win.exe; `
        $process = Start-Process -Filepath C:\dotnet-hosting-win.exe -ArgumentList  @('/install', '/q', '/norestart', 'OPT_NO_RUNTIME=1', 'OPT_NO_X86=1', 'OPT_NO_SHAREDFX=1') -Wait -PassThru ; `
        if ($process.ExitCode -ne 0) { `
            exit $process.ExitCode; `
        } `
        Remove-Item -Force C:\dotnet-hosting-win.exe; `
        Remove-Item -Force -Recurse $Env:Temp\*;`
        `
        Import-Module -Name WebAdministration;`
        Set-ItemProperty -Path 'IIS:\Sites\Default Web Site' -Name logfile.logTargetW3C -Value 'File,ETW';

#Install LogMonitor
WORKDIR /LogMonitor
RUN powershell -Command `
        Invoke-WebRequest -OutFile LogMonitor.exe -Uri https://github.com/microsoft/windows-container-tools/releases/download/v1.1/LogMonitor.exe;
COPY LogMonitorConfig.json .

WORKDIR /inetpub/wwwroot
COPY --from=build /app ./

EXPOSE 80

SHELL ["C:\\LogMonitor\\LogMonitor.exe", "powershell.exe"]
CMD ["C:\\LogMonitor\\LogMonitor.exe", "powershell.exe","C:\\ServiceMonitor.exe", "w3svc"]
```


LogMonitorを導入しているのは、以下の部分です

```Dockerfile
#Install LogMonitor
WORKDIR /LogMonitor
RUN powershell -Command `
        Invoke-WebRequest -OutFile LogMonitor.exe -Uri https://github.com/microsoft/windows-container-tools/releases/download/v1.1/LogMonitor.exe;
COPY LogMonitorConfig.json .
```

実行ファイルをダウンロードしてきて、設定ファイルであるLogMonitorConfig.jsonを配置しています。
設定ファイルは、`C:\LogMonitor\LogMonitorConfig.json`に置かれる事が想定されているようなので、

```
C:\LogMonitor
    |- LogMonitor.exe
    |- LogMonitorConfig.json
```

という配置にしています。設定ファイルであるLogMonitorConfig.jsonは以下の様な内容で定義してみました。
Windows Container on Fargate でどの様なログが確認できるかを試したかったので、幅広に取得する様にしています。

IISの設定的には、アクセスログは、ファイル出力、ETWどちらも可能ですが、アクセスログをファイル出力すると、若干バッファリングされて出力されるので、アクセス直後にはログが出ないんですよね。
ETWの方が出力されるタイミングが早い様なので、今回はあえて両方取得してみました。
IIS側の設定もFile,ETWの両方を指定してあります(`Set-ItemProperty`あたりの部分)。

EventLogをソースとする場合、`"startAtOldestRecord": false`としておくと、コンテナ実行後に出力されたEventLogからログを出力してくれます。

```json
{
    "LogConfig": {
      "sources": [
        {
          "type": "EventLog",
          "startAtOldestRecord": false,
          "eventFormatMultiLine": false,
          "channels": [
            {
              "name": "system",
              "level": "Information"
            },
            {
              "name": "application",
              "level": "Error"
            }
          ]
        },
        {
          "type": "File",
          "directory": "c:\\inetpub\\logs",
          "filter": "*.log",
          "includeSubdirectories": true
        },
        {
          "type": "ETW",
          "eventFormatMultiLine": false,
          "providers": [
            {
              "providerName": "IIS: WWW Server",
              "providerGuid": "3A2A4E84-4C21-4981-AE10-3FDA0D9B0F83",
              "level": "Information"
            },
            {
              "providerName": "Microsoft-Windows-IIS-Logging",
              "providerGuid": "7E8AD27F-B271-4EA2-A783-A47BDE29143B",
              "level": "Information"
            }
          ]
        }
      ]
    }
  }
```


IISを起動する部分は以下の様な記載になりました。ここで突然`ServiceMonitor.exe`が出てきました。 これは[Windowsコンテナの中でIISを利用するとき](https://github.com/microsoft/IIS.ServiceMonitor)のツールです。`ServiceMonitor.exe`をラップする様なイメージで`LogMonitor.exe`を使います。

```Dockerfile
SHELL ["C:\\LogMonitor\\LogMonitor.exe", "powershell.exe"]
CMD ["C:\\LogMonitor\\LogMonitor.exe", "powershell.exe","C:\\ServiceMonitor.exe", "w3svc"]
```

## 起動してみました
CloudWatch Logsにもログが流れ、いい感じになりました。不要なログがあれば、設定を見直していけばよさそうですね
`<Source>EtwEvent</Source>`となっているところがETW経由のアクセスログです。ちなみに`<Source>EventLog</Source>`であれば、
EventLogがソースとなっているログエントリです。

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63007/00e87bab-4459-f206-46f2-11a02574bb03.png)

Container Insightsでもメトリクスが取れてますね。

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63007/b6c819eb-544b-5ba8-7ac7-0c2b039c995c.png)


さあ、これから色々試すぞ！