前提
- Fly.ioのページについては、2024年8月7日時点の構成で説明しています。
- 検証環境
- MacBook Pro(Intel)
- macOS Sonoma 14.5
- MacBook Pro(Intel)
Fly.ioのページにアクセスし、画面上部メニューのから「Docs」をクリックします。
検索フォームから「Laravel」と入力します。
「Laravel on Fly.io」がヒットするのでクリックします。
「Laravel on Fly.io」のページに従って作業を進めていきます。
事前準備として以下の説明が記載されています。
PHP8以上と composer
コマンドを実行できる環境が必要なので、今回はDockerを使って「php:8.3.10
」を利用することとします。
以下のコマンドを実行してコンテナを起動し、コンテナ内に入ります。
- デプロイ練習が終わったら不要な環境となるため
--rm
を付与しています。 - コンテナ実行環境の8000番ポートを利用するので
127.0.0.1:8000:8000
を付与しています。
% docker container run --rm -p 127.0.0.1:8000:8000 -it php:8.3.10 bash
コンテナのOSを確認すると Debian GNU/Linux 12 (bookworm)
であることがわかります。
# grep PRETTY_NAME /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
PHPのバージョンは「php:8.3.10
」と指定していたので、当然「8.3.10
」と表示されます。
# php -v
PHP 8.3.10 (cli) (built: Aug 1 2024 19:23:45) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.10, Copyright (c) Zend Technologies
composer
コマンドはインストールされていません。
# composer --version
bash: composer: command not found
Download Composerのページに従って、composer
コマンドをインストールします。
# php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
# php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
# php composer-setup.php
# php -r "unlink('composer-setup.php');"
# mv composer.phar /usr/local/bin/composer
Composerのバージョンは2024年8月7日時点では「2.7.7
」がインストールされるようです。
# composer --version
Composer version 2.7.7 2024-06-10 22:11:12
PHP version 8.3.10 (/usr/local/bin/php)
Run the "diagnose" command to get more detailed diagnostics output.
Laravelのインストールの過程で必要なパッケージ群をインストールします。
# apt update
# apt install -y git zip unzip
準備が整いましたので、コンテナ内でLaravelの環境を構築します。
- コンテナ内の8000番ポートをコンテナ実行環境にマッピングするため
--host=0.0.0.0 --port=8000
を付与します。
# composer create-project laravel/laravel fly-laravel
# cd fly-laravel
# php artisan serve --host=0.0.0.0 --port=8000
コンテナ実行環境のブラウザで http://127.0.0.1:8000/ にアクセスして、動作していることを確認します。
Fly.ioにデプロイするために flyctl(fly)
コマンドをインストールします。
※インストール方法については「Fly.io の「Hands-on」ページに従ってデプロイの練習をやってみた」で紹介しています。
# curl -L https://fly.io/install.sh | sh
・
・
flyctl was installed successfully to /root/.fly/bin/flyctl
Manually add the directory to your $HOME/.bash_profile (or similar)
export FLYCTL_INSTALL="/root/.fly"
export PATH="$FLYCTL_INSTALL/bin:$PATH"
Run '/root/.fly/bin/flyctl --help' to get started
# apt install -y vim
# vim $HOME/.bash_profile
# tail -2 $HOME/.bash_profile
export FLYCTL_INSTALL="/root/.fly"
export PATH="$FLYCTL_INSTALL/bin:$PATH"
# . $HOME/.bash_profile
flyctl(fly)
コマンドのバージョンを確認します。
# flyctl version
flyctl v0.2.107 linux/amd64 Commit: 87aa565a15aa220c2e846453279417724658a598 BuildDate: 2024-08-06T18:32:57Z
# fly version
flyctl v0.2.107 linux/amd64 Commit: 87aa565a15aa220c2e846453279417724658a598 BuildDate: 2024-08-06T18:32:57Z
fly
コマンドは flyctl
コマンドへのシンボリックリンクになっています。
# which flyctl
/root/.fly/bin/flyctl
# which fly
/root/.fly/bin/fly
# ls -l /root/.fly/bin/fly
lrwxrwxrwx 1 root root 21 Aug 7 00:58 /root/.fly/bin/fly -> /root/.fly/bin/flyctl
なお、 flyctl
コマンドは「Command Lines, Flyctl and Fly」の記事によると、Go製のようです。
flyctl
コマンドと fly
コマンドが共存している状態ですが、コミュニティページの「What’s the difference between Fly and Flyctl?」によると、いずれ fly
コマンドに統合される?のかもしれません。
これを踏まえ、以降は fly
コマンドにて説明を続けます。
Laravelのアプリケーションをインストールしたディレクトリで Fly.io へのデプロイ作業を行っていきます。
# pwd
/fly-laravel
fly launch
コマンドを実行すると、 Fly.io へのサインインが求められるので「y」と入力します。
# fly launch
? You must be logged in to do this. Would you like to sign in? (y/N) y
以下のように表示が切り替わり、本来であればここでブラウザが起動するのですが、コンテナ内で実行しているためブラウザが起動しません。
なので、表示されているURLをコピーし、ブラウザのURL欄に貼り付けて直接アクセスします。
? You must be logged in to do this. Would you like to sign in? Yes
failed opening browser. Copy the url (https://fly.io/app/auth/cli/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX) into a browser and continue
Opening https://fly.io/app/auth/cli/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ...
Waiting for session...⣷
ブラウザでアクセスすると以下のように表示されるので、「Continue as メールアドレス」の部分をクリックします。
「Waiting for session...」の表示が「Done」に切り替わり、以下のように表示されます。
ここでは、どのOrganizationを利用するかだったり、マシンスペックなどが表示されています。
何も変更する必要がなければ「N」と入力します。「y」と入力すると(本来であれば)ブラウザが起動し、Fly.ioの画面上で設定を調整することが可能です。
今回はデプロイ練習なのでサッと試すために「N」と入力します。
Waiting for session... Done
successfully logged in as メールアドレス
Scanning source code
Detected a Laravel app
Creating app in /fly-laravel
We're about to launch your Laravel app on Fly.io. Here's what you're getting:
Organization: XXXXXXXX (fly launch defaults to the personal org)
Name: fly-laravel-XXXXXXXXX-XXXX-XXXX (generated)
Region: Tokyo, Japan (this is the fastest region for you)
App Machines: shared-cpu-1x, 1GB RAM (most apps need about 1GB of RAM)
Postgres: <none> (not requested)
Redis: Pay-as-you-go Plan: 10 GB Max Data Size, eviction disabled (determined from app source)
Tigris: <none> (not requested)
? Do you want to tweak these settings before proceeding? (y/N) N
以下のように表示が切り替わり、Laravelのインストールやデプロイの経過が確認できます。
? Do you want to tweak these settings before proceeding? No
Created app 'fly-laravel-XXXXXXXXX-XXXX-XXXX' in organization 'personal'
Admin URL: https://fly.io/apps/fly-laravel-XXXXXXXXX-XXXX-XXXX
Hostname: fly-laravel-XXXXXXXXX-XXXX-XXXX.fly.dev
Set secrets on fly-laravel-XXXXXXXXX-XXXX-XXXX: APP_KEY
Your database fly-laravel-XXXXXXXXX-XXXX-XXXX-redis is ready. Apps in the personal org can connect to Redis at redis://default:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@fly-fly-laravel-XXXXXXXXX-XXXX-XXXX-redis.upstash.io:6379
If you have redis-cli installed, use fly redis connect to get a Redis console.
Your database is billed at $0.20 per 100K commands. If you're using Sidekiq or BullMQ, which poll Redis frequently, consider switching to a fixed-price plan. See https://fly.io/docs/reference/redis/#pricing
Redis database fly-laravel-XXXXXXXXX-XXXX-XXXX-redis is set on fly-laravel-XXXXXXXXX-XXXX-XXXX as the REDIS_URL environment variable
installing: composer require --dev fly-apps/dockerfile-laravel
./composer.json has been updated
Running composer update fly-apps/dockerfile-laravel
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking fly-apps/dockerfile-laravel (1.0.6)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Downloading fly-apps/dockerfile-laravel (1.0.6)
- Installing fly-apps/dockerfile-laravel (1.0.6): Extracting archive
Generating optimized autoload files
Warning: Ambiguous class resolution, "App\Providers\AppServiceProvider" was found in both "/fly-laravel/app/Providers/AppServiceProvider.php" and "/fly-laravel/vendor/fly-apps/dockerfile-laravel/app/Providers/AppServiceProvider.php", the first will be used.
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
INFO Discovering packages.
laravel/sail ....................................................................................................... DONE
laravel/tinker ..................................................................................................... DONE
nesbot/carbon ...................................................................................................... DONE
nunomaduro/collision ............................................................................................... DONE
nunomaduro/termwind ................................................................................................ DONE
79 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force
INFO No publishable resources for tag [laravel-assets].
No security vulnerability advisories found.
Using version ^1.0 for fly-apps/dockerfile-laravel
Running: vendor/bin/dockerfile-laravel generate
create Dockerfile
create .dockerignore
create .fly/entrypoint.sh
create .fly/start-nginx.sh
create .fly/scripts/caches.sh
create .fly/fpm/pool.d/www.conf
create .fly/nginx/conf.d/access-log.conf
create .fly/nginx/conf.d/websockets.conf
create .fly/nginx/sites-available/default-octane
create .fly/nginx/sites-available/default
create .fly/nginx/nginx.conf
create .fly/php/packages/7.4.txt
create .fly/php/packages/8.0.txt
create .fly/php/packages/8.1.txt
create .fly/php/packages/8.2.txt
create .fly/php/packages/8.3.txt
create .fly/php/ondrej_ubuntu_php.gpg
create .fly/supervisor/conf.d/fpm.conf
create .fly/supervisor/conf.d/nginx.conf
create .fly/supervisor/supervisord.conf
Wrote config file fly.toml
Validating /fly-laravel/fly.toml
✓ Configuration is valid
==> Building image
Remote builder fly-builder-rough-glitter-2586 ready
==> Building image with Docker
--> docker host: 24.0.7 linux x86_64
[+] Building 22.4s (34/34) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 4.52kB 0.1s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 476B 0.1s
=> resolve image config for docker.io/docker/dockerfile:experimental 1.4s
=> CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 0.0s
=> [internal] load .dockerignore 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:22.04 0.4s
=> [internal] load metadata for docker.io/library/node:18 0.6s
=> [internal] load build context 0.5s
=> => transferring context: 419.16kB 0.5s
=> FROM docker.io/library/composer:2 0.4s
=> => resolve docker.io/library/composer:2 0.4s
=> [node_modules_go_brrr 1/7] FROM docker.io/library/node:18@sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 0.0s
=> [base 1/14] FROM docker.io/library/ubuntu:22.04@sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 0.0s
=> CACHED [node_modules_go_brrr 2/7] RUN mkdir /app 0.0s
=> CACHED [node_modules_go_brrr 4/7] WORKDIR /app 0.0s
=> CACHED [node_modules_go_brrr 3/7] RUN mkdir -p /app 0.0s
=> CACHED [base 8/14] COPY .fly/supervisor/ /etc/supervisor/ 0.0s
=> CACHED [base 6/14] COPY .fly/nginx/ /etc/nginx/ 0.0s
=> CACHED [base 7/14] COPY .fly/fpm/ /etc/php/8.3/fpm/ 0.0s
=> CACHED [base 2/14] COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 0.0s
=> CACHED [base 9/14] COPY .fly/entrypoint.sh /entrypoint 0.0s
=> CACHED [base 10/14] COPY .fly/start-nginx.sh /usr/local/bin/start-nginx 0.0s
=> CACHED [base 11/14] RUN chmod 754 /usr/local/bin/start-nginx 0.0s
=> CACHED [base 5/14] RUN apt-get update && apt-get install -y --no-install-recommends gnupg2 ca-certificates gi 0.0s
=> CACHED [base 4/14] ADD .fly/php/packages/8.3.txt /tmp/php-packages.txt 0.0s
=> CACHED [base 3/14] COPY .fly/php/ondrej_ubuntu_php.gpg /etc/apt/trusted.gpg.d/ondrej_ubuntu_php.gpg 0.0s
=> [base 12/14] COPY . /var/www/html 0.0s
=> [node_modules_go_brrr 5/7] COPY . . 0.0s
=> [base 13/14] WORKDIR /var/www/html 0.0s
=> [base 14/14] RUN composer install --optimize-autoloader --no-dev && mkdir -p storage/logs && php artisan o 8.2s
=> [node_modules_go_brrr 6/7] COPY --from=base /var/www/html/vendor /app/vendor 0.8s
=> [node_modules_go_brrr 7/7] RUN if [ -f "vite.config.js" ]; then ASSET_CMD="build"; else ASSET_ 8.9s
=> [stage-2 1/2] COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm 0.0s
=> [stage-2 2/2] RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ && rm -rf /var/www/html/public-npm 0.2s
=> exporting to image 0.6s
=> => exporting layers 0.6s
=> => writing image sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 0.0s
=> => naming to registry.fly.io/fly-laravel-XXXXXXXXX-XXXX-XXXX:deployment-01J4N6MTJPTNHZ9N9M91WTX3CE 0.0s
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/fly-laravel-XXXXXXXXX-XXXX-XXXX]
XXXXXXXXXXXX: Pushed
・
・
XXXXXXXXXXXX: Pushed
deployment-XXXXXXXXXXXXXXXXXXXXXXXXXX: digest: sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX size: 3653
--> Pushing image done
image: registry.fly.io/fly-laravel-XXXXXXXXX-XXXX-XXXX:deployment-01J4N6MTJPTNHZ9N9M91WTX3CE
image size: 357 MB
Watch your deployment at https://fly.io/apps/fly-laravel-XXXXXXXXX-XXXX-XXXX/monitoring
Provisioning ips for fly-laravel-XXXXXXXXX-XXXX-XXXX
Dedicated ipv6: XXXX:XXXX:X::XX:XXXX:X
Shared ipv4: XXX.XXX.XXX.XXX
Add a dedicated ipv4 with: fly ips allocate-v4
This deployment will:
* create 2 "app" machines
No machines in group app, launching a new machine
WARNING The app is not listening on the expected address and will not be reachable by fly-proxy.
You can fix this by configuring your app to listen on the following addresses:
- 0.0.0.0:8080
Found these processes inside the machine with open listening sockets:
PROCESS | ADDRESSES
-----------------*---------------------------------------
/.fly/hallpass | [XXXX:X:XXXX:XXX:XXX:XXXX:XXXX:X]:XX
Creating a second machine to increase service availability
Finished launching new machines
-------
NOTE: The machines for [app] have services with 'auto_stop_machines = "stop"' that will be stopped when idling
-------
Checking DNS configuration for fly-laravel-XXXXXXXXX-XXXX-XXXX.fly.dev
Visit your newly deployed app at https://fly-laravel-XXXXXXXXX-XXXX-XXXX.fly.dev/
最後に「Visit your newly deployed app at https://fly-laravel-XXXXXXXXX-XXXX-XXXX.fly.dev/ 」のようにURLが表示されるので、ブラウザでアクセスすると、 Fly.io 上にデプロイされたLaravelのアプリケーションが確認できます。
fly launch
コマンド実行時に自動生成された Dockerfile
と fly.toml
の中身はこんな感じです。
Fly.io へのデプロイは Dockerfile
と fly.toml
によって制御されています。
# cat Dockerfile
# syntax = docker/dockerfile:experimental
ARG PHP_VERSION=8.2
ARG NODE_VERSION=18
FROM ubuntu:22.04 as base
LABEL fly_launch_runtime="laravel"
# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION
ENV DEBIAN_FRONTEND=noninteractive \
COMPOSER_ALLOW_SUPERUSER=1 \
COMPOSER_HOME=/composer \
COMPOSER_MAX_PARALLEL_HTTP=24 \
PHP_PM_MAX_CHILDREN=10 \
PHP_PM_START_SERVERS=3 \
PHP_MIN_SPARE_SERVERS=2 \
PHP_MAX_SPARE_SERVERS=4 \
PHP_DATE_TIMEZONE=UTC \
PHP_DISPLAY_ERRORS=Off \
PHP_ERROR_REPORTING=22527 \
PHP_MEMORY_LIMIT=256M \
PHP_MAX_EXECUTION_TIME=90 \
PHP_POST_MAX_SIZE=100M \
PHP_UPLOAD_MAX_FILE_SIZE=100M \
PHP_ALLOW_URL_FOPEN=Off
# Prepare base container:
# 1. Install PHP, Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY .fly/php/ondrej_ubuntu_php.gpg /etc/apt/trusted.gpg.d/ondrej_ubuntu_php.gpg
ADD .fly/php/packages/${PHP_VERSION}.txt /tmp/php-packages.txt
RUN apt-get update \
&& apt-get install -y --no-install-recommends gnupg2 ca-certificates git-core curl zip unzip \
rsync vim-tiny htop sqlite3 nginx supervisor cron \
&& ln -sf /usr/bin/vim.tiny /etc/alternatives/vim \
&& ln -sf /etc/alternatives/vim /usr/bin/vim \
&& echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ondrej-ubuntu-php-focal.list \
&& apt-get update \
&& apt-get -y --no-install-recommends install $(cat /tmp/php-packages.txt) \
&& ln -sf /usr/sbin/php-fpm${PHP_VERSION} /usr/sbin/php-fpm \
&& mkdir -p /var/www/html/public && echo "index" > /var/www/html/public/index.php \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
# 2. Copy config files to proper locations
COPY .fly/nginx/ /etc/nginx/
COPY .fly/fpm/ /etc/php/${PHP_VERSION}/fpm/
COPY .fly/supervisor/ /etc/supervisor/
COPY .fly/entrypoint.sh /entrypoint
COPY .fly/start-nginx.sh /usr/local/bin/start-nginx
RUN chmod 754 /usr/local/bin/start-nginx
# 3. Copy application code, skipping files based on .dockerignore
COPY . /var/www/html
WORKDIR /var/www/html
# 4. Setup application dependencies
RUN composer install --optimize-autoloader --no-dev \
&& mkdir -p storage/logs \
&& php artisan optimize:clear \
&& chown -R www-data:www-data /var/www/html \
&& echo "MAILTO=\"\"\n* * * * * www-data /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
&& sed -i='' '/->withMiddleware(function (Middleware \$middleware) {/a\
\$middleware->trustProxies(at: "*");\
' bootstrap/app.php; \
if [ -d .fly ]; then cp .fly/entrypoint.sh /entrypoint; chmod +x /entrypoint; fi;
# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:${NODE_VERSION} as node_modules_go_brrr
RUN mkdir /app
RUN mkdir -p /app
WORKDIR /app
COPY . .
COPY --from=base /var/www/html/vendor /app/vendor
# Use yarn or npm depending on what type of
# lock file we might find. Defaults to
# NPM if no lock file is found.
# Note: We run "production" for Mix and "build" for Vite
RUN if [ -f "vite.config.js" ]; then \
ASSET_CMD="build"; \
else \
ASSET_CMD="production"; \
fi; \
if [ -f "yarn.lock" ]; then \
yarn install --frozen-lockfile; \
yarn $ASSET_CMD; \
elif [ -f "pnpm-lock.yaml" ]; then \
corepack enable && corepack prepare pnpm@latest-8 --activate; \
pnpm install --frozen-lockfile; \
pnpm run $ASSET_CMD; \
elif [ -f "package-lock.json" ]; then \
npm ci --no-audit; \
npm run $ASSET_CMD; \
else \
npm install; \
npm run $ASSET_CMD; \
fi;
# From our base container created above, we
# create our final image, adding in static
# assets that we generated above
FROM base
# Packages like Laravel Nova may have added assets to the public directory
# or maybe some custom assets were added manually! Either way, we merge
# in the assets we generated above rather than overwrite them
COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm
RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ \
&& rm -rf /var/www/html/public-npm \
&& chown -R www-data:www-data /var/www/html/public
# 5. Setup Entrypoint
EXPOSE 8080
ENTRYPOINT ["/entrypoint"]
# cat fly.toml
# fly.toml app configuration file generated for fly-laravel-thrumming-frog-1677 on 2024-08-07T01:18:15Z
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'fly-laravel-XXXXXXXXX-XXXX-XXXX'
primary_region = 'nrt'
console_command = 'php /var/www/html/artisan tinker'
[build]
[build.args]
NODE_VERSION = '18'
PHP_VERSION = '8.3'
[env]
APP_ENV = 'production'
LOG_CHANNEL = 'stderr'
LOG_LEVEL = 'info'
LOG_STDERR_FORMATTER = 'Monolog\Formatter\JsonFormatter'
SESSION_DRIVER = 'cookie'
SESSION_SECURE_COOKIE = 'true'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1