Cloud Function でhttpリクエストを受け取りPubSubを経由して別のFunctionを起動する、という機能を開発した際のローカル環境構築メモ。
Function用のイメージのビルド
Gemfile
source "https://rubygems.org" gem "functions_framework", "~> 0.7" gem "google-cloud-pubsub"
docker/function/Dockerfile
# Build: docker build -t function -f docker/function/Dockerfile . FROM ruby:3.2.2 WORKDIR /function COPY Gemfile /function/Gemfile RUN bundle config --local without "development test" \ && bundle install
ソースコードをvolumeで取り込んで bundle exec functions-framework-ruby --target xxxx
でターゲットを切り替えることで同じイメージで http 側とサブスクライバ側のコンテナを起動する想定。
http側のFunction
protobuf形式のメッセージを送信したいので定義。
message.proto
syntax = "proto3"; option ruby_package = "Message::"; message Request { int64 id = 1; string body = 2; }
下記コマンドでrbファイルを生成
mkdir lib protoc --ruby_out=./lib ./message.proto
http.rb
require "functions_framework" require "google/cloud/pubsub" require_relative "lib/message_pb" TOPIC = ENV["TOPIC_ID"] FunctionsFramework.http "hello" do |request| FunctionsFramework.logger.info("hello http") pubsub = ENV["PUBSUB_EMULATOR_HOST"].nil? ? Google::Cloud::Pubsub.new : Google::Cloud::Pubsub.new(project_id: "emulator") topic = pubsub.topic(TOPIC) message = Message::Message.new(id: 123, body: "hello!") message = Message::Message.encode(message) topic.publish(message) { "message" => "OK" } end
イベント側のファンクション
event.rb
require "base64" require "functions_framework" require_relative "lib/message_pb" FunctionsFramework.cloud_event "hello-event" do |event| FunctionsFramework.logger.info("hello event") message = Message::Message.decode(Base64.decode64(event.data["message"]["data"])) FunctionsFramework.logger.info("id: #{message.id}, body: #{message.body}") end
PubSubエミュレータ
この記事が良さそうなので参考にさせてもらう。
docker/pubsub/Dockerfile
# Build: docker build -t pubsub -f docker/pubsub/Dockerfile . FROM gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators RUN apt-get update && \ apt-get install -y git python3-pip netcat && \ git clone https://github.com/googleapis/python-pubsub.git WORKDIR /python-pubsub/samples/snippets RUN pip3 install virtualenv && \ virtualenv env && \ . env/bin/activate && \ pip3 install -r requirements.txt COPY ./docker/pubsub/entrypoint.sh ./ EXPOSE 8085 ENTRYPOINT ["./entrypoint.sh"]
docker/pubsub/entrypoint.sh
#!/bin/bash set -em export PUBSUB_PROJECT_ID=$PROJECT_ID export PUBSUB_EMULATOR_HOST=0.0.0.0:8085 gcloud beta emulators pubsub start --project=$PUBSUB_PROJECT_ID --host-port=$PUBSUB_EMULATOR_HOST --quiet & while ! nc -z localhost 8085; do sleep 0.1 done . env/bin/activate python3 publisher.py $PUBSUB_PROJECT_ID create $TOPIC_ID python3 subscriber.py $PUBSUB_PROJECT_ID create-push $TOPIC_ID $SUBSCRIPTION_ID $PUSH_ENDPOINT fg %1
chmod +x docker/pubsub/entrypoint.sh
を忘れずに
docker-compose でまとめる
docker-compose.yaml
version: '3.8' services: pubsub: image: pubsub:latest restart: always environment: - PROJECT_ID=emulator - TOPIC_ID=event-topic - SUBSCRIPTION_ID=event-subscription - PUSH_ENDPOINT=http://event:8080 http: image: function:latest environment: - TOPIC_ID=event-topic - PUBSUB_EMULATOR_HOST=pubsub:8085 - PUBSUB_PROJECT_ID=emulator ports: - 8080:8080 volumes: - .:/function entrypoint: ["bundle", "exec", "functions-framework-ruby", "--source=http.rb","--target=hello"] event: image: function:latest volumes: - .:/function entrypoint: ["bundle", "exec", "functions-framework-ruby", "--source=event.rb","--target=hello-event"]
docker-compose up
で起動
これで、http://localhost:8080/ にアクセスすると http側のfunctionが呼ばれ、pubsubを経由してイベント側のfunctionが呼ばれることがログから確認できる