13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS EC2にsupabaseをdockerでself-hostingする

Last updated at Posted at 2023-02-24

はじめに

Firebaseの代替と謳われているsupabaseをよく使ってwebアプリを構築していますが、
Web版の無料枠では2つのプロジェクトまでしか作成することができないため、
AWSのEC2上にself-hostingしました
今回はシングル構成ですが、docker swarmなどを利用することで冗長構成にすることも可能です

1. EC2の作成

  1. AWS management consoleを開く

  2. EC2のページを開く

  3. 画面右上の「インスタンスを起動」ボタンをクリック
    以下設定項目を設定

    • 名前
      • 自由に設定
    • OSイメージ
      • AmazonLinux
    • インスタンスタイプ
      • 自由に設定
      • 無料枠のt2.microでも動くかもしれませんが、あまりにもストレスが溜まったのでt3a.smallにしました。
    • キーペア
      • 新しいキーペアを生成
        • キーペア名:自由に設定
        • 他はそのままキーペア生成
    • セキュリティグループ
      • あとで設定するため、
        「既存のセキュリティグループを設定」 > 空のまま
    • ストレージ
      • 無料枠の限界30GiBまで拡張
  4. 「インスタンスを起動」

2. セキュリティーグループを作成する。

  1. 左側サイドメニューから、「ネットワーク & セキュリティ」 > 「セキュリティグループ」を選択する

  2. 画面右上「セキュリティグループを作成」をクリック
    以下設定項目を設定

    • セキュリティグループ名
      • 自由に設定
    • 説明
      • 自由に設定
    • VPC
      • 先ほどEC2を構築したVPCを選択
    • インバウンドルール
    タイプ プロトコル ポート範囲 ソース 説明
    カスタムTCP TCP 8000 Anywhere-IPv4 0.0.0.0/0 自由に設定
    ssh TCP 22 Anywhere-IPv4 0.0.0.0/0 自由に設定
    HTTPS TCP 443 Anywhere-IPv4 0.0.0.0/0 自由に設定
    カスタムTCP TCP 3000 Anywhere-IPv4 0.0.0.0/0 自由に設定
    カスタムTCP TCP 5432 Anywhere-IPv4 0.0.0.0/0 自由に設定

    0.0.0.0/0の部分を自社のIPに設定することでIP制限をかけることができます

    • タグ
      • 自由に設定
  3. 先ほど作成したEC2インスタンスを選択

  4. 「アクション」 > 「セキュリティ」 > 「セキュリティグループを変更」

  5. 今作ったセキュリティグループを設定

スクリーンショット 2023-02-24 23.17.59.png

このようになっていたらOK

3. EC2に接続し、supabaseを起動する

  1. terratermなどのツールや、instance connectを利用してEC2にssh接続をする

    • ファイルを操作するのでVScodeで入っちゃうのがおすすめ
    • 楽なのはinstance connectで、画面上部の「接続」ボタンから接続したらOK
  2. 以下の通りにコマンドを実行

    $ sudo yum update
    ・
    ・
    ・
    Upgrade  4 Packages
    
    Total download size: 34 M
    Is this ok [y/d/N]: y
    ・
    ・
    ・
    Complete!
    
    # docker install
    $ sudo yum install docker -y
    $ sudo service docker start
    
    # docker-compose install
    # version 1.29.2の場合
    # versionは好きなものを入れる
    $ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    $ sudo chmod +x /usr/local/bin/docker-compose
    $ sudo chmod 666 /var/run/docker.sock
    
    # Gitのinstall
    $ sudo yum install git -y
    
    # supabase リポジトリのcloen
    $ git clone --depth 1 https://github.com/supabase/supabase
    $ cd supabase/docker
    $ cp .env.example .env
    
  3. .envの修正

    .env内に出てくるlocalhostの値を全てEC2のpublicIPに置き換える
    私の環境では4箇所ありました。

  4. supabaseのセキュリティセットアップ

    1. 公式のガイドに従いJWT Generatorを利用して
      jwt secretと、anon keyservice keyを作成する

    2. 次の箇所を書き換える

      .env:
          JWT_SECRET - jwt secret
          ANON_KEY - anon key
          SERVICE_ROLE_KEY - service key
      
      volumes/api/kong.yml
          anon - anon key
          service_role - service key
      
  5. docker-compose.ymlの微修正(いらないかも)

    私のPC上ではこれまでのセットアップで動いたのですが、Amazon linux上ではコンテナがうまく立ち上がらなかったので、脳死気味になるが、以下の設定を全てのサービスに追加する

     privileged: true
    

    例)

      rest:
        container_name: supabase-rest
        image: postgrest/postgrest:v10.1.2
        depends_on:
          db: # Disable this if you are using an external Postgres database
            condition: service_healthy
        restart: unless-stopped
        environment:
          PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
          PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
          PGRST_DB_ANON_ROLE: anon
          PGRST_JWT_SECRET: ${JWT_SECRET}
          PGRST_DB_USE_LEGACY_GUCS: "false"
        privileged: true   # 追加
    

    最後にdocker-compose.ymlファイルを載せます

  6. お待ちかねのサーバー起動(初回はimageのpullに時間がかかります。)

    $ docker-compose up -d
    
  7. http://public ip:3000を開くと、、、

スクリーンショット 2023-02-25 0.23.38.png

やったー!

4. storageをs3に変更する

  • せっかくAWS上で動かしているので、storageはs3を使いましょう〜
  • EBSも圧迫しますしね
  1. s3を作成する

    名前は自由。他はデフォルトのままでOKです。

  2. docker-compose.ymlファイルのstorageの環境変数を変更

          FILE_SIZE_LIMIT: 52428800
          STORAGE_BACKEND: s3       # 変更
          FILE_STORAGE_BACKEND_PATH: /var/lib/storage
          TENANT_ID: supabase       # 変更(なんでもいいです。変えなくてもいいです。)
          REGION: ap-northeast-1    # s3のリージョン指定
          GLOBAL_S3_BUCKET: *****   # s3バケット名を指定
          ENABLE_IMAGE_TRANSFORMATION: "true"
          IMGPROXY_URL: http://imgproxy:5001
    
  3. 環境再起動

    docker-compose restart
    

これでstorage機能の保存先がs3になります。

5. 参考

開く: docker-compose.yaml
docker-compose.yml
# Usage
#   Start:          docker compose up
#   With helpers:   docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
#   Stop:           docker compose down
#   Destroy:        docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans

version: "3.8"

services:
  studio:
    container_name: supabase-studio
    image: supabase/studio:20230216-e731b77
    restart: unless-stopped
    healthcheck:
      test: [ "CMD", "node", "-e", "require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" ]
      timeout: 5s
      interval: 5s
      retries: 3
    ports:
      - ${STUDIO_PORT}:3000/tcp
    environment:
      STUDIO_PG_META_URL: http://meta:8080
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

      DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}
      DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}

      SUPABASE_URL: http://kong:8000
      SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
      SUPABASE_ANON_KEY: ${ANON_KEY}
      SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
    privileged: true

  kong:
    container_name: supabase-kong
    image: kong:2.8.1
    restart: unless-stopped
    ports:
      - ${KONG_HTTP_PORT}:8000/tcp
      - ${KONG_HTTPS_PORT}:8443/tcp
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
      # https://github.com/supabase/cli/issues/14
      KONG_DNS_ORDER: LAST,A,CNAME
      KONG_PLUGINS: request-transformer,cors,key-auth,acl
      KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
      KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
    volumes:
      - ./volumes/api:/var/lib/kong:ro
    privileged: true

  auth:
    container_name: supabase-auth
    image: supabase/gotrue:v2.47.0
    depends_on:
      db: # Disable this if you are using an external Postgres database
        condition: service_healthy
    healthcheck:
      test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9999/health" ]
      timeout: 5s
      interval: 5s
      retries: 3
    restart: unless-stopped
    environment:
      GOTRUE_API_HOST: 0.0.0.0
      GOTRUE_API_PORT: 9999
      API_EXTERNAL_URL: ${API_EXTERNAL_URL}

      GOTRUE_DB_DRIVER: postgres
      GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}

      GOTRUE_SITE_URL: ${SITE_URL}
      GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
      GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}

      GOTRUE_JWT_ADMIN_ROLES: service_role
      GOTRUE_JWT_AUD: authenticated
      GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
      GOTRUE_JWT_EXP: ${JWT_EXPIRY}
      GOTRUE_JWT_SECRET: ${JWT_SECRET}

      GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
      GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
      # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true
      # GOTRUE_SMTP_MAX_FREQUENCY: 1s
      GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
      GOTRUE_SMTP_HOST: ${SMTP_HOST}
      GOTRUE_SMTP_PORT: ${SMTP_PORT}
      GOTRUE_SMTP_USER: ${SMTP_USER}
      GOTRUE_SMTP_PASS: ${SMTP_PASS}
      GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME}
      GOTRUE_MAILER_URLPATHS_INVITE: ${MAILER_URLPATHS_INVITE}
      GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION}
      GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY}
      GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE}

      GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}
      GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}
      MFA_ENABLED: ${MFA_ENABLED}
    privileged: true

  rest:
    container_name: supabase-rest
    image: postgrest/postgrest:v10.1.2
    depends_on:
      db: # Disable this if you are using an external Postgres database
        condition: service_healthy
    restart: unless-stopped
    environment:
      PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
      PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
      PGRST_DB_ANON_ROLE: anon
      PGRST_JWT_SECRET: ${JWT_SECRET}
      PGRST_DB_USE_LEGACY_GUCS: "false"
    privileged: true

  realtime:
    container_name: realtime-dev.supabase-realtime
    image: supabase/realtime:v2.5.1
    depends_on:
      db: # Disable this if you are using an external Postgres database
        condition: service_healthy
    healthcheck:
      test: [ "CMD", "bash", "-c", "printf \\0 > /dev/tcp/localhost/4000" ]
      timeout: 5s
      interval: 5s
      retries: 3
    restart: unless-stopped
    environment:
      PORT: 4000
      DB_HOST: ${POSTGRES_HOST}
      DB_PORT: ${POSTGRES_PORT}
      DB_USER: supabase_admin
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      DB_NAME: ${POSTGRES_DB}
      DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
      DB_ENC_KEY: supabaserealtime
      API_JWT_SECRET: ${JWT_SECRET}
      FLY_ALLOC_ID: fly123
      FLY_APP_NAME: realtime
      SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
      ERL_AFLAGS: -proto_dist inet_tcp
      ENABLE_TAILSCALE: "false"
      DNS_NODES: "''"
    command: >
      sh -c "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server"
    privileged: true

  storage:
    container_name: supabase-storage
    image: supabase/storage-api:v0.28.2
    depends_on:
      db: # Disable this if you are using an external Postgres database
        condition: service_healthy
      rest:
        condition: service_started
      imgproxy:
        condition: service_started
    healthcheck:
      test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/status" ]
      timeout: 5s
      interval: 5s
      retries: 3
    restart: unless-stopped
    environment:
      ANON_KEY: ${ANON_KEY}
      SERVICE_KEY: ${SERVICE_ROLE_KEY}
      POSTGREST_URL: http://rest:3000
      PGRST_JWT_SECRET: ${JWT_SECRET}
      DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
      FILE_SIZE_LIMIT: 52428800
      STORAGE_BACKEND: s3
      FILE_STORAGE_BACKEND_PATH: /var/lib/storage
      TENANT_ID: supabase
      # TODO: https://github.com/supabase/storage-api/issues/55
      REGION: ap-northeast-1
      GLOBAL_S3_BUCKET: supabase-sample-storage
      ENABLE_IMAGE_TRANSFORMATION: "true"
      IMGPROXY_URL: http://imgproxy:5001
    volumes:
      - ./volumes/storage:/var/lib/storage
    privileged: true

  imgproxy:
    container_name: supabase-imgproxy
    image: darthsim/imgproxy:v3.11
    healthcheck:
      test: [ "CMD", "imgproxy", "health" ]
      timeout: 5s
      interval: 5s
      retries: 3
    environment:
      IMGPROXY_BIND: ":5001"
      IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
      IMGPROXY_USE_ETAG: "true"
    volumes:
      - ./volumes/storage:/var/lib/storage
    privileged: true

  meta:
    container_name: supabase-meta
    image: supabase/postgres-meta:v0.60.7
    depends_on:
      db: # Disable this if you are using an external Postgres database
        condition: service_healthy
    restart: unless-stopped
    environment:
      PG_META_PORT: 8080
      PG_META_DB_HOST: ${POSTGRES_HOST}
      PG_META_DB_PORT: ${POSTGRES_PORT}
      PG_META_DB_NAME: ${POSTGRES_DB}
      PG_META_DB_USER: supabase_admin
      PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
    privileged: true

  # Comment out everything below this point if you are using an external Postgres database
  db:
    container_name: supabase-db
    image: supabase/postgres:15.1.0.42-rc2
    healthcheck:
      test: pg_isready -U postgres -h localhost
      interval: 5s
      timeout: 5s
      retries: 10
    command:
      - postgres
      - -c
      - config_file=/etc/postgresql/postgresql.conf
      - -c
      - log_min_messages=fatal # prevents Realtime polling queries from appearing in logs
    restart: unless-stopped
    ports:
      # Pass down internal port because it's set dynamically by other services
      - ${POSTGRES_PORT}:${POSTGRES_PORT}
    environment:
      POSTGRES_HOST: /var/run/postgresql
      PGPORT: ${POSTGRES_PORT}
      POSTGRES_PORT: ${POSTGRES_PORT}
      PGPASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      PGDATABASE: ${POSTGRES_DB}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/realtime.sql
      - ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/roles.sql
      # Must be superuser to enable pg_net extension
      - ./volumes/db/webhooks.sql:/etc/postgresql.schema.sql
      - ./volumes/db/data:/var/lib/postgresql/data
    privileged: true
13
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?