.NETのお勉強1_公式イメージを利用してコンテナでサーバたてる

# 概要
AmazonLinux2023インスタンス上にコンテナで.NETを利用したサーバをたてる
そのお勉強メモ1

とりあえずサーバ立ってレスポンス返るところまで

# インスタンスの準備
これ参照
https://qiita.com/chanP_yamazaki/items/25ce29518741cd55a2a6

# 最小限？のプロジェクト構成
```
cs_project
├── Makefile
├── Dockerfile.build
├── Dockerfile.runtime
├── docker-compose.build.yml
├── docker-compose.runtime.yml
├── src
│   ├── Controllers
│   │   └── HelloController.cs
│   ├── MyServerApp.csproj
│   └── Program.cs
└── app
```

# ソース
``` Makefile
build:
	docker compose -f docker-compose.build.yml build --no-cache
	docker compose -f docker-compose.build.yml up

upd:
	docker compose -f docker-compose.runtime.yml build --no-cache
	docker compose -f docker-compose.runtime.yml up -d

clean:
	@docker compose -f docker-compose.runtime.yml down --rmi all --volumes
	@docker compose -f docker-compose.build.yml down --rmi all --volumes

```

``` Dockerfile.build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS builder
WORKDIR /src

COPY ./src/MyServerApp.csproj .
RUN dotnet restore

COPY ./src/ .
```

``` Dockerfile.runtime
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
WORKDIR /app
COPY ./app/ .
ENTRYPOINT ["dotnet", "MyServerApp.dll"]
```

``` docker-compose.build.yml
services:
  build:
    image: builder
    build:
      context: .
      dockerfile: Dockerfile.build
    volumes:
      - ./app:/app
    command: dotnet publish -c Release -o /app
```

``` docker-compose.runtime.yml
services:
  app:
    image: runtime
    build:
      dockerfile: Dockerfile.runtime
    ports:
      - "80:8080"
```

``` src/Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

var app = builder.Build();
app.MapGet("/", () => "Hello, World!");
app.MapControllers();
app.Run();
```

``` src/MyServerApp.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>
```

``` src/Controller/HelloController.cs
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class HelloController : ControllerBase
{
    [HttpGet]
    public string Get() => "Hello from API";
}
```

# コンテナイメージについて
プログラムのビルド用コンテナと、実行環境用コンテナは別
`mcr.microsoft.com/dotnet/sdk:9.0`がビルド用、`mcr.microsoft.com/dotnet/aspnet:9.0`がビルド後PJの実行環境用

プロジェクトビルド用のsdkイメージをdocker compose buildでビルド、docker compose upでプロジェクトビルドを実行
やろうと思えば1コマンドでできるけど、役割を明確に分けた方が良さげ？

# 実行テスト
`make upd`実行してコンテナを立ち上げ、`xx.xx.xx.xx/`アクセスで「Hello, World!」、`xx.xx.xx.xx/hello`アクセスで「Hello from API!」と出力されればok

# その他メモ
### Dockerfile.build
`dotnet restore`はphpでいうところの`composer install`的なこと
`MyServerApp.csproj`が`composer.json`でrestore時にはキャッシュが使われるが、プロジェクト全体が存在するとキャッシュがうまく使われない可能性があるので、`MyServerApp.csproj`だけをCOPYしたあとにrestoreを実行し、終わってからソース全体をCOPYするんだとか

### MyServerApp.csprojとProgram.cs
.csprojに`<ImplicitUsings>enable</ImplicitUsings>`を定義しないとビルド時にエラーになる
```
/src/Program.cs(1,15): error CS0103: The name 'WebApplication' does not exist in the current context [/src/MyServerApp.csproj]
```

この定義は、.NETで一般的なusing宣言を全ファイルで暗黙的に宣言したことにするか否かのフラグ
定義しないこともできるけど、その場合は全ファイルで同じようなusingを書く必要があるので定義しておいた方が楽だとか

仮に定義しない場合は`Program.cs`には以下のusingが必要になる
```
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
```

### .NETサーバのデフォルトポート
8080で待ち受けているのでymlファイル側で外部80ポートを内部8080ポートにリッスン