きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × SIer

ASP.NET Core × アプリケーション設定 × 設定方法について考えてみた

この記事の目的

この記事では、
ASP.NET Coreでの環境変数の設定についてまとめ、
より良い設定方法について考えること
を目的としています。

プログラミングASP.NET Coreを参考に、進めていきます。


本題

★実行環境について

今回利用する開発環境は以下の通りです。

  • Windows 10 64bit
  • C# (8.0 bata)
  • .NET Core Version 3.0.100-preview3-010431
  • ASP.NET core 空テンプレート

f:id:kinakomotitti:20190602143449p:plain


★ホストする環境の読み込み

アプリケーションを開発する環境(=開発環境)と、利用者に利用してもらえるように準備した環境(=本番環境)など、アプリケーションをホストする環境にはいろいろな定義があります。そのため、アプリケーションをデプロイするためには、データベースの接続文字列など、それぞれの環境下で適切な設定をアプリケーションにしていく必要があります。

従来のASP.NET開発では、よくWeb.configにそれぞれの環境用の設定をして配置するときにコメントアウト・インで対応したり、「Web.config.Prod」のように、ファイルをそれぞれ分けて管理し、アプリケーションを配置した後に、名前を元に戻すといった方法をしてきました。そのため、アプリケーションの配置のときに、設定ファイル関連のミスが絶えず発生していました。

そういった問題を解決できる機能がASP.NET Coreにありました。
ASP.NET Coreでは、【ASPNETCORE_ENVIRONMENT】という環境変数を利用して、今アプリケーションがホストされているサーバがどの環境(開発環境・本番環境など)であるかを判断します。フレームワークでは、 Development、Staging、Production という 3 つの値が定義されており、これらを利用して環境の切り分けをすることができます。また、独自の環境を定義することもできるようです。

環境名の切り替えについては、Startup.csのテンプレートで見つけることができる以下のコードからもわかります。

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment()) //←ココ
            {
                app.UseDeveloperExceptionPage();
            }
        }

このIsDevelopmentメソッドで実行される処理は、以下のようになっており、”Development”という文字かどうかで判断されます。
※hostingEnvironment .EnvironmentNameに、【ASPNETCORE_ENVIRONMENT】の値が設定されています。

        /// <summary>
        /// Compares the current hosting environment name against the specified value.
        /// </summary>
        /// <param name="hostingEnvironment">An instance of <see cref="IHostingEnvironment"/>.</param>
        /// <param name="environmentName">Environment name to validate against.</param>
        /// <returns>True if the specified name is the same as the current environment, otherwise false.</returns>
        public static bool IsEnvironment(
            this IHostingEnvironment hostingEnvironment,
            string environmentName)
        {
            if (hostingEnvironment == null)
            {
                throw new ArgumentNullException(nameof(hostingEnvironment));
            }

            return string.Equals(
                hostingEnvironment.EnvironmentName,
                environmentName,
                StringComparison.OrdinalIgnoreCase); //←環境設定の判断処理のコア部分
        }

デフォルトでは、Developmentが設定されています。
f:id:kinakomotitti:20190602172826p:plain

このキーの環境変数がない場合は、Productとして認識されるため、本番環境として実行されます。DevelopmentやProductのような環境の切り分けをしていないのであれば、特に影響はありませんが、開発環境でのみ実行したい処理などの実装があったり、後述するアプリケーション設定の切り替えをしようとする場合は、もろに影響を受けるところなので、注意が必要です。




★環境ごとのStartiup処理

開発環境や、本番環境で、Startupで処理する内容が異なるときの対応についてです。1つの方法としては、上記の実装のように、IsDevelopmentメソッドなどを利用し、実行時の環境を判定し、そこに必要な処理を追加するといったものがあります。


それ以外の方法としては、Startup*.csというファイルを用意してそこに実装を組み込むということもできます。
まず、Program.csにて、以下のようにStartUpクラスの指定処理を変更します。これにより、実行しているアセンブリの中のStartup*クラスを探して、それを利用して実行します。探す場合は、環境変数【ASPNETCORE_ENVIRONMENT】の値をもとに適切な環境のStartupを探しますが、それらのクラスが定義されていないばあいは、Startupクラスが実行されます。

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    //webBuilder.UseStartup<Startup>(); ←ココ
                    webBuilder.UseStartup(Assembly.GetEntryAssembly().GetName().Name); //←ココ
                });


次に、開発環境に対しては、StartupDevelopment.csというファイルを作成し、そこに開発環境用のStartup処理を記述し、本番環境用には、Startup.csに本番用だけの処理を記述します。

この2つの処理を行い、環境変数【ASPNETCORE_ENVIRONMENT】の値を適切に定義することで、実行時の環境を判定する処理を省くことができます。
f:id:kinakomotitti:20190602173852p:plain

ものによっては、条件分岐が多くなりすぎて可読性が下がることにもなるので、こういう切り分けができるのは素敵だと思いました。



★環境ごとのアプリケーション設定

ASP.NET Coreでは、Web.configこそいなくなりましたが、似たようなものとして、「appsettings.json」があります。これにより、アプリ設定をすることができます。appsettingsを読み込んで利用するためには、①Startupクラスのコンストラクタで、ConfigurationBuilderクラスを使って、任意のファイル(ここではappsettings.json)から情報を取得し、構成情報のDOMを構築します。②構築したDOMは、Http処理のパイプライン構築時(StartupクラスのConfigureServicesメソッド)に、パイプラインに登録する必要があるため、クラスのフィールド変数として保存します。③StartupクラスのConfigureServicesメソッドで、パイプラインに①で構築したDOMを設定します。
上記の処理を実装に落とすと、以下のようになります。
参考)依存関係の注入に関してのドキュメント
docs.microsoft.com

    public class Startup
    {
        //②用のプロパティ
        public IConfigurationRoot configuration { get; set; }

        public Startup(IHostEnvironment env)
        {
           //①
            var dom = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json")//←こうすることで、任意の環境用の設定を読み込むことも可能
                .AddInMemoryCollection(new Dictionary<string, string>())
                .AddEnvironmentVariables()
                .Build();
            //②
            configuration = dom;
               
        }

        public void ConfigureServices(IServiceCollection services)
        {
            //③
            services.AddSingleton<IConfigurationRoot>(configuration);
        }
    }


★適切な設定方法について考えてみる

アプリケーションの設定情報を格納する方法についていくつか方法があるので、どこで、どうやって設定していくのかという観点で考えてみようと思います。
※アプリケーションをコンテナイメージにしたものをAzureにデプロイするシナリオをベースに考えています。

Appsettings 環境変数
✖設定の変更の度にイメージの変更が必要 〇値の変更に対して柔軟に対応ができる
〇配置するときには、開発環境、ステージング環境、本番環境のいずれかを選ぶだけでよくなるので、設定が楽 ✖コンテナデプロイ時のコマンドが設定値の分だけ長くなる
△AppSettingにパスワードを記述すると、構成管理上でバレバレになってしまう。 △CD環境設定に携われる人にパスワードがばれてしまう。

現在は、コードをGitHubでPublicにしているため、Azureなどのパスワードが第三者に知られることは極力避けたいなと思います。AzureDevOpsで設定しているCDについては、Privateレポジトリで設定しているので、第三者にCDパイプラインの設定内容を見られる心配はありません。
ということで、今回は、環境変数でアプリケーション設定を定義し、それを利用する方針で進めていこうと思います。

しかし、Appsettingに設定したほうが良い設定情報、環境変数で設定したほうが良い設定情報など、それぞれに何かしらの特徴があるはずなので、一概に、どこで管理すると決めずに、それぞれの特徴を見極めながら精査していくことを今後の課題としていきます。


まとめ

  • ASP.NET coreには、Web.configがなく、その代わりとして、環境変数やappsettings.jsonなどのような定義ファイルからアプリケーション設定を読み込む口が用意されている。
  • コンテナでアプリケーションをデプロイする場合は、設定する設定情報の特性を考慮して、適切な場所に配置するのが良い。(・・・が、自分の中ではその判断材料は十分に考えられていない( ゚Д゚))
  • 環境変数【ASPNETCORE_ENVIRONMENT】を切り替えることで、Development、Staging、Productionなどのホスト環境を切り替えることができる機構が備わっている。(従来のASP.NETでもあったような気もするけど…)


あと、読み込んだ設定情報は、DIシステムによってアプリケーション全体で利用することができるようになるので、そこらへんは今後手を動かしながら学んでいこうと思います(=゚ω゚)ノ