2025-09-16

laravel newしたFrankenPHPなアプリケーションをCloud Runでホスティングするまで

FrankenPHPを laravel new して最速で使う方法をメモ

とりあえず laravel new

laravel new frankenphp

フロントエンドは React を選択

ローカル環境のDockerを整備

とりあえずDockerfileやcompose.ymlを作る

FROM dunglas/frankenphp:1-php8.4-bookworm

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV DEBCONF_NOWARNINGS=yes

RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

RUN apt-get update \
  && apt-get install -y --no-install-recommends \
    locales strace vim git less unzip

RUN install-php-extensions @composer xdebug

RUN curl -sLS https://deb.nodesource.com/setup_24.x | bash - \
  && apt-get install -y nodejs


RUN sed -i -e 's/# \(ja_JP.UTF-8\)/\1/' /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8

ENV LANG=ja_JP.UTF-8

RUN cp /usr/local/etc/php/php.ini-development /usr/local/etc/php/php.ini

必要に応じて pdo_mysql とか intl とかをインストールする。

compose.yml

services:
  app:
    build: .
    volumes:
      - .:/app
    ports:
      - 8080:80
    environment:
      SERVER_NAME: ':80'

SERVICE_NAME: ':80' がポイント。 :80を指定するとauto https(自動で証明書発行したりする機能)が動かなくなる。

TLSを利用する場合

mkcert -install
mkcert {domain_name}
services:
  app:
    build: .
    volumes:
      - .:/app
    ports:
      - 443:443
    environment:
      SERVER_NAME: '{domain_name}:443'
      CADDY_SERVER_EXTRA_DIRECTIVES: "tls /app/{domain_name}.pem /app/{domain_name}-key.pem"

DNSで{domain_name}を127.0.0.1に向ける

Google Cloud Runにデプロイしてみる

デプロイ用Docker

FROM dunglas/frankenphp:1-php8.4-bookworm

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV DEBCONF_NOWARNINGS=yes

RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

RUN apt-get update \
  && apt-get install -y --no-install-recommends \
    locales unzip

RUN install-php-extensions @composer

RUN curl -sLS https://deb.nodesource.com/setup_24.x | bash - \
  && apt-get install -y nodejs

RUN sed -i -e 's/# \(ja_JP.UTF-8\)/\1/' /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8

ENV LANG=ja_JP.UTF-8

RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini

COPY . /app

WORKDIR /app
RUN composer install --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader
RUN npm ci
RUN npm run build

デプロイはこんな感じで

APP_NAME={app_name}
REGION=asia-northeast1
PROJECT_ID={project_id}
REPOSITORY=${REGION}-docker.pkg.dev/${PROJECT_ID}/{artifact_registry}/{repository}

docker build --platform linux/amd64 -f Dockerfile.prod . --tag ${REPOSITORY} 
docker push ${REPOSITORY}
gcloud run deploy ${APP_NAME} --project ${PROJECT_ID} \
 --image ${REPOSITORY}:latest \
 --region ${REGION} \
 --set-env-vars=SERVER_NAME=:80,APP_ENV=production \
 --port=80

そのままだとassetの配信がhttpになるので、APP_ENV=productionのときはhttpsを強制するようにする

// app/Providers/AppServiceProvider.php
if (app()->isProduction()) {
    \URL::forceScheme('https');
}

FrankenPHPとDockerfileとCaddyfile

FrankenPHPはCaddyというサーバーを使って動いている。CaddyはCaddyfileという設定ファイルがあり、FrankenPHPのCaddyfileはこんな感じになっている。{$xxx} の部分は環境変数で設定できる。

{
	skip_install_trust

	{$CADDY_GLOBAL_OPTIONS}

	frankenphp {
		{$FRANKENPHP_CONFIG}
	}
}

{$CADDY_EXTRA_CONFIG}

{$SERVER_NAME:localhost} {
	root {$SERVER_ROOT:public/}
	encode zstd br gzip

	{$CADDY_SERVER_EXTRA_DIRECTIVES}

	php_server {
		#worker /path/to/your/worker.php
	}
}

import Caddyfile.d/*.caddyfile

https://github.com/php/frankenphp/blob/7754abcbd0814029aed7eae65d270df9cca67cfa/caddy/frankenphp/Caddyfile

SERVER_NAMEでホスティングするドメインやポートが決定される。そしてこれによってauto httpsという証明書の自動発行処理が動くかどうかも決まる。
https://caddyserver.com/docs/caddyfile/concepts#addresses

:80 という設定はホスト名のバリデーションは行わず、httpでホスティングする(証明書の自動発行は行わない)という設定。{domain_name}:443 はhttpsでホスティングするが証明書の自動発行は行わない、という設定。

php_server がFrankenPHPが拡張している部分で、実体は以下のような設定と等価になる。

route {
	# Add trailing slash for directory requests
	@canonicalPath {
		file {path}/index.php
		not path */
	}
	redir @canonicalPath {path}/ 308
	# If the requested file does not exist, try index files
	@indexFiles file {
		try_files {path} {path}/index.php index.php
		split_path .php
	}
	rewrite @indexFiles {http.matchers.file.relative}
	# FrankenPHP!
	@phpFiles path *.php
	php @phpFiles
	file_server
}

よく見そうなapacheやnginxの設定みたいな感じになっている。ということで特に設定無くこのデフォ設定でLaravelなどは動くようになる。

ちなみにLaravel OctaneはOctane側に内包されているCaddyfileを使っていたりする。
https://github.com/laravel/octane/blob/7cbb80a0bd7ccfbf0615dd33de7907c5dad21171/src/Commands/stubs/Caddyfile