0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

dtm機材用のまとめサイトを作ったけど

0
Posted at

PHPアプリをRenderにデプロイし、Supabase(PostgreSQL)へ接続する

PHPで作成したDTM製品検索サイトを、RenderにDockerでデプロイし、SupabaseをPostgreSQLのデータベースとして利用する構成を試しました。

この記事では、構成とデプロイ時につまずいた点をまとめます。

使用した構成

  • PHP 8.2
  • Apache
  • Docker
  • Render
  • Supabase(PostgreSQL)
  • PDO(pdo_pgsql)

Dockerfile

PHPからPostgreSQLへ接続するため、pdo_pgsqlをインストールします。Renderで利用するポートに合わせ、Apacheも10000で待ち受けるようにしています。

FROM php:8.2-apache

RUN apt-get update \
    && apt-get install -y --no-install-recommends libpq-dev \
    && docker-php-ext-install pdo_pgsql \
    && sed -ri 's/^Listen 80$/Listen 10000/' /etc/apache2/ports.conf \
    && sed -ri 's/<VirtualHost \*:80>/<VirtualHost *:10000>/' /etc/apache2/sites-available/000-default.conf \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /var/www/html
COPY . /var/www/html/

EXPOSE 10000

注意点:DockerfileにはPHPコードを書きません。 PHPの接続処理は、PHPファイルとして別に保存します。

PostgreSQLへの接続

config/db.phpで環境変数を読み込み、PDOで接続します。

<?php

class Database
{
    public static function getConnection(): PDO
    {
        $host = getenv('DB_HOST');
        $user = getenv('DB_USER');
        $password = getenv('DB_PASSWORD');

        if (!$host || !$user || $password === false) {
            throw new RuntimeException('データベース接続用の環境変数が設定されていません。');
        }

        $dsn = sprintf(
            'pgsql:host=%s;port=%s;dbname=%s;sslmode=require',
            $host,
            getenv('DB_PORT') ?: '5432',
            getenv('DB_NAME') ?: 'postgres'
        );

        return new PDO($dsn, $user, $password, [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    }
}

アプリ側では、次のように接続を取得します。

$pdo = Database::getConnection();

Renderの環境変数

Renderのサービスに、Supabaseの接続情報を環境変数として設定します。

変数名 設定する値
DB_HOST ホスト名のみ
DB_PORT Supabaseが指定するポート番号
DB_NAME データベース名
DB_USER データベースユーザー名
DB_PASSWORD データベースパスワード

DB_HOSTに接続URI全体を入れないようにします。たとえば、postgresql://や:5432、port=などは含めず、ホスト名だけを設定します。

接続先の種類やポートは、Supabaseの接続情報でRenderから利用できるものを確認してください。パスワードなどの秘密情報は、ソースコードや記事に書かないようにします。

PostgreSQL用のテーブル定義

今回のDBでは、次のテーブルを使います。

  • categories:カテゴリ
  • brands:メーカー・ブランド
  • products:製品
  • tags:タグ
  • product_tag:製品とタグの関連
  • external_media:YouTubeなどのデモメディア

init.sqlはPostgreSQLの型や構文に合わせます。たとえば、IDにはGENERATED BY DEFAULT AS IDENTITY、JSONデータにはJSONBを使っています。

また、MySQLのSET NAMES、FOREIGN_KEY_CHECKS、ENGINE=InnoDBなどはPostgreSQLでは使えません。MySQL向けSQLが残っていないか確認してから、SupabaseのSQL Editorで実行します。

デプロイでつまずいた点

1. DockerfileにPHPコードが混入した

ビルドログに次のエラーが出ました。

Dockerfile parse error ... unknown instruction: <?php

Dockerfileの先頭にPHPの<?phpがあると、DockerはPHPコードをDockerfileの命令として解釈してしまいます。

Dockerfileとconfig/db.phpを別ファイルに分けて解決しました。

2. DB_HOSTの設定形式が誤っていた

次のようなエラーが発生しました。

could not translate host name "port=5432" to address

DB_HOSTにホスト名以外の文字列が含まれていると、名前解決に失敗します。環境変数を項目ごとに分け、ホスト名とポート番号を別々に設定します。

3. アプリは起動したが、テーブルがなかった

RenderのビルドとWebサービス起動は成功しても、Supabase側にテーブルが自動作成されるわけではありません。

次のエラーが出た場合は、接続先DBにテーブルが存在するか確認します。

relation "products" does not exist

SupabaseのSQL EditorでPostgreSQL用のinit.sqlを実行したあと、テーブルの存在を確認します。

SELECT to_regclass('public.products');

public.productsが返れば、少なくともそのテーブルは存在します。何も返らない場合は、スキーマ作成が未実行か、接続先が異なる可能性があります。

デプロイ後の確認

RenderのビルドログでDockerイメージの作成が成功し、Webサービスが起動したことを確認します。その後、実際にDBを使うページを開き、データが取得できるか確認します。

HTTPのヘルスチェックが200でも、DB接続やテーブル作成まで成功しているとは限りません。アプリの画面と実行ログの両方を確認する必要があります。

まとめ

今回の構成で特に重要だった点は次のとおりです。

  1. DockerfileとPHPコードを別ファイルに分ける
  2. PHPイメージにpdo_pgsqlをインストールする
  3. Renderの環境変数はホスト・ポートなどに分けて設定する
  4. MySQL用SQLをPostgreSQL用に修正する
  5. Renderへのデプロイとは別に、Supabaseでスキーマを作成する
  6. ビルド成功だけでなく、DBを使う画面も確認する

Renderのサービスが起動していても、Supabaseのテーブル作成が済んでいなければアプリのDB処理は失敗します。デプロイとデータベース初期化を別の作業として確認することが大切です。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?