ASP.NET CoreをDocker使ってHerokuにデプロイ & PostgreSQLを使う
ASP.NET CoreはHerokuにデプロイできないものだと思っていたのですが、
HerokuのDockerコンテナを使ったデプロイ方法であれば、ASP.NET Coreも動くようなので試してみました。
Dockerを使用したHerokuへのデプロイ方法は2つあるようです。
- 自分でビルドしたDockerイメージをHerokuにデプロイする方法
heroku.yml
という設定ファイルを使ってHeroku上でDockerイメージをビルドし、デプロイする方法
1の方法は他の記事でも見ましたが、2の方法を書いているところがなさそうだったのでheroku.yml
を使った方法でデプロイしたいと思います。
環境
- .NET Core 3.1
- macOS Catalina 10.15.1
サンプルプロジェクト
Razor Pagesの公式チュートリアルを元に、Herokuデプロイ用のサンプルプロジェクトを用意しました。
このプロジェクトは、生徒の情報をCRUDできるRazor Pagesアプリです。
データベースはSQLiteが使われています。
このプロジェクトを使って進めていきますので、GitHubからリポジトリをクローンしておいてください。
Nugetパッケージの復元
クローンしたらまずはNugetパッケージを復元します。
ターミナル上でHerokuDeploySample.csproj
があるディレクトリまで移動し、以下のコマンドを実行。
dotnet restore
HerokuのPostgreSQLへ接続するための設定
Heroku上の本番環境ではPostgreSQLを使い、開発環境ではSQLiteを使うようにコードを修正していきます。
EF CoreのPostgreSQLライブラリをNugetから追加します。
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
追加できたら、環境によってSQLiteかPostgreSQLか接続を分けるようにStartup.cs
を修正します。
public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; Env = env; } public IConfiguration Configuration { get; } public IWebHostEnvironment Env { get; }
Startup
コンストラクタにIWebHostEnvironment
インターフェースを追加して、
Env
プロパティから環境情報にアクセスできるようにしました。
次に、Env
プロパティを使って接続先を分けます。
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); if (Env.IsDevelopment()) { services.AddDbContext<SchoolContext>(options => options.UseSQLite(Configuration.GetConnectionString("SchoolContext"))); } else { var uri = new Uri(Configuration["DATABASE_URL"]); var userInfo = uri.UserInfo.Split(":"); (var user, var password) = (userInfo[0], userInfo[1]); var db = Path.GetFileName(uri.AbsolutePath); var connStr = $"Host={uri.Host};Port={uri.Port};Database={db};Username={user};Password={password};Enlist=true"; services.AddDbContext<SchoolContext>(options => options.UseNpgsql(connStr)); } }
ConfigureServices
メソッド内でEnv.IsDevelopment
を使用して接続先を分岐させます。
if
のtureの部分が開発時(SQLite)で、falseの部分がHeroku(PostgreSQL)になっています。
このあと設定しますが、HerokuのPostgresアドオンを追加すると、DATABASE_URL
というPostgreSQLの接続先URLが入った環境変数がHerokuに追加されます。
上のConfiguration["DATABASE_URL"]
はその環境変数にアクセスし、接続先URLを取得するコードです。
これをそのままUseNpgsql
に渡しても、接続文字列のフォーマットが異なるため接続できません。
そのため接続先URLから情報を抜き出し、ライブラリに渡す用の文字列を作っています。
ロジックが結構書かれちゃってますが、サンプルなのでこのままで。
修正後のStartup.cs
全体のコード
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.EntityFrameworkCore; using HerokuDeploySample.Data; using System.IO; namespace HerokuDeploySample { public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; Env = env; } public IConfiguration Configuration { get; } public IWebHostEnvironment Env { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); if (Env.IsDevelopment()) { services.AddDbContext<SchoolContext>(options => options.UseSqlite(Configuration.GetConnectionString("SchoolContext"))); } else { var uri = new Uri(Configuration["DATABASE_URL"]); var userInfo = uri.UserInfo.Split(":"); (var user, var password) = (userInfo[0], userInfo[1]); var db = Path.GetFileName(uri.AbsolutePath); var connStr = $"Host={uri.Host};Port={uri.Port};Database={db};Username={user};Password={password};Enlist=true"; services.AddDbContext<SchoolContext>(options => options.UseNpgsql(connStr)); } } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } } }
Dockerfileを作成
次に、Heroku上で動かすコンテナの元となるDockerfileを作ります。
Startup.cs
と同じディレクトリに以下の内容のDockerfileを作成してください。
※2020/11/26 追記
DockerfileにEXPOSE
を書いていましたがherokuではサポートしておらず、使用するポートは$PORT
に渡されるので不要でした。
また、dotnet publish
でrestore
もbuild
も暗黙的に実行されるため不要なコマンド行を削除しました。
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS publish WORKDIR "/src/HerokuDeploySample" COPY . . RUN dotnet publish "HerokuDeploySample.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . CMD ASPNETCORE_URLS=http://*:$PORT dotnet HerokuDeploySample.dll
このDockerfileはVisual StudioでDockerfile追加したときにデフォルトで書かれている内容を、Heroku用に書き換えたものです。
参考: Deploy ASP.NET Core 3.1 Web API to Heroku with Docker – Jakub Wajs
heroku.yml
heroku.ymlとは
Dockerを使ったHerokuアプリを構築するための設定ファイルです。
Heroku上でイメージをビルドでき、コマンドや環境変数なども設定できるみたいです。
heroku.ymlを作成
GitHubからクローンしたサンプルプロジェクトのルートに以下の内容のheroku.ymlを作ります。
ディレクトリ構造
root/ ├ HerokuDeploySample/ │ ├ Data/ │ ├ Models/ │ ├ ...../ │ └ ....../ └ heroku.yml
heroku.yml
build: docker: web: HerokuDeploySample/Dockerfile
中身は簡単で、Dockerfileの場所を指定してあげるだけでOKです。
Herokuにはリポジトリをプッシュしてデプロイするので、
ここまでの修正をコミットしておいてください。
Herokuへデプロイ
Herokuアプリを構築してデプロイします。
Herokuの操作はすべてHeroku CLIを使って行います。
ターミナルなどではカレントディレクトリをプロジェクトのルート(heroku.ymlを作った場所)にしておきます。
1. Herokuへログイン
heroku login
2. Herokuアプリの作成
デプロイ先が作られ、gitのリモートにheroku
が追加されます。
heroku create
3. Postgresアドオンを追加
HerokuアプリでPostgreSQLを使うためアドオンを追加します。
これで環境変数DATABASE_URL
が追加され、HerokuでPostgreSQLを使う準備ができました。
環境変数の中身はコマンドで見れます。
heroku addons:create heroku-postgresql:hobby-dev # DATABASE_URLの中身を表示するコマンド # heroku config:get DATABASE_URL
4. アプリをDockerコンテナで動かすように設定
heroku stack:set container
5. Herokuへリポジトリをプッシュ
git push heroku master
プッシュするとHeroku上で、追加したDockerfileを元にイメージのビルドが始まります。
しばらく待ち、「remote: Verifying deploy... done.」と表示されたら完了です。
heroku open
コマンドでアプリを開いて、動いているか確認しましょう。
成功していれば、/Students
に生徒情報が一覧表示されています。
PCにPostgreSQLがインストールされていれば、Heroku上のデーテベースもコマンドから中身を確認できます。
heroku pg:psql => SELECT * FROM "Student"; ID | LastName | FirstMidName ----+-----------+-------------- 1 | Alexander | Carson 2 | Alonso | Meredith 3 | Anand | Arturo 4 | Barzdukas | Gytis 5 | Li | Yan 6 | Justice | Peggy 7 | Norman | Laura 8 | Olivetto | Nino (8 rows)
修正後のプロジェクト
同じリポジトリのafterブランチに修正後のプロジェクトがあるのでこちらも参照してください。
GitHub - eiken7kyuu/AspNetCoreHeroku at after
その他参考にした記事
ASP.NET Core 3.1のREST-APIをVisualStudio2019とDockerでHerokuにデプロイする手順 - Qiita