GCP CloudRunでrails cする方法

# 背景

普段はAWSでサーバー構築されている案件を担当することが多いWebエンジニア(主にRuby/RoR)で、必要であればインフラ周りも調べつつ対応するレベル感です。

大人の都合上GCPを利用する必要があり、アプリ自体はCloudRunで構築されてました。
GCP自体私はほぼ初。
CloudRunではAmazon ECS Execみたいなサービスもなく、rails cして対応ができないことが判明(ECS感覚でできると思ってた・・・)

あまり情報がないので記事にしてみた。

# 結論

今回はGCEでrails c実行専用のVMを(AWSのEC2のようなもの)構築し、DB(Cloud SQL)に繋いで必要な時だけ起動して利用する方法で対応しました。

理由としては下記2点です

- AWS EC2の感覚で構築できそうなので構築工数がかからない
- 必要な時に起動・そうでないときは停止していればいいのでランニング費用を抑えられる

調べた中にはGKE Autopilot でという記事も見かけたりしましたが、費用感がわからなかったり学習コストがかかりそうな気がしたので今回は外しました。
詳しい方いらっしゃればコメントくださると嬉しいです。

https://zenn.dev/nownabe/articles/rails-on-google-cloud#rails-%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB

# 前提

- gcloud(Google CloudのCLLI)で操作ができること
- IAMの権限が操作できる権限であること(今回は編集者ロール)

https://cloud.google.com/sdk/docs/install?hl=ja

# 1. GCE VM構築

Compute Engine > VMインスタンス > インスタンスを作成

下記以外はデフォルト設定です。
インスタンスタイプは今回コンソール用なので最低限のspecとしていますが、用途に合わせてでいいと思います。

- VMインスタンス名：判別しやすい名称に
- 東京リージョン・ゾーン：asia-northeast1-a
- マシンタイプ：t2d-standard-1（1 vCPU、4 GB メモリ）
- ネットワーキング
    - ネットワーク：test-vpc
    - サブネットワーク: test-stg-subnet IPv4 => `インスタンスに、サブネットワークの範囲の 
IPv4 アドレスを割り当てます。別のサブネットワーク内のインスタンスは、同じネットワークに属している限り、内部 IP を使用して互いに通信できます。`とのことなので、この設定で本番運用も大丈夫だった。
    - IPスタックタイプ：IPv4
    - カスタムエフェメラルIPアドレス：自動
    - プライマリ内部IPv4アドレス：エフェメラル
    - ネットワークサービスティア：スタンダード
    - ネットワークタグ：ssh-access => VPCで設定したファイアーウォールルールの名称(sshアクセスをIPで制御している)

- ブートディスク
    - オペレーティングシステム：Ubuntu
    - バージョン：Ubuntu 24.04 LTS(Intelで)

# 2. サーバー内構築

## sshログイン

gcloud compute sshすることで自動的に鍵が登録される模様

authorized_keysに自動で情報が入っていた(Added by Googleとコメントあり)

```
% gcloud compute ssh [インスタンス名]
```


また、デフォルトでIAMの権限による制御もされているので安心

> Google Cloud コンソールまたは Google Cloud CLI を使用して VM に接続する場合、Compute Engine はこれらの構成をユーザーに代わって実行します。

https://cloud.google.com/compute/docs/instances/ssh?hl=ja&_gl=1*1yiy8cu*_ga*Nzk2MDI3NTkyLjE3Mjk0ODQ3Njg.*_ga_WH2QY8WWF5*MTczMjU3Nzk1MS4zNy4xLjE3MzI1ODM5MzUuNTIuMC4w

今回はすでに「編集」ロールが与えられていたので操作は問題なかったが、細かい制御が必要であれば用途により調整は可能。

> 必要な IAM 権限は以下のとおりです。
>
> ・compute.instances.use 権限等 ( Compute インスタンス管理者 (v1) (roles/compute.instanceAdmin.v1) ロール推奨)
・VM にサービスアカウントがアタッチされている場合はサービスアカウントまたはプロジェクトに対する iam.serviceAccounts.actAs 権限 ( サービス アカウント ユーザー ロール等)
・OS ログイン機能 を利用する場合は OS ログインに必要な IAM ロール ( roles/compute.osAdminLogin または roles/compute.osLogin )

https://cloud.google.com/iam/docs/roles-overview?hl=ja#basic

https://blog.g-gen.co.jp/entry/ssh-to-compute-engine-vm#gcloud-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89

## test_userの作成

このままでもいいが一応ユーザーを作成しておく

```
# gcloud compute sshでIAM権限による制御・キー認証になっている・IP制限もかけているため今回はパスワードなしにしています
# https://cloud.google.com/compute/docs/instances/ssh?hl=ja&_gl=1*1yiy8cu*_ga*Nzk2MDI3NTkyLjE3Mjk0ODQ3Njg.*_ga_WH2QY8WWF5*MTczMjU3Nzk1MS4zNy4xLjE3MzI1ODM5MzUuNTIuMC4w

username@instance-20241126-002205:~$ sudo adduser test_user --disabled-password
info: Adding user `test_user' ...
info: Selecting UID/GID from range 1000 to 59999 ...
info: Adding new group `test_user' (1004) ...
info: Adding new user `test_user' (1004) with group `test_user (1004)' ...
info: Creating home directory `/home/test_user' ...
info: Copying files from `/etc/skel' ...
Changing the user information for test_user
Enter the new value, or press ENTER for the default
	Full Name []: # エンターでスキップ
	Room Number []: # エンターでスキップ
	Work Phone []: # エンターでスキップ
	Home Phone []: # エンターでスキップ
	Other []: 
Is the information correct? [Y/n] Y
info: Adding new user `test_user' to supplemental / extra groups `users' ...
info: Adding user `test_user' to group `users' ...

# sudo権限で実行するとtest_userにパスワード設定していないのにパスワードを求められるので下記を追加実行
# https://kazuhira-r.hatenablog.com/entry/20160116/1452933679
username@instance-20241126-002205:~$ sudo sh -c 'echo "test_user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/test_user'
username@instance-20241126-002205:~$ sudo chmod 440 /etc/sudoers.d/test_user
```
## GitHub登録用公開鍵を作成

今回はGitHubからgit clone/git pullでソースコードを持ってくるのでGitHub DeployKeysの設定を行う

```
username@instance-20241126-002205:~$ mkdir ~/.ssh
username@instance-20241126-002205:~/.ssh$ cd ~/.ssh
username@instance-20241126-002205:~/.ssh$ ssh-keygen -t ed25519 -C "GitHub_key"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/test_user/.ssh/id_ed25519): 
Enter passphrase (empty for no passphrase): # 必要であればパスワード設定(不要であればそのままエンター)
Enter same passphrase again: # 必要であれば上記と同じパスワード設定(不要であればそのままエンター)
Your identification has been saved in /home/test_user/.ssh/id_ed25519
Your public key has been saved in /home/test_user/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:xxxxxxxx GitHub_key
The key's randomart image is:

# 鍵ができている
test_user@instance-20241126-002205:~/.ssh$ ls -l
total 8
-rw------- 1 test_user test_user 484 Nov 26 01:27 id_ed25519
-rw-r--r-- 1 test_user test_user 112 Nov 26 01:27 id_ed25519.pub

# コピーしてGitHub DeployKeysにReadOnlyで登録
test_user@instance-20241126-002205:~/.ssh$ cat id_ed25519.pub
```

## rbenvを入れてrubyをインストール

```
# 必要なソフトウェアをインストール
test_user@instance-20241126-002205:~$ sudo apt install -y libssl-dev libreadline-dev zlib1g-dev libyaml-dev
test_user@instance-20241126-002205:~$ sudo apt install -y gcc g++ make

# postgresqlで必要
test_user@instance-20241126-002205:~$ sudo apt install -y libpq-dev

# JSの実行環境がないよというエラーの解消
# Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
test_user@instance-20241126-002205:~$ sudo apt install -y nodejs

# rbenvインストール
test_user@instance-20241126-002205:~$ git clone https://github.com/sstephenson/rbenv.git .rbenv
test_user@instance-20241126-002205:~$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
test_user@instance-20241126-002205:~$ echo "# For rbenv" >> ~/.bashrc
test_user@instance-20241126-002205:~$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
test_user@instance-20241126-002205:~$ echo 'eval "$(~/.rbenv/bin/rbenv init -)"' >> ~/.bashrc
test_user@instance-20241126-002205:~$ source ~/.bashrc
test_user@instance-20241126-002205:~$ which rbenv
/home/test_user/.rbenv/bin/rbenv

# ruby3.3.3をインストール(結構時間かかる)
test_user@instance-20241126-002205:~$ rbenv install 3.3.3
==> Downloading ruby-3.3.3.tar.gz...
-> curl -q -fL -o ruby-3.3.3.tar.gz https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 21.0M  100 21.0M    0     0  16.6M      0  0:00:01  0:00:01 --:--:-- 16.6M
==> Installing ruby-3.3.3...
-> ./configure "--prefix=$HOME/.rbenv/versions/3.3.3" --enable-shared --with-ext=openssl,psych,+
-> make -j 1
-> make install
==> Installed ruby-3.3.3 to /home/test_user/.rbenv/versions/3.3.3

NOTE: to activate this Ruby version as the new default, run: rbenv global 3.3.3

test_user@instance-20241126-002205:~$ rbenv global 3.3.3
test_user@instance-20241126-002205:~$ ruby -v
ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [x86_64-linux]
```

## rails cできるようにする

```
# 作業ディレクトリ作成
test_user@instance-20241126-002205:~/.ssh$ cd
test_user@instance-20241126-002205:~$ mkdir test_app
test_user@instance-20241126-002205:~$ cd test_app
test_user@instance-20241126-002205:~/test_app$ mkdir staging
test_user@instance-20241126-002205:~/test_app$ cd staging

# clone
test_user@instance-20241126-002205:~/test_app/staging$ git clone git@github.com:xxxxxxx.git
test_user@instance-20241126-002205:~/test_app/staging$ ls -l
total 4
drwxrwxr-x 16 test_user test_user 4096 Nov 26 01:38 test_app_repo

# rails cできるようにする
test_user@instance-20241126-002205:~/test_app/staging$ cd test_app_repo
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ git branch
* main
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ git checkout staging
  main
* staging
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ bundle config set --local path vendor/bundle # bundle install先のデフォルトをvendor/bundleに設定
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ touch .env
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ vi .env # 必要であればここで環境変数の設定をする
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ bundle install

## Missing `secret_key_base` for 'staging' environment, set this string with `bin/rails credentials:edit` (ArgumentError)への対処法
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ bin/rails secret
<ランダムコード>
test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ EDITOR=vim bin/rails credentials:edit --environment staging # ここで上述の<ランダムコード>を設定して保存する

test_user@instance-20241126-002205:~/test_app/staging/test_app_repo$ bin/rails c -e staging
I, [2024-11-26T12:05:17.521791 #18876]  INFO -- : [dotenv] Loaded .env
I, [2024-11-26T12:05:18.771302 #18876]  INFO -- : [CORS] Using the following CORS setting: https://staging.test.jp
Loading staging environment (Rails 7.1.3.4)
[1] pry(main)> 
```