・約4分で読めます
更新:

GCP Cloud RunからOCI Always Freeに移行してサービス費用を$0にした話


PRXサーバー国内シェアNo.1のレンタルサーバー。高速・安定・サポート充実で初心者にもおすすめ。
Xサーバー 公式サイト →

GCP無料トライアルの期限($300クレジット)が迫ってきた。

使っていたのはCloud Run 2サービス。FX Bot関連のAPI(Python/FastAPI)と市場分析ダッシュボード(Streamlit)だ。トライアル終了後はそのまま課金が発生するので、期限前に手を打つことにした。

選んだ移行先は OCI Always Free。VM.Standard.E2.1.Microが1テナントにつき2台まで永久無料で使える。すでに1台目はFX自動売買Bot用に使っており、2台目が空いていた。今回はこれを活用する。

移行前の構成

GCP (sns-bot-ueno プロジェクト)
├── Cloud Run: sns-bot (Python/FastAPI) ← us-central1
│   └── Cloud Scheduler 13ジョブが呼び出し
GCP (stock-finance-ueno プロジェクト)
└── Cloud Run: finance-dashboard (Streamlit) ← asia-northeast1

Cloud Runは便利だが、トライアル終了後は「使った分だけ課金」になる。稼働率が低いサービスなら余計コストがかかることもある。

移行後の構成

OCI Osaka (138.2.x.x) — Always Free
├── Docker: sns-bot (8081番ポート)
├── Docker: finance-dashboard (8082番ポート)
└── nginx: パスベース振り分け
    ├── /post/, /note/, /report/, /pdca/ → sns-bot
    └── / → finance-dashboard

月額コスト: $0(Always Free永久無料)


Step 1: OCI 2台目インスタンスを作成する

1台目のインスタンス(autotrade用)と同じテナントに、2台目を追加で作る。

OCI CLIが使えるならコマンド一発だ。事前に以下の情報を控えておく。

  • テナントのOCID(コンソール → プロファイル → テナンシ)
  • ホームリージョン(例: ap-osaka-1)
  • 作成済みVCNのサブネットOCID
  • SSH公開鍵ファイルのパス
oci compute instance launch \
  --compartment-id <テナントOCID> \
  --availability-domain <AD> \
  --shape VM.Standard.E2.1.Micro \
  --display-name sns-finance-bot \
  --image-id <Ubuntu 22.04のOCID> \
  --subnet-id <サブネットOCID> \
  --ssh-authorized-keys-file ~/.ssh/id_ed25519.pub \
  --assign-public-ip true

数分後にパブリックIPが払い出される。

初期セットアップ

ssh ubuntu@<新インスタンスIP>

# Docker インストール
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker ubuntu

# docker-compose v1 インストール(Ubuntu 22.04標準)
sudo apt-get install -y docker-compose

# nginx インストール
sudo apt-get install -y nginx

# 再ログイン(dockerグループ反映)
exit

Step 2: サービスアカウントキーを準備する

Cloud RunはGCPのIAMで認証していたが、OCIからはサービスアカウントキー(JSONファイル)で認証する。

GCPコンソール → IAM → サービスアカウントから、専用SAを作成して最小権限を付与する。

sns-bot用:
  roles/datastore.user(Firestore読み書き)

finance-dashboard用:
  roles/storage.objectViewer(GCSバケット参照)

作成したキーファイルをOCIインスタンスに配置する。

# ローカルで作業
scp sa-sns.json ubuntu@<IP>:/tmp/
scp sa-finance.json ubuntu@<IP>:/tmp/

# OCI上で配置
ssh ubuntu@<IP>
sudo mkdir -p /opt/sns-bot /opt/finance
sudo mv /tmp/sa-sns.json /opt/sns-bot/sa.json
sudo mv /tmp/sa-finance.json /opt/finance/sa.json
sudo chmod 600 /opt/sns-bot/sa.json /opt/finance/sa.json

Step 3: .envファイルを作成する

Cloud RunではSecret Managerや環境変数を使っていた設定値を、.envファイルに移行する。

GCP Secret Managerに保存していた値は以下で取得できる。

gcloud secrets versions access latest --secret=<シークレット名> --project=<プロジェクトID>

取得した値を/opt/sns-bot/.envに書き出す。Firestore接続先、APIキー類などをまとめて記述する。


Step 4: Docker Composeで2サービスを起動する

/opt/sns-finance/docker-compose.ymlを作成する。

version: '3.8'

services:
  sns-bot:
    image: sns-bot:latest
    container_name: sns-bot
    restart: unless-stopped
    env_file: /opt/sns-bot/.env
    volumes:
      - /opt/sns-bot/sa.json:/opt/sns-bot/sa.json:ro
    ports:
      - '8081:8080'
    healthcheck:
      test:
        [
          'CMD',
          'python3',
          '-c',
          "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')",
        ]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 30s

  finance-dashboard:
    image: finance-dashboard:latest
    container_name: finance-dashboard
    restart: unless-stopped
    env_file: /opt/finance/.env
    volumes:
      - /opt/finance/sa.json:/opt/finance/sa.json:ro
    ports:
      - '8082:8080'
    healthcheck:
      test:
        [
          'CMD',
          'python3',
          '-c',
          "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')",
        ]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 30s

コンテナイメージはローカルでビルドしてOCIに転送する(またはDockerfileをOCI上に転送してビルド)。

# ローカルでビルド → tarで転送
docker build -t sns-bot:latest ./sns/
docker save sns-bot:latest | ssh ubuntu@<IP> "docker load"

# OCI上で起動
ssh ubuntu@<IP>
cd /opt/sns-finance
docker-compose up -d
docker-compose ps  # State が Up (healthy) になればOK

Step 5: nginxでリバースプロキシを設定する

Cloud Runではパスごとに別サービスを呼び出す構成だったが、OCIではnginxで振り分ける。

/etc/nginx/sites-available/sns-financeを作成する。

upstream sns_bot {
    server 127.0.0.1:8081;
}

upstream finance_dash {
    server 127.0.0.1:8082;
}

server {
    listen 80;
    server_name _;

    # sns-bot エンドポイント(Cloud Schedulerから呼ばれる)
    location /post/  { proxy_pass http://sns_bot; proxy_read_timeout 120s; }
    location /note/  { proxy_pass http://sns_bot; proxy_read_timeout 120s; }
    location /health { proxy_pass http://sns_bot; }
    location /analytics/ { proxy_pass http://sns_bot; proxy_read_timeout 60s; }
    location /report/ { proxy_pass http://sns_bot; proxy_read_timeout 120s; }
    location /pdca/  { proxy_pass http://sns_bot; proxy_read_timeout 120s; }
    location /smoke  { proxy_pass http://sns_bot; proxy_read_timeout 60s; }

    # finance-dashboard(ブラウザからアクセス)
    location / {
        proxy_pass http://finance_dash;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 60s;
    }
}

有効化して再起動する。

sudo ln -s /etc/nginx/sites-available/sns-finance /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

ハマりどころ3選

1. iptablesのデフォルトルール問題

OCI Ubuntu インスタンスはセキュリティリストで80番を開けても、OS内のiptablesがREJECT-allになっているため外部からアクセスできない。

# 確認
sudo iptables -L INPUT --line-numbers
# → 末尾に REJECT all -- anywhere anywhere reject-with icmp-host-prohibited がある

デフォルトのACCEPTルールの直後に追加する(末尾ではなく番号指定で挿入)。

sudo iptables -I INPUT 4 -p tcp --dport 80 -j ACCEPT
sudo apt-get install -y iptables-persistent
sudo netfilter-persistent save

2. Cloud SchedulerはHTTPSを強制する(認証ありの場合)

Cloud SchedulerでOIDC認証付きのジョブを更新してhttp://のURLに変えようとすると、以下のエラーが出る。

HttpRequest.url must start with 'https://' for request with HttpRequest.authorization_header

OCIのHTTPエンドポイントに向ける場合は、認証ヘッダーを削除してHTTPを使う。

gcloud scheduler jobs update http <ジョブ> \
  --location=<リージョン> \
  --project=<プロジェクト> \
  --uri="http://<OCI_IP>/post/auto" \
  --clear-auth-token

13個のジョブを一括で更新するなら、スクリプトにまとめてしまうと楽だ。

3. 1GBのRAMにスワップが必須

VM.Standard.E2.1.MicroはRAM 1GBしかない。2つのコンテナを同居させると起動時のメモリスパイクでOOMになりやすい。

スワップを必ず設定する。

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

2GBあれば、今回の構成(FastAPI + Streamlit)で余裕がある。実際の稼働メモリはsns-botが約64MB、finance-dashboardが約100MBで、合計164MB程度だった。


Step 6: Cloud Schedulerの向き先を変更する

既存のジョブのURIをOCIのIPに変更する。Cloud Runへの向き先をまとめてOCIに切り替えれば移行完了だ。

# 確認コマンド(全ジョブのURI一覧)
gcloud scheduler jobs list --location=<リージョン> --project=<プロジェクト>

# 更新(1ジョブずつ)
gcloud scheduler jobs update http x-bot-morning \
  --location=asia-northeast1 \
  --project=sns-bot-ueno \
  --uri="http://<OCI_IP>/post/auto" \
  --clear-auth-token

更新後、テスト実行で疎通確認する。

gcloud scheduler jobs run smoke-test \
  --location=asia-northeast1 \
  --project=sns-bot-ueno

/healthエンドポイントが{"status":"ok"}を返せば移行成功だ。


移行後のコスト比較

項目 移行前(Cloud Run) 移行後(OCI)
コンピュート GCPクレジット消費 $0(Always Free)
ネットワーク GCPクレジット消費 $0(Always Free)
Firestore 無料枠(50K reads/日) 無料枠(変わらず)
GCS 無料枠(5GB) 無料枠(変わらず)
Cloud Scheduler 無料枠(3ジョブ/月無料、超過は$0.10/ジョブ) 変わらず
月額合計 クレジット終了後に課金発生 $0

GCPのFirestoreやGCS、Cloud Schedulerは無料枠内なので継続利用できる。Cloud Runだけをなくすことで、コストをゼロに抑えつつGCPのマネージドサービスのメリットも活かせる構成になった。

関連記事


まとめ

GCP → OCI の移行で得られたポイントをまとめると:

  • コスト: Cloud Run課金 → $0(OCI Always Free)
  • 作業時間: 1日(ハマりどころ込み)
  • ダウンタイム: Cloud Schedulerの向き先変更は即時切り替え可能、実質ゼロ
  • 運用負荷: Docker Composeとsystemdで自動起動を管理、Cloud Runと遜色ない

「月に数百円の節約」と思うかもしれないが、GCP無料トライアル終了後に何もしなければ課金が始まる。今回の移行で永久に$0のインフラを手に入れた。

副業でサービスを動かしているエンジニアにとって、OCI Always Freeは最強の選択肢の一つだ。GCPの便利なマネージドサービス(Firestore、GCS、Cloud Scheduler)はそのまま使いながら、コンピュートリソースだけをOCIに移す「ハイブリッド構成」も十分現実的な選択肢になる。