Fly.ioでアプリをグローバルデプロイ - 完全実装ガイド
Fly.ioは、アプリケーションを世界中のエッジに展開できる次世代のPaaSプラットフォームです。Dockerコンテナをベースに、低レイテンシーとグローバルスケールを実現します。
本記事では、Fly.ioの基礎から実践的なデプロイパターン、本番運用のベストプラクティスまで詳しく解説します。
Fly.ioとは
Fly.ioは、グローバルエッジにアプリケーションをデプロイするためのプラットフォームです。ユーザーに最も近いリージョンでアプリを実行し、低レイテンシーを実現します。
主な特徴
グローバルエッジ: 世界35以上のリージョンに展開 Dockerベース: 任意の言語・フレームワークに対応 高速デプロイ: 数秒でグローバル展開 統合データベース: Postgres、Redis、SQLiteを標準提供 無料枠: 月額$5相当のクレジット(小規模アプリなら無料)
料金体系
無料枠:
- 最大3台のシェアードVMインスタンス
- 3GB永続ストレージ
- 160GBアウトバウンド転送
有料プラン:
- Dedicated VM: $0.0000008/秒 (~$2.07/月)
- メモリ: $0.0000002/MB/秒
- ストレージ: $0.15/GB/月
セットアップ
Fly CLIのインストール
# macOS
brew install flyctl
# Linux
curl -L https://fly.io/install.sh | sh
# Windows (PowerShell)
iwr https://fly.io/install.ps1 -useb | iex
# インストール確認
flyctl version
ログインと認証
# Fly.ioにログイン
flyctl auth login
# クレジットカード情報の登録(無料枠を使う場合も必要)
flyctl auth signup
# 認証状態の確認
flyctl auth whoami
Node.js/Express アプリのデプロイ
アプリケーションの準備
// app.ts
import express from 'express';
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello from Fly.io!',
region: process.env.FLY_REGION,
instance: process.env.FLY_ALLOC_ID,
});
});
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// package.json
{
"name": "fly-demo",
"version": "1.0.0",
"scripts": {
"start": "node dist/app.js",
"build": "tsc"
},
"dependencies": {
"express": "^4.18.2"
}
}
Fly.ioプロジェクトの初期化
# プロジェクトディレクトリで実行
flyctl launch
# 対話形式で設定
# - アプリ名の選択
# - リージョンの選択(自動で最適なリージョンを提案)
# - PostgreSQLの追加(オプション)
自動生成されるfly.tomlをカスタマイズします。
# fly.toml
app = "my-fly-app"
primary_region = "nrt" # 東京リージョン
[build]
[build.args]
NODE_VERSION = "20"
[env]
NODE_ENV = "production"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0 # スリープ機能有効
[[http_service.checks]]
interval = "10s"
timeout = "2s"
grace_period = "5s"
method = "GET"
path = "/health"
[[vm]]
cpu_kind = "shared"
cpus = 1
memory_mb = 256
デプロイ
# アプリをデプロイ
flyctl deploy
# デプロイ状況の確認
flyctl status
# ログの確認
flyctl logs
# アプリを開く
flyctl open
Next.js アプリケーションのデプロイ
Next.js プロジェクトの設定
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
experimental: {
serverActions: true,
},
};
module.exports = nextConfig;
Dockerfileの作成
# Dockerfile
FROM node:20-alpine AS base
# 依存関係のインストール
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# ビルド
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# 本番環境イメージ
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
fly.toml の設定
# fly.toml
app = "my-nextjs-app"
primary_region = "nrt"
[build]
[env]
NODE_ENV = "production"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
[[http_service.checks]]
interval = "15s"
timeout = "3s"
grace_period = "10s"
method = "GET"
path = "/"
[[vm]]
cpu_kind = "shared"
cpus = 1
memory_mb = 512
環境変数の設定
# 環境変数をセット
flyctl secrets set DATABASE_URL="postgresql://..." \
NEXTAUTH_SECRET="your-secret" \
NEXTAUTH_URL="https://my-nextjs-app.fly.dev"
# 環境変数の確認
flyctl secrets list
デプロイ
flyctl deploy
# デプロイ後の確認
flyctl status
flyctl logs
データベース統合
Fly Postgresの作成
# Postgresクラスターの作成
flyctl postgres create
# 対話形式で設定
# - クラスター名
# - リージョン
# - VMサイズ
# - ボリュームサイズ
# 接続
flyctl postgres connect -a my-postgres-cluster
アプリとPostgresの接続
# アプリにPostgresをアタッチ
flyctl postgres attach my-postgres-cluster -a my-app
# DATABASE_URL が自動的に環境変数にセットされる
Prismaを使った接続例
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// lib/db.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
// app/api/users/route.ts
import { prisma } from '@/lib/db';
export async function GET() {
const users = await prisma.user.findMany();
return Response.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await prisma.user.create({
data: {
email: body.email,
name: body.name,
},
});
return Response.json(user, { status: 201 });
}
マイグレーション
# ローカルで開発
npx prisma migrate dev --name init
# 本番環境でマイグレーション実行
flyctl ssh console -a my-app
npx prisma migrate deploy
スケーリング戦略
マルチリージョンデプロイ
# 複数のリージョンにデプロイ
flyctl regions add nrt # 東京
flyctl regions add sjc # サンフランシスコ
flyctl regions add ams # アムステルダム
# リージョン一覧
flyctl regions list
# スケール設定
flyctl scale count 2 --region nrt # 東京に2台
flyctl scale count 1 --region sjc # サンフランシスコに1台
オートスケーリング
# fly.toml
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 1
max_machines_running = 10
[http_service.concurrency]
type = "requests"
hard_limit = 250
soft_limit = 200
メモリとCPUのスケーリング
# VMサイズの変更
flyctl scale vm shared-cpu-2x --memory 512
# 利用可能なVMサイズ一覧
flyctl platform vm-sizes
# 現在のスケール確認
flyctl scale show
CI/CDパイプライン
GitHub Actionsでの自動デプロイ
# .github/workflows/deploy.yml
name: Deploy to Fly.io
on:
push:
branches: [main]
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Fly CLI
uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy to Fly.io
run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Fly API Tokenの取得
# トークンを生成
flyctl auth token
# GitHub Secretsに登録
# Settings > Secrets > New repository secret
# Name: FLY_API_TOKEN
# Value: <生成されたトークン>
プレビュー環境の構築
# .github/workflows/preview.yml
name: Preview Deployment
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy Preview
run: |
APP_NAME="my-app-pr-${{ github.event.pull_request.number }}"
flyctl launch --no-deploy --name $APP_NAME --copy-config --yes
flyctl deploy --app $APP_NAME
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
const appName = `my-app-pr-${{ github.event.pull_request.number }}`;
const url = `https://${appName}.fly.dev`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Preview deployed to: ${url}`
});
モニタリングとロギング
ログの確認
# リアルタイムログ
flyctl logs
# 特定のインスタンスのログ
flyctl logs -i <instance-id>
# JSON形式で出力
flyctl logs --json
メトリクスの確認
# ダッシュボードを開く
flyctl dashboard
# メトリクスの確認
flyctl metrics
Grafana/Prometheusとの統合
# fly.toml
[metrics]
port = 9091
path = "/metrics"
// metrics.ts
import express from 'express';
import promClient from 'prom-client';
const app = express();
// Prometheusメトリクスの設定
const register = new promClient.Registry();
promClient.collectDefaultMetrics({ register });
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_ms',
help: 'Duration of HTTP requests in ms',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.1, 5, 15, 50, 100, 500],
});
register.registerMetric(httpRequestDuration);
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
トラブルシューティング
デプロイが失敗する場合
# 詳細なログを確認
flyctl logs
# ビルドログを確認
flyctl deploy --verbose
# SSHで接続してデバッグ
flyctl ssh console
ヘルスチェックが失敗する場合
# fly.toml - タイムアウトを延長
[[http_service.checks]]
interval = "30s"
timeout = "5s"
grace_period = "20s"
method = "GET"
path = "/health"
メモリ不足エラー
# メモリを増やす
flyctl scale memory 512
# または fly.toml で設定
[[vm]]
memory_mb = 512
まとめ
Fly.ioは、グローバルエッジデプロイを簡単に実現できる強力なプラットフォームです。
主な利点
- 低レイテンシー: ユーザーに最も近いリージョンで実行
- 簡単デプロイ: Dockerベースで柔軟性が高い
- 統合データベース: Postgres、Redisが標準提供
- コスト効率: 小規模アプリなら無料枠で十分
デプロイのベストプラクティス
- ヘルスチェックの実装:
/healthエンドポイントを用意 - 環境変数の活用:
flyctl secretsでセキュアに管理 - マルチリージョン展開: グローバルなユーザーベースに対応
- CI/CD統合: GitHub Actionsで自動デプロイ
- モニタリング: ログとメトリクスを定期的に確認
Fly.ioを活用することで、エンタープライズレベルのインフラを個人開発者でも簡単に構築できます。