.net core 3 以降のコンテナイメージは、MCRでしか公開されない?

.NET Core Container Images now Published to Microsoft Container Registry
https://devblogs.microsoft.com/dotnet/net-core-container-images-now-published-to-microsoft-container-registry/

1.X系と2.X系のDockerイメージはDockerHubで引き続き公開されるけれど、3.X系はAzureのDockerリポジトリーであるMicrosoftContainer Registry(MCR)でのみ公開されるみたいですね。

2.0はすでにEOLなので、MCRでは改めてホストされずDockerHubにだけあるみたいです。

広告
カテゴリー:ASP.NET, Docker

Visual Studio 2019のAlpine対応

2019年4月8日 1件のコメント

Visual Studio 2019がRTMされましたね。

Visual Studio 2017を利用している場合、Visual Studio で Alpine をベースイメージにしている場合にデバックが失敗する問題がありました。Visual Studio 2019 で対応すると言うことだったので早速試してみることにしようと思います。

Visual Studio 2019でASP.NET CoreでDockerサポートを有効にした場合のDockerfileです。
DockerリポジトリがDockerHubからAzureのMCRに変更になっていますね。

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

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

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

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

この部分を、

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base

こうします。

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-alpine AS base

bash を使ってコンテナを強制停止していたのが、shを利用するに変更されたみたいです。

1>------ ビルド開始: プロジェクト: WebApplication15, 構成: Debug Any CPU ------
1>docker exec -i 2f39a9a30f607e1a0cd9cee7043ebfceff83ec20112e91363acd971b9fadbeee /bin/sh -c "if PID=$(pidof dotnet); then kill $PID; fi"

コンテナのベースイメージの時点で100MB近い違いがあるので、プロダクションでの利用はできるだけこっちを使いたいですね。
vs2019-container-image

カテゴリー:ASP.NET, Docker

asp.net coreのコンテナ作成時にgulpを動かしてscssのコンパイルを行う。

2019年3月28日 コメントを残す

コンテナ作って動かしたらCSSあたってないよと言われて内容を確認する。
wwwroot/cssにcssがない、、、gulpのタスクが動いていないっぽい。
Visual StudioのタスクランナーでAfter Build などに指定しても、dotnet restoreでは実行してくれないので、自前にgulpを叩いてあげる必要があるみたいですね。

gulpfile.jsにこんな感じでscssのビルドタスクが登録されているとして

gulp.task("build:sass", () => {
    return gulp.src("./Assets/Sass/*.scss")
        .pipe(sass())
        .pipe(concat("site2.css"))
        .pipe(gulp.dest("wwwroot/css"));
});

... 略 ...

gulp.task("min", gulp.series(["min:js", "min:css"]));

// A 'default' task is required by Gulp v4
gulp.task("default", gulp.series(["min", "build:sass"]));

Dockerfileはこんな感じ、

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.2-sdk AS build
RUN apt update -yq && apt upgrade -yq && apt install -yq curl git nano
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt install -yq nodejs build-essential
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN npm install -g gulp && npm install && gulp
RUN dotnet build "WebApplication1.csproj" -c Release -o /app

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

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

microsoft/dotnet:2.2-sdkにはnodeがインストールされていないので、nodeのビルドに必要なパッケージをaptでインストールしたあとに、gitからnodeのセットアップ一式を持ってきてインストールします。

RUN apt update -yq && apt upgrade -yq && apt install -yq curl git nano
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt install -yq nodejs build-essential

npmでgulpをグローバルインストールした後に、必要なモジュールをnpmで取得してgulpをキックします。

RUN npm install -g gulp && npm install && gulp

dockerignoreでnode_moduleを指定しておかないと、Windows環境などでビルドしていた場合にnode-sassのWindowsバイナリーがコピーされておかしくなってしまうので注意ですね。
.dockerignore

node_modules
カテゴリー:ASP.NET, Docker

ASP.NET Core のヘルスチェック

2019年3月25日 コメントを残す

これまでは当たり障りのないAPIなどを、L/BのハートビートのURLに指定してきたけれど、asp.net core からはヘルスチェック用のエンドポイントを作成するミドルウェアが予め組み込まれているみたい。

Microsoft Docs 正常性の監視
https://docs.microsoft.com/ja-jp/dotnet/standard/microservices-architecture/implement-resilient-applications/monitor-app-health

基本的には、こんな感じ、例はeShopOnWebの実装箇所

 

ヘルスチェックライブラリ

MySqlやS3、MongoDBみたいな標準的なサービスのヘルスチェックはxabarilで実装されているやつが使えそう。
https://github.com/xabaril/AspNetCore.Diagnostics.HealthChecks

こんなものもというところで、IdentityServerやHangfireといったライブラリ用のCheckerも登録されていますね。IdentityServerのCheckerはaspnetcore.healthchecks.openidconnectserverというパッケージ名で登録されている割に、拡張メソッドはAddIdentityServerなのでちょっとハマった。

しばやん雑記に詳しい説明があったのでリンクしておく
https://blog.shibayan.jp/entry/20181219/1545185333

カテゴリー:ASP.NET

Viewのマイグレーションを考える

2019年3月18日 コメントを残す

異なるスキーマを参照するViewを作って、Entity Frameworkから参照したいという要件があって、Viewの更新管理をどうにかしたいなーと思っていたんだけれど、だいたい固まったのでメモしておく

方針としてはこんな感じ

  1. Viewの形のEntityを作り、DbContextにDbQueryとして公開する
  2. OnModelCreatingで、Query~ToViewで関連付ける
  3. 空のマイグレーションファイルを作り、upでcreate viewを、downでdrop viewを行う。スキーマの差し替えもこのタイミングで行う。

サンプル
https://github.com/karuakun/EntityFrameworkSchemaAcrossDbViewSample

EntityとDbContext

1,2はササッとこんな感じで
Data/Entities/QueryTest.cs

    public class Test1
    {
        [Key]
        public string Id { get; set; }

        public string Name { get; set; }
        public string QueryTest2Id { get; set; }
    }

Data/SampleDbContext.cs

    public class SampleDbContext: DbContext
    {
        public const string DefaultConnectionStringName = "sampledb";

        public SampleDbContext(DbContextOptions options) : base(options)
        {
        }

        public virtual DbQuery QueryTest2 { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Query(entity =>
            {
                entity.ToView("v_seconddb_test2");
            });
        }
    }

マイグレーションの作成

DbContextにマイグレーションに関わる変更がない状態で、マイグレーションファイルを追加すると、空のマイグレーションファイルが出来上がるので、この子にViewの作成と削除を書いてあげる。

dotnet migrations add addsampleview

追加されたマイグレーションファイル

    public partial class addview : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
        }
    }

まずは単純に

create view にスキーマ名が入っているので気持ち悪い。。。

    public partial class addview : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(
                "create view v_seconddb_test2 as select Id, Name, Property1 from schema2.test2");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql("drop view v_seconddb_test2");
        }
    }

Configurationもしくは環境変数から、マッピングを取得する

Startup時に作るIConfigurationRootにアクセスできれば良いんだけれど、MigrationだとDIでConfigurationを受け取ることができない。
ConfigurationをStartupのstaticにしてあげるのは嫌なので、、、DesignTimeContextを使う

こんなクラスをプロジェクトのルートに置いておくと、dotnet efで起動した場合にこっちのコンテキストで初期化してくれる。これなら多少無茶してもいいかなぁ、、、と言うことで、IConfigurationRootをstaticで生やしてあげる。

    public class DesignTimeSampleDbContextFactory : IDesignTimeDbContextFactory
    {
        public static IConfigurationRoot DesignTimeConfiguration { get; private set; }

        public SampleDbContext CreateDbContext(string[] args)
        {
            DesignTimeConfiguration = DesignTimeConfigurationBuilder.BuildConfiguration(args);
            var builder = new DbContextOptionsBuilder()
                .UseMySql(DesignTimeConfiguration.GetConnectionString(SampleDbContext.DefaultConnectionStringName)
                );
            return new SampleDbContext(builder.Options);
        }
    }

Configurationを参照できれば、あとはJSONと環境変数の世界なので、appsettings.jsonにこんな感じでエントリを追加して

  "MigrationSettings": {
    "MigrationType": "Local",

    "SchemaMappings": {
      "Local": {
        "AppDb": "appdb", 
        "SecondDb": "seconddb"
      },
      "Stating": {
        "AppDb": "stg_appdb",
        "SecondDb": "stg_seconddb"
      }
    }
  },

設定を受けるようにこんなクラスを作っておく

    public class MigrationSchemaMappings
    {
        public string AppDb { get; set; }
        public string SecondDb { get; set; }
    }

で、手作業で作ったSQLがマイグレーションの海に埋もれるのは悲しいので、手作業で作った子はマイグレーション配下にこんな感じでmanualフォルダーを作って、そこに物理ファイルとしておいておきたい。
無題

マイグレーションの拡張メソッドとして、こんな感じのものを作っておけば、

    public static class MigrationExtensions
    {
        public static string GetSqlText(this Migration source, string migrationId, string fileName)
        {
            var sqlPath = Path.Combine(Environment.CurrentDirectory, "Migrations", "manual", migrationId, fileName);
            if (!File.Exists(sqlPath))
                throw new FileNotFoundException(sqlPath);

            var sqlText = File.ReadAllText(sqlPath);
            if (string.IsNullOrEmpty(sqlText))
                throw new InvalidOperationException($"sqlfile is empty: {sqlText}");

            var settings = GetMigrationSettings();
            var schemaReplacedSqlText = sqlText.Replace("$SecondDb$", settings.SecondDb);
            return schemaReplacedSqlText;
        }

        private static MigrationSchemaMappings GetMigrationSettings()
        {
            var configuration = DesignTimeSampleDbContextFactory.DesignTimeConfiguration;
            var migrationType = configuration.GetValue("MigrationSettings:MigrationType");
            return configuration.GetSection($"MigrationSettings:SchemaMappings:{migrationType}")
                .Get();
        }
    }

マイグレーションスクリプトはこうかける。

    public partial class addview : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(this.GetSqlText(this.GetId(), "up.v_seconddb_test2.sql"));
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(this.GetSqlText(this.GetId(), "down.v_seconddb_test2.sql"));
        }
    }
カテゴリー:ASP.NET, Visual Studio

asp.net core を linux 上で動かす場合、環境変数でConfigurationを上書きする場合はコロンではなく、ダブルアンダースコアを利用する。

asp.net core を利用する場合、設定情報を jsonファイルだけでなく、環境変数やコマンドライン引数からも読み込みマージして利用することができます。

例えばこんな appsettings.json にこんな設定を書いた場合、

{
  "HogeConfig": {
    "Value": "ValueHoge",
    "ValueList": [
      "a", "b", "c"
    ]
  } 
}

Program.cs でこんな感じに設定ファイル>環境変数>コマンドラインから読み込むように定義して置くと、

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration(cfg =>
                {
                    cfg.SetBasePath(Directory.GetCurrentDirectory())
                        .AddJsonFile("appsettings.json")
                        .AddEnvironmentVariables()
                        .AddCommandLine(args);
                })
                .UseStartup();
    }

利用側は環境変数や、コマンドライン引数で HogeConfig:Value=Hogegege のように設定値を上書きすることができます。
ただし、linux などの環境では、 export HogeConfig:Value=Hogegege や HogeConfig:0=abc とかすると、識別子が不正(not a valid identifier)とかいって怒られてしまいます。
(docker-compose の environment で指定する分にはコロン区切りでも使えるのに、、、)

この場合、コロンの代わりにアンダーバーを2つ()使うことで回避できるようです。
ASP.NET Coreのドキュメントを見ると、
はすべての環境で利用できると書いてあるので、できるだけこっちを使ったほうが良いのかもしれませんね。
https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/configuration/index?tabs=basicconfiguration&view=aspnetcore-2.2#conventions

カテゴリー:ASP.NET

自分なりにRailsの開発用ベースコンテナを作った

2019年2月24日 コメントを残す

Rails自体はなんとなく基礎は知っていて、入門本を一通り読んだぐらいの知識しかないわけだけれど、コードレビューできるぐいらにはなっときたいなということで、とりあえず環境構築を簡略化するためのDockerコンテナを自分なりに整理したので、とりあえず公開しておく。

バージョンの違いなんかで動いた、動かないは辛いし、他の人に環境を作ってっていう場合にフォローするのが大変なので、この頃は基本的にDockerでの開発イメージをベースにしている。
https://github.com/karuakun/ruby-practise-base

利用方法はREADME.mdに書いたけれど、同じコンテナに対して2回 Docker Build しかも2回目は –no-cache しないと行けないのが気になる。。。

カテゴリー:未分類