.NET 6 と Daprを使った分散サービス開発 その12 コンテナイメージの作成 実行 Push

# コンテナイメージの作成 実行 Push

前回は、Secret管理について触れてきました。今までの、トピックでいわゆる **ローカル環境** でのデバッグや開発は一通り行いやすくなったと思います。
今回は、いよいよ コンテナイメージの作成からKubernetes にデプロイする**下準備**をしていきます、といっても今まで プロジェクト単位でコンテナをデプロイする事もDockerfileすら作っていませんが、楽にできます。

----
https://qiita.com/kazumihirose/items/ec98a49743d24702cce5

https://qiita.com/kazumihirose/items/d6d98d2e482947aad936

https://qiita.com/kazumihirose/items/aa34373369ce818e8027

https://qiita.com/kazumihirose/items/ff13fa45bf797e6eb878

https://qiita.com/kazumihirose/items/58d0127212068563fe27

https://qiita.com/kazumihirose/items/45b0ea36dd50d63f1d86

https://qiita.com/kazumihirose/items/701fcbfb397fe8670bf7

https://qiita.com/kazumihirose/items/016dde8ca9b35d1030cc

https://qiita.com/kazumihirose/items/e996e3cc34c511bc1124

https://qiita.com/kazumihirose/items/f7811bf3e5abe405a551

https://qiita.com/kazumihirose/items/57e3aeaab65a1a68977b

## ローカル環境でコンテナの作成

今までは、特にプロジェクトでコンテナを作ったりという事を、行わないまま作業を進めてきました。
tyeを使ってローカルでのコンテナイメージの作成と実行を行います。

### 新規にプロジェクトとソリューションを作ります

今までの検証で、あまりに検証プロジェクトが増えたので、**新しく作ることにします**。今までのおさらいと思って進めてください。
以下コマンドでプロジェクトとソリューションを作成します。いずれもプロジェクトルートとなるフォルダでの作業です。

```bash
$ dotnet new webapi -n ServiceA
テンプレート "ASP.NET Core Web API" が正常に作成されました。
$ dotnet new webapi -n ServiceB
テンプレート "ASP.NET Core Web API" が正常に作成されました。
$ dotnet new sln
テンプレート "ソリューション ファイル" が正常に作成されました。
$ dotnet sln add ServiceA ServiceB
プロジェクト `ServiceA\ServiceA.csproj` をソリューションに追加しました。
プロジェクト `ServiceB\ServiceB.csproj` をソリューションに追加しました。
```

### プロジェクト修正

ServiceAプロジェクトのControllersに、以下のファイルを追加します。

```csharp:JobController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace ServiceA.Controllers;

[ApiController]
[Route("[controller]")]
public class JobController : ControllerBase
{
    private readonly ILogger<JobController> _logger;

    public JobController(ILogger<JobController> logger)
    {
        _logger = logger;
    }

    [HttpPost(Name = "PostJob")]
    public JobResponse Post()
    {
        JobResponse response = new JobResponse
        {
            Date = DateTime.Now
        };

        Console.WriteLine(response.ToString());
        return response;
    }
}
```

ServiceAプロジェクトルートには、レスポンス用のクラスを追加します。

```csharp:JobResponse.cs
namespace ServiceA;

public class JobResponse
{
    public DateTime Date { get; set; }

}
```

#### ServiceAとServiceB共通
app.UseHttpsRedirection();をコメントアウトしておきます。

```csharp:Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

```

### tye 初期化とコンポーネントフォルダの作成
以下コマンドでtye.yamlを作成しましょう。

```bash
$ tye init
$ mkdir components
```

#### tye.yaml編集
作っておいたプロジェクトは、あらかじめ自動で追加されています。これを修正します。

```YAML:tye.yaml(修正前)
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
#
# when you've given us a try, we'd love to know what you think:
#    https://aka.ms/AA7q20u
#
name: daprqiitaaks
services:
- name: servicea
  project: ServiceA/ServiceA.csproj
- name: serviceb
  project: ServiceB/ServiceB.csproj

```

以下のように、daprに関するextensionsの項目を追加します。

```YAML:tye.yaml(修正後)
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
#
# when you've given us a try, we'd love to know what you think:
#    https://aka.ms/AA7q20u
#
name: daprqiitaaks
extensions:
- name: dapr
  log-level: debug
  components-path: "./components/"
services:
- name: servicea
  project: ServiceA/ServiceA.csproj
- name: serviceb
  project: ServiceB/ServiceB.csproj

```

#### コンポーネントの追加

コンポーネントの動作も確認できるように、今回はスケジューラーを追加します。componentフォルダにcron-binding.yamlを設定します。

```YAML:cron-binding.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: Job
spec:
  type: bindings.cron
  version: v1
  metadata:
  - name: schedule
    value: "@every 10s"
scopes:
- servicea

```


### tyeからDocker Imageをビルド

今までは、tye runやtye run --watchで起動してきました、その際にプロジェクトは自動でビルドされていたと思います。今回は tye build でビルドしてみます。

```bash (tyeによるDocker imageのビルド)
$ tye build
Loading Application Details...
Processing Service 'servicea'...
    Applying container defaults...
    Compiling Services...
    Publishing Project...
    Building Docker Image...
            #1 [internal] load build definition from Dockerfile
            # ... ... ...中略 ... ... ...
            #8 DONE 0.1s

            Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
        Created Docker Image: 'servicea:1.0.0'
Processing Service 'serviceb'...
    Applying container defaults...
    Compiling Services...
    Publishing Project...
    Building Docker Image...
            #1 [internal] load build definition from Dockerfile
            # ... ... ...中略 ... ... ...
            #8 DONE 0.1s

            Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
        Created Docker Image: 'serviceb:1.0.0'

```

Dockerfileも作っていませんが、これでDocker Imageがビルドできています。また、これは.NET標準で提供されているmcr.microsoft.com/dotnet/aspnet:6.0をベースイメージとして、自動でビルドしてくれています。

#### 作成されたDocker Imageの確認
以下の通り、serviceaとservicebのイメージが作成されています。

```bash
$ docker images
REPOSITORY          TAG            IMAGE ID       CREATED         SIZE
serviceb            1.0.0          dc943a517d2e   4 minutes ago   212MB
servicea            1.0.0          135a4f48497e   4 minutes ago   212MB
... ... ...中略 ... ... ...
```

#### 作成されたDocker Imageの起動

今までは、デバッグのしやすさなどを考慮して、コンテナで起動していませんでした。コンテナからローカル起動してみます。

```bash
$ tye run --docker
```

Tye Dashboard (http://localhost:8000) から各プロセスを確認してみましょう。TypeがContainerとなり、Sourceがmcr.microsoft.com/dotnet/aspnet:6.0となっているのがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/b69c1a42-2160-31ea-1aed-b03816ffcd77.png)

この時点で、スケジュールコンポーネントによるトリガ動作も確認しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/6f3a8c6d-437e-7c03-e433-1444576551d2.png)


### レジストリへイメージを登録

DockerHub や Azure Container Registry など、パブリック、プライベートなどの目的別に、様々なコンテナレジストリがあると思います。今回は Azure Container Registry を使いますが、認証ができて、ローカルからレジストリの操作ができれば、どこのコンテナレジストリでもOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/6d8b5d20-4636-29c5-c2c0-b37924876848.png)

#### 認証

Azure Container Registry の場合、事前に認証を行っておく必要があります。
今回は、対話型でログインしますが、Azure DevOps や GitHub Actionsで、CI/CDパイプラインを構成する場合は AD Service Principal や Managed IDを使います。これらの操作は各クラウドやレジストリによって異なりますので、それぞれ確認してください。

Azure Container Registry の場合

https://docs.microsoft.com/ja-jp/azure/container-registry/container-registry-authentication?tabs=azure-cli

```bash 
----
# Azure CLIでログインしてない人は、以下も実行しましょう
$ az login # Azureへのログイン
$ az account set --subscription mysubscription #複数サブスクリプションの人はサブスクリプションの指定
----
$ az acr login -n レジストリ名
Login Succeeded
```

#### レジストリにPush

Pushもtyeによって、一括でビルド、イメージビルド、Pushを行うことができます。tye push でコンテナイメージを作成後Pushします。

```bash:(イメージのPush)
$ tye push --interactive
Loading Application Details...
Enter the Container Registry (ex: 'example.azurecr.io' for Azure or 'example' for dockerhub): レジストリ名.azurecr.io
Processing Service 'servicea'...
    Applying container defaults...
    Compiling Services...
    Publishing Project...
    Building Docker Image...
            #1 [internal] load build definition from Dockerfile
            # ... ... ...中略 ... ... ...
            #8 DONE 0.0s

            Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
        Created Docker Image: 'レジストリ名.azurecr.io/servicea:1.0.0'
    Pushing Docker Image...
        Pushed docker image: 'レジストリ名.azurecr.io/servicea:1.0.0'
Processing Service 'serviceb'...
    Applying container defaults...
    Compiling Services...
    Publishing Project...
    Building Docker Image...
            #1 [internal] load build definition from Dockerfile
            # ... ... ...中略 ... ... ...
            #8 DONE 0.0s

            Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
        Created Docker Image: 'レジストリ名.azurecr.io/serviceb:1.0.0'
    Pushing Docker Image...
        Pushed docker image: 'レジストリ名.azurecr.io/serviceb:1.0.0'
```

これでレジストリに登録されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/9beed03c-79e6-4510-78f9-a7493e475661.png)

#### tye.yamlにレジストリを追加

毎回、interactiveモードで行うのも面倒なので、tye.yamlにあらかじめレジストリを追加しておく事ができます。
これでtye pushだけでpushする事ができます。ビルドパイプラインに組み込んでも、とても便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24782/212f9762-3278-9da4-938b-bbea5d40aa1d.png)

```YAML:tye.yaml(レジストリ追加)

# tye application configuration file
# read all about it at https://github.com/dotnet/tye
#
# when you've given us a try, we'd love to know what you think:
#    https://aka.ms/AA7q20u
#
name: daprqiitaaks
registry: レジストリ名.azurecr.io
extensions:
- name: dapr
  log-level: debug
  components-path: "./components/"
services:
- name: servicea
  project: ServiceA/ServiceA.csproj
- name: serviceb
  project: ServiceB/ServiceB.csproj
```