アプリデプロイメント
優れた Shopify アプリの構築は戦いの半分に過ぎません -- 本番環境で確実にデプロイし運用することがもう半分です。このモジュールでは、ホスティングプラットフォームの選択、環境変数管理、GitHub Actions による CI/CD パイプラインのセットアップ、モニタリング、ロギング、エラートラッキングをカバーします。終了時には、すべてのリリースに自信を持てる本番グレードのデプロイメントパイプラインが得られます。
ホスティングオプション
Shopify アプリは HTTPS 経由で公開アクセス可能でなければならない Web アプリケーションです。最適なホスティングプラットフォームは、アプリのアーキテクチャ、トラフィックパターン、予算によって異なります。
プラットフォーム比較
| プラットフォーム | 最適な用途 | 開始コスト | コールドスタート | データベース |
|---|---|---|---|---|
| Vercel | Remix/Next.js apps, serverless | Free tier | Yes (serverless) | External required |
| Railway | Full-stack apps, easy setup | $5/month | No | Built-in PostgreSQL |
| Render | Containers, background workers | Free tier | Yes (free tier) | Built-in PostgreSQL |
| Fly.io | Low-latency global apps | Pay-as-you-go | No | Built-in PostgreSQL |
| AWS | Enterprise, full control | Varies | Depends on service | RDS, DynamoDB |
Railway へのデプロイ
Railway は Shopify アプリに最適な選択肢です。長時間実行プロセス(Webhook やバックグラウンドジョブに必要)をサポートし、組み込みの PostgreSQL を備えています:
# Install Railway CLI
npm install -g @railway/cli
# Login and initialize
railway login
railway init
# Add PostgreSQL
railway add --plugin postgresql
# Deploy
railway up
Railway 用の Procfile を設定します:
web: npm run start
worker: npm run worker
Fly.io へのデプロイ
Fly.io は低レイテンシのグローバル分散デプロイが必要な場合に最適です:
# fly.toml
app = "your-shopify-app"
primary_region = "iad"
[build]
builder = "heroku/buildpacks:20"
[env]
NODE_ENV = "production"
PORT = "8080"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 1
[[services]]
protocol = "tcp"
internal_port = 8080
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[services.concurrency]
type = "connections"
hard_limit = 250
soft_limit = 200
[[services.http_checks]]
interval = 10000
grace_period = "5s"
method = "get"
path = "/health"
protocol = "http"
timeout = 2000
# Deploy to Fly.io
fly launch
fly deploy
# Scale to multiple regions
fly regions add lhr sin
fly scale count 2
Vercel へのデプロイ
Remix ベースの Shopify アプリの場合、Vercel はゼロコンフィグのデプロイを提供します:
// vercel.json
{
"framework": "remix",
"buildCommand": "npm run build",
"installCommand": "npm install",
"regions": ["iad1", "lhr1"],
"functions": {
"api/**/*.js": {
"maxDuration": 30
}
},
"headers": [
{
"source": "/webhooks/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "no-store" }
]
}
]
}
Vercel のサーバーレス関数には最大実行時間があります(Pro で 30 秒、Enterprise で 300 秒)。アプリが長時間実行の Webhook やバルクオペレーションを処理する場合は、Railway や Fly.io のような永続プロセスをサポートするプラットフォームを使用するか、重い処理を別のワーカーサービスにオフロードしてください。
環境変数
必須環境変数
すべての Shopify アプリは本番環境でこれらの環境変数を設定する必要があります:
# .env.example (never commit actual .env files)
# Shopify App Credentials
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret
SCOPES=read_products,write_products,read_orders
# App Configuration
HOST=https://your-app.fly.dev
PORT=8080
NODE_ENV=production
# Database
DATABASE_URL=postgresql://user:pass@host:5432/dbname
# Session Storage
REDIS_URL=redis://default:pass@host:6379
# Encryption (for access token storage)
ENCRYPTION_KEY=generate-a-32-byte-hex-string
# Error Tracking
SENTRY_DSN=https://key@sentry.io/project-id
# Logging
LOG_LEVEL=info
.env ファイルをバージョン管理にコミットしないでください。.env* を .gitignore に追加してください。ホスティングプラットフォームのシークレット管理(Railway variables、Fly.io secrets、Vercel environment variables)を使用して、ランタイム時にこれらを注入してください。
安全な暗号化キーの生成
# Generate a 32-byte encryption key for AES-256
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
プラットフォーム固有のシークレット管理
# Fly.io
fly secrets set SHOPIFY_API_KEY=xxx SHOPIFY_API_SECRET=yyy
# Railway (via dashboard or CLI)
railway variables set SHOPIFY_API_KEY=xxx
# Vercel
vercel env add SHOPIFY_API_KEY production
GitHub Actions による CI/CD
適切な CI/CD パイプラインにより、すべてのデプロイがテスト、ビルド、一貫してデプロイされることが保証されます。
完全な GitHub Actions ワークフロー
# .github/workflows/deploy.yml
name: Test, Build, and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
lint-and-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run test -- --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
integration-tests:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: shopify_app_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://test:test@localhost:5432/shopify_app_test
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://test:test@localhost:5432/shopify_app_test
SHOPIFY_API_KEY: ${{ secrets.TEST_SHOPIFY_API_KEY }}
SHOPIFY_API_SECRET: ${{ secrets.TEST_SHOPIFY_API_SECRET }}
security-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm audit --audit-level=high
- name: Check for secrets in code
uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
deploy-staging:
needs: [lint-and-typecheck, unit-tests, security-audit]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: staging
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --app your-app-staging
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
- name: Run smoke tests against staging
run: |
npm run test:smoke -- --base-url=https://your-app-staging.fly.dev
deploy-production:
needs: [deploy-staging]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://your-app.fly.dev
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --app your-app-production --strategy canary
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
- name: Verify deployment health
run: |
for i in 1 2 3 4 5; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://your-app.fly.dev/health)
if [ "$STATUS" = "200" ]; then
echo "Health check passed"
exit 0
fi
sleep 10
done
echo "Health check failed"
exit 1
Shopify CLI エクステンションデプロイ
アプリにエクステンション(Theme App Extension、Checkout UI Extension など)が含まれている場合は、別のデプロイステップを追加します:
deploy-extensions:
needs: [deploy-production]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: Deploy Shopify extensions
run: npx shopify app deploy --force
env:
SHOPIFY_API_KEY: ${{ secrets.SHOPIFY_API_KEY }}
SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
本番デプロイには、必須レビュアー付きの GitHub Actions environments を使用してください。これにより、コードが本番に到達する前に手動承認ゲートが追加され、チームがステージングテスト結果をレビューする機会が得られます。
モニタリングとログ
ヘルスチェックエンドポイント
すべての本番アプリにはヘルスチェックエンドポイントが必要です:
// routes/health.js
export async function loader() {
const checks = {};
// Database connectivity
try {
await prisma.$queryRaw`SELECT 1`;
checks.database = 'healthy';
} catch (error) {
checks.database = 'unhealthy';
}
// Redis connectivity
try {
await redis.ping();
checks.redis = 'healthy';
} catch (error) {
checks.redis = 'unhealthy';
}
const allHealthy = Object.values(checks).every((v) => v === 'healthy');
return new Response(JSON.stringify({
status: allHealthy ? 'healthy' : 'degraded',
checks,
timestamp: new Date().toISOString(),
version: process.env.COMMIT_SHA || 'unknown',
}), {
status: allHealthy ? 200 : 503,
headers: { 'Content-Type': 'application/json' },
});
}
構造化ログ
// utils/logger.js
import pino from 'pino';
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport:
process.env.NODE_ENV === 'development'
? { target: 'pino-pretty' }
: undefined,
base: {
app: 'shopify-app',
env: process.env.NODE_ENV,
version: process.env.COMMIT_SHA,
},
});
// Usage
logger.info({ shop: 'example.myshopify.com', event: 'install' }, 'App installed');
logger.error({ err, shop, webhookTopic }, 'Webhook processing failed');
エラートラッキング
Sentry 統合
// utils/sentry.js
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
release: process.env.COMMIT_SHA,
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.2 : 1.0,
integrations: [
Sentry.prismaIntegration(),
Sentry.httpIntegration(),
],
beforeSend(event) {
// Scrub sensitive data
if (event.request?.headers) {
delete event.request.headers['x-shopify-access-token'];
delete event.request.headers['authorization'];
}
return event;
},
});
export { Sentry };
セッションリプレイ用 LogRocket
アプリの管理 UI でマーチャントから報告された問題をデバッグするには:
// app/entry.client.jsx
import LogRocket from 'logrocket';
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') {
LogRocket.init('your-org/your-app');
// Identify the merchant
const shop = new URLSearchParams(window.location.search).get('shop');
if (shop) {
LogRocket.identify(shop, { shop });
}
}
LogRocket はユーザーセッションを記録するため、マーチャントがバグを報告した際に体験した内容を正確にリプレイできます。再現が難しい問題のデバッグに非常に有用です。プライバシーに注意し、決済情報や顧客 PII などの機密フィールドを編集するように LogRocket を設定してください。
デプロイチェックリスト
| 項目 | ステータス | メモ |
|---|---|---|
| HTTPS の強制 | 必須 | 上記のすべてのプラットフォームがこれを強制します |
| ヘルスチェックエンドポイント | 必須 | ホスティングプラットフォームと稼働時間監視で使用 |
| 環境変数の保護 | 必須 | コードには入れず、常にプラットフォームのシークレットに保存 |
| データベースマイグレーションの自動化 | 必須 | デプロイ前に CI で実行 |
| エラートラッキングの設定 | 強く推奨 | Sentry、Bugsnag、または同等のツール |
| 構造化ログ | 強く推奨 | 検索性のために JSON 形式 |
| CI/CD パイプライン | 強く推奨 | テストとデプロイの自動化 |
| ステージング環境 | 推奨 | 本番前にテスト |
| カナリアデプロイ | 推奨 | リスク軽減のための段階的ロールアウト |
| 稼働時間監視 | 推奨 | 外部監視(Uptime Robot、Better Stack) |
堅実なデプロイメントパイプラインは信頼性の高い Shopify アプリの基盤です。初日から適切にセットアップする時間を投資すれば、本番環境の問題と戦う時間が大幅に減り、マーチャントが喜ぶ機能を構築する時間が大幅に増えます。