1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Cloud FunctionsのRubyがベータ公開されたので使ってみた

Posted at

これはなに?

先日、Google Cloud FunctionsにRubyのサポートが入ったので開発者体験を見ながら検証してみました。なお、私は普段AWS Lambdaを使っておりCloud Functions自体に慣れていないので何かお気づきがあればお知らせください。

Functionの実装

Function Frameworkというgemを使って実装をします。HTTPのハンドラーの他に各種イベントハンドラーやテストを便利にできるモジュールが揃っています。AWSでもこういうツールがほしかったですね。SAMとかServerless Frameworkもありますが、Rubyから利用できてデプロイツールから独立しているところが好印象です。AWSさん、お願いできませんかね〜
さっそくgemも入れて最小限の実装をしてみましょう。
app.rbGemfile必要だそうです。あと、Lambdaで必要なvendor/bundleは入れてもいいですが、デプロイ時にインストールされるので今回はなしにします。

app.rb
# frozen_string_literal: true

require 'functions_framework'

# 一度初期化して何回も呼び出しをしたくないものはon_startupのブロック内に記述する
# https://googlecloudplatform.github.io/functions-framework-ruby/v0.7.1/file.writing-functions.html#startup-tasks
FunctionsFramework.on_startup do
  require 'json'
  require 'rack'
end

# ここのhandlerはCloud Functionのentrypoint名になる
FunctionsFramework.http 'handler' do |request|
  # requestはRack::Requestのインスタンス
  body_hash = JSON.parse(request.body.read, symbolize_names: true)
  raise StandardError, 'hoge not supported' if body_hash.key? :hoge

  { body: body_hash }
# 何もしなくても500エラーが返るが、bodyをカスタマイズする場合はRack::Responseのインスタンスを返す
rescue StandardError => e
  Rack::Response.new(
    JSON.generate({ error: e }), 500, { 'Content-Type' => 'application/json' }
  )
end
source "https://rubygems.org"

gem "functions_framework", "~> 0.7"
gem "rack"

# 現仕様ではdevelopment, testグループのgemはインストールされないそう
# https://sue445.hatenablog.com/entry/2021/01/24/000623
group :development, :test do
  gem "rspec"
end

テスト

テスト、書きますよね?
今回はRSpecで書きましたがminitestも対応されています。Function Frameworkさまざまですね。Rubyコードではなく実際のリクエストとレスポンスが使えるのも嬉しいところです。
RSpecのボイラープレートは長いので割愛します。

spec/app_spec.rb
require 'rspec'
require 'functions_framework/testing'

describe 'CloudFunction handler' do
  include FunctionsFramework::Testing

  subject(:response) do
    load_temporary 'app.rb' do # => spec/../app.rbというパスに解釈される
      request = make_post_request('https://example.com/foo', request_body, ['Content-Type: application/json'])
      call_http('handler', request)
    end
  end

  context '正常系' do
    let(:request_body) { { piyo: true }.to_json }
    let(:expected_response_body) { { 'body' => { 'piyo' => true } } }
    let(:response_body) { JSON.parse(response.body[0]) }
    let(:response_status) { response.status }

    it '成功する' do
      expect(response_body).to eq expected_response_body
      expect(response_status).to eq 200
    end
  end

  context '異常系' do
    let(:request_body) { { hoge: true }.to_json }
    let(:expected_response_body) { { 'error' => 'hoge not supported' } }
    let(:response_body) { JSON.parse(response.body[0]) }
    let(:response_status) { response.status }

    it '失敗する' do
      expect(response_body).to eq expected_response_body
      expect(response_status).to eq 500
    end
  end
end

ローカルで実行する

テストが通ったところで必要ないかもしれませんが、SAMたちと違ってDockerでなくてもできるということで試してみましょう。

$ bundle exec functions-framework-ruby --target=handler --port 8888
I, [2021-01-29T00:40:57.630286 #41870]  INFO -- : FunctionsFramework v0.7.0
I, [2021-01-29T00:40:57.630348 #41870]  INFO -- : FunctionsFramework: Loading functions from "foo.rb"...
I, [2021-01-29T00:40:57.631502 #41870]  INFO -- : FunctionsFramework: Looking for function name "handler"...
I, [2021-01-29T00:40:57.631530 #41870]  INFO -- : FunctionsFramework: Starting server...
I, [2021-01-29T00:40:57.763187 #41870]  INFO -- : FunctionsFramework: Serving function "handler" on port 8888...

# 別ターミナルにて
$ curl localhost:8888 -d '{ "foo": "bar" }'
{"body":{"foo":"bar"}}%
I, [2021-01-29T00:41:19.113264 #41870]  INFO -- : FunctionsFramework: Handling HTTP POST
I, [2021-01-29T00:41:32.663198 #41870]  INFO -- : FunctionsFramework: Caught SIGINT; shutting down server...

デプロイする

一旦コンソールからやります。ちゃんとやるときはGCSにzipを上げる運用でもよさそうです。
アップロード手順は割愛しますが、注意するところは以下のとおりです。

  • entrypointが入っているファイルの名前がapp.rbであること
  • entrypoint (本記事ではhandler)を指定すること
  • private gemをインストールする場合は、当然ながらBUNDLE_GITHUB__COMを環境変数に設定するなど認証を通すこと。ドキュメントではローカルに配置するやりかたも提示されています

Lambdaと同様にzipに入れてアップロードしましょう。デプロイが終わるまで2分くらいかかって体感的に遅いです。今のところ、デプロイのスピードに関してはLambdaが完全に上ですね。

zip -x '*.git*' -r function.zip app.rb Gemfile Gemfile.lock

デプロイされたFunctionを試す

アップロードしたらさっそくリクエストを投げてみましょう。Triggerタブに呼び出し用リンクが記載されています。Lambdaと同様に、コンソールからテスト実行がしたり、cliから直接実行したりすることもできます。標準的ですね。

さいごに

これでひと通り試すことができました。Function Framework、褒めても褒めたりないですね!
さっそく本番運用を始めたいところですが、まだBetaでした。
Ruby使いとしてプラットフォームが1つ増えて嬉しい限りです。
みなさまも快適なRubyライフを!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?