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接続やテーブル作成まで成功しているとは限りません。アプリの画面と実行ログの両方を確認する必要があります。
まとめ
今回の構成で特に重要だった点は次のとおりです。
- DockerfileとPHPコードを別ファイルに分ける
- PHPイメージに
pdo_pgsqlをインストールする - Renderの環境変数はホスト・ポートなどに分けて設定する
- MySQL用SQLをPostgreSQL用に修正する
- Renderへのデプロイとは別に、Supabaseでスキーマを作成する
- ビルド成功だけでなく、DBを使う画面も確認する
Renderのサービスが起動していても、Supabaseのテーブル作成が済んでいなければアプリのDB処理は失敗します。デプロイとデータベース初期化を別の作業として確認することが大切です。