[Ruby] Google Cloud PlatformでSeleniumを動かすまで

# あらすじ

身内向けの小さなサービス作ってまわしてたら、Herokuの無償プランが終わった。なのでfly.ioとRailwayに移民したらRailwayの無償プランもおなくなり。そこで白羽の矢が立ったのがGCP。GCPでSeleniumの定期実行スクリプトをまわすよ！

# GCPのメリットとデメリット
## メリット
とりあえず無料枠があること。基本的に有料になる場合は承認が必要なので、知らず知らず有料になることはないらしいこと。定期実行のCloud Schedulerがあるので、redis+sidekiq+sidekiq schedulerとか使わなくても、cron的な設定も簡単にできること。rubyも対応してること。ただ、Railsが動くのかは知らないし試してない。そういうのはPaaSでやった方がいいのかな、どうなんだろう。

## デメリット
今のところRuby対応がわりと最近というのもあって情報が少ないことが挙げられる。あととにかくUIがわけわからんし、サービスの量も多すぎて何が何だかわからない。デプロイコマンドもやたら長い。

あと自分だけなのかわからないが、再デプロイするとなぜかエラーを吐いてまったくデプロイできなくなることがある。そういうときは一旦Functionsからファンクションを削除して、デプロイし直すという行程が必要になることがある。自分が悪いのかもしれないが、何がおかしいのかわからない。

# 大まかな流れ

1. GCPのアカウントを作る
1. GoogleCloudCLIをインストールする
1. プロジェクトを新規作成する
2. 各種APIを有効化する
3. Cloud Pub/Subでトピックを作成
3. サービスアカウントとやらを作る（よくわかってない）
3. Cloud Functionsを新規作成
3. またサービスアカウントとやらをつくる（よくわかってない）
3. Cloud FunctionsのトリガーにPub/Subで作ったトピックを設定
3. Cloud FunctionsにRubyスクリプトをデプロイ
3. Cloud Schedulerを新規作成し、cron記法でPub/Subにメッセージを送る間隔を設定する

要するに、Cloud SchedulerがPub/Subにn時間ごとにメッセージを送るように設定して、Pub/Subからメッセージを受けたらFunctionsのスクリプトが動くという感じ。えらいまわりくどい、Schedulerから直接Functions叩ければよくない？とか思う。

今回はとりあえず、以上のなかでスクリプトを書いてデプロイするところだけとりあえずメモしておく。

# Cloud Functions内でのスクリプト
## デプロイコマンド
以下、全てCloud Functionsにデプロイするという前提で。

まずはカレントディレクトリ（デプロイ予定のファイルの入ってるディレクトリ）をプロジェクトと紐付けしないといけない。

```
gcloud config set project <プロジェクトID>
```

で、デプロイの呪文がとにかく面倒くさい。

```
gcloud functions deploy <ファンクションID> --memory=<割り当てたいメモリー容量> --runtime <使用言語の指定> --gen2 --timeout=<タイムアウトまでの時間> --entry-point=<エントリーポイント> --region=<地域指定> --trigger-topic=<pub/subのトピック名> --env-vars-file .env.yaml
```
ふつう、こういうのって hogehoge deploy とかいれたら初回にコンソール上で質問に答えていくだけでいいと思うんだけど、これはちゃんとこういうパラメーターをずらずらつけないといけない。めんどくさい。

これらは設定によって違うので、適時変更すべきところや追加すべきところもある。あと二度目以降は省略していい項目もあるが、省略しちゃダメとなってる項目もあって面倒くさいので、もう全部コピペしてる。今回はトリガーはpub/subからのメッセージ送信ということにしてるので、トリガーの指定は-topicになっている。

それと環境変数は.env.yamlで指定する。環境変数自体は個別でも足せるが、複数あるならいちいちここに書き足すのは面倒くさいし、Functionsはエラーで度々消さないといけないことがあるので。

## ファイルの構成
rubyの場合は

- app.rb
- Gemfile
- Gemfile.lock

をとりあえず作っておけばOK。で、Seleniumを動かすのでのちのちはここに「chromedriver」と、「chrome」もしくは「headless-chromium」をいれることになる。

環境変数を指定する場合は

- .env.yaml
- .gcloudignore

こちらを作り、.gcloudignoreには.env.yamlなどをかいておいてデプロイされないようにしておく。

## Gemfile

Gemfile.lockはとくに中身なにもかかなくてよし。
Gemfileについてはとりあえず

```ruby:Gemfile
source "https://rubygems.org"

gem "functions_framework"
```

これらはつけておく。あとは普通どおり、追加したいgemを足していくだけ。ちなみに、Gemfileの更新が一番なぜか再デプロイ時にひっかかりやすい。よくわからんけど、そうなったら上に書いてるように一旦Functionを消してしまってデプロイし直した方が速い。

## app.rb
まずは最初のフォーマットとしては

```ruby:app.rb

require "functions_framework"
require "base64"

FunctionsFramework.cloud_event "エントリポイント名" do

    puts "hello, world"

end

```

base64の呼び出しは必要なのかわからないが、Functionsでrubyの新規作成するとこのコードを書いてくるので、知らんけどいるのかもしれない。``.cloud_event``後のエントリポイント名が、上のコマンドでも書いたようにエントリポイントというやつなので、必ずスクリプト内にいれておかないといけない（よくわかってない）

とりあえずここまでやったら一旦デプロイして、pubsubから適当にメッセージを送信して、functionsのログタブからログを確認して、ちゃんとへろーわーるどされてるかを試しておく。Pub/SubとFunctionsの紐付けは他の方の記事をとりあえず参照。

## Selenium導入の大まかに必要な行程
で、肝心のSelenium導入までの流れ。Gemfileに``gem 'selenium-webdriver'``を書き入れておくのは当然なので省略。

### デプロイするファイル
必要なものとしては、chromeもしくはheadless-chromium、chromedriverの二つである。chrome本体を持ってきてもいいんだけどそれはメモリを喰っちゃうので、ヘッドレス用にしてあるheadless-chromiumの方が多分望ましい。

この二つは[Github gcf-packs](https://github.com/ryfeus/gcf-packs)から抜き出してくるのが手っ取り早い。これを解凍して、中のpack.zipも解凍すると``chromedriver``と``headless-chromium``というファイルが入っているので、これをデプロイするフォルダにコピーしておく。

### 具体的なコード

```ruby:app.rb
require "functions_framework"
require "base64"

FunctionsFramework.cloud_event "エントリポイント名" do

    require 'selenium-webdriver'

    Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver'
    options = Selenium::WebDriver::Chrome::Options.new(binary:"./headless-chromium")
    options.add_argument('--headless')
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')
    driver = Selenium::WebDriver.for :chrome, options: options

    driver.navigate.to 'https://www.yahoo.co.jp/'
    puts driver.title

end
```

これでyahoo japanのタイトルがログに流れてきたら成功。おもったよりかんたん。

pythonで同様の記事を書いてる先行者の例をみると、tmpフォルダにいれないとアクセスできないから、tmpフォルダへのコピーとパーミッション設定をしないといけないという内容が書かれている。

でも自分が試した限り、別にそんなことしなくても普通に使える。gen1だとそういう風にしないといけないとかあるんだろうか？謎。