Cloud Pub/Sub、Cloud Functions エミュレーターを Docker Compose で立ち上げる
概要
Cloud Functions を PubSub 経由で起動するものとして作るときに、ローカルで気軽に試せると開発がスムーズに進むと思います。
今回はその解決法の1つとして Cloud Pub/Sub、Cloud Functions の各エミュレーターを Docker Compose で立ち上げて動かしてみようと思います。
Cloud Pub/Sub のサブスクリプションに関しては push 型を想定しています。
Cloud Functions に関しては今回は Python で書いていますが、他の言語でも問題ないと思います。
検証環境
macOS Ventura 13.2.1
❯ docker --version
Docker version 20.10.11, build dea9396
サンプルソース
Github にサンプルソースを上げたので、試したい方はお使いください。
ディレクトリ構成
❯ tree
.
├── LICENSE
├── Makefile
├── README.md
├── docker
│ ├── function
│ │ └── Dockerfile
│ └── pubsub
│ └── Dockerfile
├── docker-compose.yaml
├── function
│ └── main.py
├── pubsub
│ ├── my_publisher.py
│ └── test.json
└── scripts
└── entrypoint.sh
6 directories, 10 files
/function 以下には関数の内容を格納しています。
/pubsub 以下にはメッセージをパブリッシュするためのロジックとメッセージ内容がそれぞれファイルでおいてあります。
/scripts/entrypoints.sh は pubsub コンテナ立ち上げ時に実行するスクリプトです。コンテナ内で pubsub エミュレーターを立ち上げ、トピックと push 型のサブスクリプションを作成しています。
Docker Compose
version: '3.8'
services:
function:
build:
context: .
dockerfile: docker/function/Dockerfile
volumes:
- ./function:/workspace
environment:
- PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
ports:
- 8080:8080
tty: true
command: functions-framework --target=main --signature-type=event --debug
pubsub:
build:
context: .
dockerfile: docker/pubsub/Dockerfile
volumes:
- ./scripts:/python-pubsub/samples/snippets/scripts
- ./pubsub:/python-pubsub/samples/snippets/pubsub
ports:
- 8085:8085
env_file: .env
tty: true
command: ./scripts/entrypoint.sh
function に関しては、Dockerfile でエミュレーターとして動かすためのライブラリである functions-framework を使用してサーバーを立ち上げています。
pubsub に関してはマウントしたスクリプトファイルを実行し、そのなかでエミュレーターを動かしています。Cloud Functions
FROM python:3.7
WORKDIR /workspace
RUN pip3 install functions-framework
Dockerfile 内では functions-framework をインストールしています。
import base64
def main(event, _):
pubsub_message = base64.b64decode(event['data']).decode('utf-8')
print(f'pubsub_message: {pubsub_message}')
return "OK"
関数内では検証のためにイベントからメッセージを抽出して標準出力に流すことのみ行います。
Cloud Pub/Sub
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 -r requirements.txt
Google が提供しているエミュレーターのイメージを使います。
トピックとサブスクリプションを作成するコマンドを使うためのソースをクローンします。
#!/bin/bash
# pubsub コンテナ立ち上げ時に使用
set -em
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
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
コンテナ立ち上げ時に実行するスクリプトです。
エミュレーターをバックグラウンドで立ち上げ、トピックとサブスクリプションを作成し、最後に立ち上げたジョブをフォアグラウンドに戻しています。
# local 開発で使用。json データを publish するために必要。
import sys
from google.cloud import pubsub_v1
def publish_message(project_id: str, topic_id: str, file_path: str):
# パブリッシャークライアントを作成
publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)
# JSON ファイルを読み込み
with open(file_path) as f:
data_str = f.read()
# メッセージをパブリッシュする
data = data_str.encode("utf-8")
future = publisher.publish(topic_path, data)
future.result()
return 0
if __name__ == "__main__":
args = sys.argv
project_id = args[1]
topic_id = args[2]
file_path = args[3]
publish_message(project_id, topic_id, file_path)
{
"text": "sample"
}
publish_message 関数を実行することで sample.json の内容をパブリッシュできます。
Makefile
# .env を読み込む
include .env
.PHONY:build
build:
docker-compose build
.PHONY:up
up:
docker-compose up
.PHONY:down
down:
docker-compose down
.PHONY:reup
reup:
make down up
.PHONY:publish
publish:
docker-compose exec pubsub python3 pubsub/my_publisher.py $(PUBSUB_PROJECT_ID) $(TOPIC_ID) ./pubsub/sample.json
make publish でパブリッシュできます。pubsub コンテナ内で pubsub/my_publisher.py を実行しています。
実行
❯ make up
function_1 | * Running on all addresses (0.0.0.0)
function_1 | * Running on http://127.0.0.1:8080
function_1 | * Running on http://172.22.0.2:8080
pubsub_1 | Endpoint for subscription is: http://function:8080
❯ make publish
pubsub_1 | [pubsub] Feb 28, 2023 6:09:46 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
pubsub_1 | [pubsub] INFO: Detected HTTP/2 connection.
function_1 | pubsub_message: {
function_1 | "text": "sample"
function_1 | }
function_1 | 172.22.0.3 - - [28/Feb/2023 06:09:46] "POST / HTTP/1.1" 200 -
function がメッセージのログを出力しました。
雑感
pubsub の Dockerfile でクローンしたソースのなかにもパブリッシュを実行できる関数があるのですが、メッセージ内容が変えられなかったため(もし変えられた方いらっしゃればコメント頂ければ嬉しいです)、今回自前のパブリッシュ関数を用意しました。
用意するのは多少手間がかかりますが、一度作ってしまえば手軽にテストできるのでやはり開発しやすいです。
pubsub のベースイメージがでかいので、そこが今回における一番の課題かなと思います。
Discussion