【Shlink-UI-Rails】GitHub Actions × Oracle Cloud A1でRailsアプリを自動デプロイする方法|無料枠でCI/CDを実現

技術

はじめに

自作している Shlink-UI-Rails を、Oracle Cloud Free Tierの Ampere A1インスタンスに本番デプロイしました。
GitHubにpushした瞬間から、テスト → Dockerイメージビルド → GHCR保存 → 本番サーバpull → 再起動 → ヘルスチェックまでを完全自動化しています。

仕組みをまとめると:

  1. GitHubにpush
  2. GitHub Actionsでテスト&Dockerビルド
  3. GHCR (GitHub Container Registry) にイメージをpush
  4. Oracle A1インスタンスがpullして再起動
  5. /healthで正常確認、失敗ならロールバック

この流れをコード付きで紹介します。


Oracle Cloud A1とは?

Oracle Cloud Free Tierの「Always Free」枠に含まれる仮想マシンです。

  • CPU: Ampere Altra (ARM64)
  • 無料枠スペック: 最大4 OCPU / 24GB RAM / 200GB Block Volume
  • 特徴:
    • 常時無料で使える
    • ARM64なので低消費電力&高コア数
    • DockerやKubernetesとの相性が良い

ARM64環境という点が特徴的で、GitHub ActionsでのDockerビルド時に platforms: linux/arm64 を指定する必要があります。


GitHub Actionsの設定

Pre-check & Test

まずは「本当にデプロイして良いか」を確認します。

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.4
        env:
          MYSQL_DATABASE: shlink_ui_rails_test
          MYSQL_USER: app
          MYSQL_PASSWORD: apppass
      redis:
        image: redis:7-alpine
    steps:
      - uses: actions/checkout@v5
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.4.5
      - run: bundle exec rspec

RSpecが通らなければ、この時点でデプロイは中止です。


Dockerビルド & GHCR push

CIでビルドしたイメージをGHCRにpushしますdeploy。

- name: Build and push Docker image
  uses: docker/build-push-action@v6
  with:
    context: .
    file: ./Dockerfile.production
    platforms: linux/arm64
    push: true
    tags: ghcr.io/${{ github.repository }}:latest

これで GHCR に

ghcr.io/enjoydarts/shlink-ui-rails:latest

という形で保存されます。


デプロイジョブ

サーバにSSHで接続し、deploy.sh を叩いてデプロイを開始しますdeploy。

- name: Deploy to OCI
  run: |
    ssh -i ~/.ssh/id_ed25519 ${{ secrets.OCI_USERNAME }}@${{ secrets.OCI_HOST }} '
      cd /opt/shlink-ui-rails
      git fetch origin
      git reset --hard origin/main
      bash scripts/deploy.sh
    '

本番サーバ側の処理(deploy.sh)

GHCRからpull

サーバ側ではまず、GHCRにログインして最新イメージをpullしますdeploy。

echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
docker-compose -f docker-compose.prod.yml pull

「本番サーバではビルドしない」ことが大事。完成済みイメージをpullするだけなので安定します。


再起動(ゼロダウンタイム)

pullしたイメージでサービスを再起動します。

docker-compose -f docker-compose.prod.yml up -d --force-recreate

このオプションで既存のコンテナと入れ替えるため、サービスが途切れません。


ヘルスチェック

起動後に /health を叩き、応答があるか確認します。

while ! curl -sf http://localhost:3000/health; do
  sleep 10
done

応答がなければロールバックに移ります。


ロールバック

直前の状態を docker commit で保存してあるので、問題が起きたらすぐ戻せます。

create_backup() {
  local backup_tag="backup-$(date +%Y%m%d-%H%M%S)"
  docker commit "$running_container" "shlink-ui-rails:$backup_tag"
}

通知

最後にSlackやDiscordに通知。

send_deployment_notification() {
  if [[ "$status" == "success" ]]; then
    curl -X POST -H 'Content-type: application/json' \
      --data "{\"text\":\"✅ デプロイ完了: $commit_hash\"}" \
      "$SLACK_WEBHOOK_URL"
  fi
}

運用してみての感想

実際にこのCI/CDを使い続けてみて、まず感じたのは「デプロイが怖くなくなった」ということです。以前は git pull から依存ライブラリの更新、DBマイグレーション、サーバ再起動まで手で流す必要があり、その都度「もし途中で失敗したら…」という不安がつきまとっていました。今はGitHubにpushするだけで、テストが走り、Dockerイメージがビルドされ、サーバが自動で更新されるので心理的負担が激減しました。

サーバ管理も驚くほどシンプルになりました。本番サーバは「イメージをpullして動かすだけ」の存在になったため、Rubyのバージョン調整やnpm依存の地獄に悩まされることがなくなりました。A1インスタンス上でやることはほとんど無く、結果としてトラブルの余地も減っています。

無料でここまでできるのも大きな魅力です。GitHub Actionsの無料分とOracle Cloud Free Tierを組み合わせれば、ゼロ円で本格的なCI/CDを回せます。小規模サービスや個人開発において「お金をかけずに安心して本番を回せる」環境は強力な武器です。

特に印象的だったのは、実際にトラブルが発生した時のことです。環境変数の設定ミスでアプリが起動せず、ヘルスチェックが落ちましたが、自動で直前のイメージにロールバックされ数分で復旧。失敗も体験したことで、この仕組みが本当に実用的だと確信しました。

通知機能も地味ながら便利です。SlackやDiscordに「成功しました」「失敗しました」と結果が届くので、外出中でもスマホで確認でき、ログを追わなくても状況を把握できます。運用面での安心感をさらに強めてくれる要素になっています。


まとめ

GitHub Actions、GHCR、Oracle A1インスタンスを組み合わせた今回の仕組みは、無料でありながら十分に実用的な本番デプロイ環境を実現してくれました。

  • GitHubにpushするとテストとビルドが自動で実行される
  • 完成済みのDockerイメージがGHCRに保存される
  • 本番サーバはイメージをpullして再起動するだけ
  • ヘルスチェックとロールバックにより安定運用が担保される
  • 通知によって開発者は状況をすぐに把握できる

この仕組みがもたらした最大のメリットは「安心感」と「開発速度」です。手動デプロイの緊張感から解放され、pushすれば勝手に本番が更新される環境は、コードを書くことに集中させてくれます。

個人開発や小規模サービスでは「お金をかけずに、でも本番運用に耐える環境を作りたい」というニーズが強いはずです。その答えとして、このGitHub Actions × GHCR × Oracle A1インスタンスの組み合わせは非常に有効だと感じました。

コメント

タイトルとURLをコピーしました