はじめに

少し前に話題になっていた Supabase を最近になって知りました。
SupabaseはFirebaseの代替手段と謳っており、Postgresのデータベースや認証、ストレージなどの機能を提供するプラットフォームのことです。

個人的にPosrgresデータベースを無料枠で使用することができる点が素晴らしいと思っています。
FirebaseのNoSQLもいいですが、EntityFrameworkとの相性は悪くて開発体験がよくないですから、あまり使いたくはありません。また、Cloud SQLや RDSのようなフルマネージドデータベースを使用するという選択肢もありますが、いかんせんコストが高いので、個人開発には向きません。

そんなこんなでCloudRun + Supabaseを使用すれば、実質無料で.NETの環境を作ることができるのではないかと思って、試してみました。

インフラ

  • Cloud Run
  • Supabase (Postgres)

環境

  • Docker
  • .NET MVC Identity
  • Postgres

Supabaseでプロジェクト作成

ログインは割愛します。

まず、プロジェクトを作成します。(データベースパスワードは控えておく)

スクリーンショット2023-03-31002638.png

プロジェクトを作成してプロジェクト詳細に入ったら、左メニューの設定(Project Settings)>Databaseに移動します。

Connection stringの.NETタブを選択すると.NETアプリ用の接続設定を取得できます。
この時点でSQLクライアントソフトから接続確認ができました。

スクリーンショット2023-03-31010948.png

.NET Core Identity環境の作成

.NETの環境を作成します。
開発環境はVSCodeでDev Container拡張機能を使用します。

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS runtime

WORKDIR /app

COPY ./src .

RUN apt-get update && apt-get install -y \
    git \
    vim

RUN dotnet tool install --global dotnet-ef --version 7.0
RUN dotnet tool install --global dotnet-aspnet-codegenerator --version 7.0

ENV PATH $PATH:/root/.dotnet/tools
ENV TZ Asia/Tokyo
docker-compose.yml
version: '3.7'

services:
  supabase_web:
    container_name: "supabase_web"
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "50001:8080"
    working_dir: /app/
    tty: true
    volumes:
      - ./src/:/app/

  supabase_db:
    container_name: "supabase_db"
    image: postgres:13-bullseye
    ports:
      - "55432:5432"
    volumes:
      - .dbstore:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_DB=postgres
      - POSTGRES_PASSWORD=A9AbbsJiNXUA

mkdir srcでsrcフォルダを作成したあとに、コマンドパレットを開き、>Dev Containers: Open Folder in Container...を実行してコンテナを起動します。

次に、下記コマンドで.NET MVCテンプレートを作成します。

cd /
dotnet new mvc --auth Individual -uld -o app -n SupabaseTest

併せて、gitignoreファイルも作成します。

cd /app && dotnet new gitignore

作成したら、下記コマンドでPostgresのNugetパッケージをインストールします。

dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL

PostgreSQLに接続できるようにProgram.csファイルを編集します。

Program.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SupabaseTest.Data;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseUrls("http://0.0.0.0:8080");

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseNpgsql(connectionString));

// 略 //

また、appSettings.Develop.jsonを以下のように変更します。

appSettings.Develop.json
{
  "DetailedErrors": true,
  "ConnectionStrings": {
    "DefaultConnection": "Host= supabase_db;Port=5432;Database=postgres;Username=postgres;Password=A9AbbsJiNXUA"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

次にマイグレーション関連を整理します。

今のままではマイグレーションがSQL Server用のものとなっており、updateを行ってもエラーが発生します。そのため、下記コマンドで初期マイグレーションを再発行します。

cd /app
rm Data/Migrations/*.cs
# マイグレーションの発行
dotnet ef migrations add InitialCreate --context ApplicationDbContext --output-dir Data/Migrations

# マイグレーションをデータベースに反映
dotnet ef database update --context ApplicationDbContext

次にデバッグできるようにVSCodeの左メニューから、「実行とデバッグ」を開き、「launch.jsonファイルを作成」し、.NET 5+ .NET MVCを選択してlaunch.jsonを自動生成します。
また、VSCodeの拡張機能で「C# ms-dotnettools.csharp」をインストールしておきます。

launch.jsonファイルが自動生成できたら、F5キーを押してデバッグを開始します。

デバッグが実行出来たら、http://localhost:50001/ でアクセスできます。

データベースに接続できているか確認するために適当にユーザーを登録して、「Click here to confirm your account」をクリックします。その後、ログインができていたらDBは接続できています。

これでローカルで開発できる環境ができました。

本番環境にマイグレーション

CloudRunにデプロイした際にSupabaseのPostgreに接続できるようにする必要があります。

まず、Postgresの接続情報を記載したappsettings.Production.jsonを作成します。

appsettings.Production.json
{
  "DetailedErrors": false,
  "ConnectionStrings": {
    "DefaultConnection": "User Id=postgres;Password=XXXXXXXX;Server=db.xxxxxxxxxxxxxxxxxx.supabase.co;Port=5432;Database=postgre"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

次に下記のコマンドでsupabaseのデータベースを更新します。

cd /app
export ASPNETCORE_ENVIRONMENT="Production"
dotnet ef database update
export ASPNETCORE_ENVIRONMENT="Development"

Cloud Runにデプロイ

まず、Cloud Runとは、サーバーレスのプラットフォームで、コンテナベースのWebアプリケーションを実行させることができます。

要はDockerなどのコンテナからアプリケーションを実行するため、任意の言語の環境を使用することができます。また、Dockerベースの開発をする場合、ローカルの環境とCloud Runの環境を同じ環境にすることができるメリットがあります。

先ほど作成した.NETアプリケーションをCloud Runにデプロイします。

GCPのロール変更

GCPのサービスアカウントからCloudRunにデプロイするため、権限を付与する必要があります。

  1. Cloud Buildに移動して、左メニューの「設定」に移動
  2. 「Cloud Run」のステータスを有効に変更
  3. モーダルの「すべてのサービス アカウントにアクセス権を付与」

Cloud buildでイメージをビルドする

gcloud sdkがローカルにインストールされている前提で話します。

Cloud Runでデプロイするためにはデプロイするコンテナのイメージが必要なので、Cloud Buildを使用してCloud Registryにイメージを保存します。

まず、デプロイ用のDocker設定ファイルであるCloudRun.Dockerfileを作成します。

注意点としては、CloudRunにデプロイする場合は 必ず8080ポートを公開することです。そうしないとデプロイ時にエラーが発生します。

CloudRun.Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build

WORKDIR /app
COPY ./src .

RUN dotnet publish -c Release -o dist

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime

WORKDIR /app
COPY --from=build /app/dist .

ENV TZ Asia/Tokyo
ENV ASPNETCORE_ENVIRONMENT Production

ENV PORT=8080
EXPOSE 8080

ENTRYPOINT ["dotnet", "SupabaseTest.dll"]

次にビルド設定のcloudbuild.ymlを作成します。

cloudbuild.yml
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/PROJECT_ID/IMAGE', '-f', 'CloudRun.Dockerfile', '.']
# Push the container image to Container Registry
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/PROJECT_ID/IMAGE']
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  entrypoint: gcloud
  args: ['run', 'deploy', 'SERVICE_NAME', '--image', 'gcr.io/PROJECT_ID/IMAGE', '--region', 'REGION', '--allow-unauthenticated']
images:
- gcr.io/PROJECT_ID/IMAGE

cloudbuild.ymlの各項目を変換しておいてください。

変換前 内容
PROJECT_ID プロジェクトのID
IMAGE Contaier Registoryに保存する名前
SERVICE_NAME デプロイサービスの名前
REGION デプロイ先のリージョン
東京ならasia-northeast1

デプロイする

ローカル環境でCloudRun.Dockerfileのあるフォルダで以下のコマンドを実行します。

gcloud builds submit ./ --config cloudbuild.yml

特にエラーがなければデプロイの成功です。

認証の機能が使用できていたので、データベースにも接続できていますね!

スクリーンショット2023-04-01001455.png

おわりに

実際にCloud RunとSupabaseを使用することで.NETの環境を限りなく安くなるように作成できました。ただ、今回作成した.NETアプリは、Cloud Runの特性上、十数分程度でログインセッションが切れてしまうため、セッションをインメモリからデータベースに保存するなどの対応が必要になりそうです。

Supabaseのプロジェクトは無料枠で2つのみなので、数を作成できませんが、個人開発にはもっていこいのサービスだと思います。

Cloud Runなので.NET Coreの環境にとどまらず、頑張ればWordpressの環境も作成できるんじゃないかと思います。ほぼ無料でWordpress環境は夢があっていいですね。

さいごに、サクッと記事が書けるだろうと思って書き始めましたが、思ったより基本的な説明に時間をとってしまう形になってしまいました。記事を作成するのは難しいですね...