Slide 1

Slide 1 text

Python用のマイクロサービスフレームワーク を探す旅 @yukinagae

Slide 2

Slide 2 text

目的 golangの Goa のようなマイクロフレームワークに似たようなのを Pythonでもほしい 2

Slide 3

Slide 3 text

背景 webアプリではないのでマイクロなフレームワークを使っていき 言語はGo/Pythonで絞りたい 学習コストを減らす 検証やハマることを減らしたい キャッチアップしやすい 機械学習システムは似たような構成になりやすい バッチで学習しDBにデータをストア: Python DBからAPIでデータをサービングする: Go or Python 3

Slide 4

Slide 4 text

Goaとは? Goa is a Go framework for writing microservices that promotesbest practice by providing a single source of truth from which server code, client code, and documentation is derived. マイクロサービス用のフレームワーク コードからサーバサイドのスケルトンやドキュメント(swagger) を生成できる 4

Slide 5

Slide 5 text

こんな感じのAPIデザインをgoで記述する var _ = Service("calc", func() { Method("add", func() { Payload(func() { Field(1, "a", Int, "Left operand") Field(2, "b", Int, "Right operand") Required("a", "b") }) Result(Int) HTTP(func() { GET("/add/{a}/{b}") }) GRPC(func() { }) }) }) 5

Slide 6

Slide 6 text

ポチッとな(`・ω・´) goa gen calc/design 6

Slide 7

Slide 7 text

省略しているがいろいろ生成してくれる gen ├── calc │ └── *.go ├── grpc │ ├── calc │ │ ├── client │ │ │ └── *.go │ │ ├── pb │ │ │ ├── calc.pb.go │ │ │ └── calc.proto # <- protobuffer │ │ └── server │ │ └── *.go │ └── cli │ └── calc │ └── cli.go └── http ├── calc ├── cli ├── openapi.json # <- swagger ファイル └── openapi.yaml # <- swagger ファイル 7

Slide 8

Slide 8 text

ロジックを実装 あとは生成されたうちの2~3ファイルにロジックを実装するだけ。 生成された空メソッド 実装する func (s *calcsrvc) Add(ctx context.Context, p *calc.AddPayload) return 0, nil } func (s *calcsrvc) Add(ctx context.Context, p *calc.AddPayload) return p.A + p.B, nil } 8

Slide 9

Slide 9 text

Goaのいいところ スキーマファースト スキーマ作成 → 開発というワークフローができる http + gRPCの両方使える 9

Slide 10

Slide 10 text

Pythonでも似たようなのほしいのでは (`・ω・´) 10

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

Nameko(なめこ) A microservice framework for Python. マイクロサービス用のpythonフレームワーク ビジネスロジックの実装に集中できる 拡張いろいろできる(どのプロトコルやDBにもサービス拡張でき る) 名前がよい 12

Slide 13

Slide 13 text

Nameko(なめこ)の由来 Nameko takes its name from the Japanese mushroom, which grows in clusters. クラスタ(?)に成長する日本のキノコである "なめこ" が由来 13

Slide 14

Slide 14 text

HTTPの場合 アノテーションで @http を指定 import json from nameko.web.handlers import http class HttpService: name = "http_service" @http('GET', '/get/') def get_method(self, request, value): return json.dumps({'value': value}) 14

Slide 15

Slide 15 text

gRPCの場合 アノテーションで @grpc を指定 grpc = Grpc.implementing(exampleStub) class GrpcService: name = "grpc_service" @grpc def stream_unary(self, request, context): messages = [] for req in request: message = req.value * (req.multiplier or 1) messages.append(message) return ExampleReply(message=",".join(messages)) ※ namekoのコアにはgRPCが入っていないが nameko‑grpc という拡張を 使用可能 15

Slide 16

Slide 16 text

使い方 HTTPサービスを起動 nameko run http_service curlで叩いてみる(`・ω・´) $ curl localhost:8000/get/123 {'value': 123} ためしにサンプルプロジェクト作成した。 ただDBからselectするだけ(/・ω・)/ yukinagae/nameko‑api‑template 16

Slide 17

Slide 17 text

軽く触ってみての感想 ざっと調べた中では一番イメージに近いPythonライブラリ コーディング量も少なくて済みそう exampleレポジトリあるので結構参考になりそう DB周りのやり方はまだよくわかってない sqlalchemyは対応してるのを軽くサンプルで動作確認した swaggerのコード生成はないので作る必要あり 17

Slide 18

Slide 18 text

namekoの足りないところ スキーマファーストではない ふつうにエントリーポイントを作成する感じ http or gRPCはそれぞれ別に作らないといけない swaggerのコード生成 18

Slide 19

Slide 19 text

結論 Flaskでよくない? 19

Slide 20

Slide 20 text

Flaskから置き換えるほどか?? 実績 + ノウハウ + ドキュメントが多い namekoを新しく使うメリットがどこまであるか? 正直今後の新サービスは9割方go(Goa)でいけるのでは? 20

Slide 21

Slide 21 text

ただやはり諦めきれない...... namekoにもいいところある(`・ω・´) Flaskよりミニマム API部分はselectしたいだけならばこれくらいで十分 gRPCのエントリーポイントをアノテーションで記述できる 可読性高いのでは 21

Slide 22

Slide 22 text

これを改善すればよいのでは? スキーマからコード生成 swagger生成 22

Slide 23

Slide 23 text

スキーマからコード生成だけやってみる 方法案 × yamlから生成 × swaggerから生成 ○ PythonでDSLっぽくやる ← 今回はこの方法(`・ω・´) ※ テンプレートエンジンは Jinja2 を使用 23

Slide 24

Slide 24 text

作ってみた(`・ω・´) yukinagae/nameko‑design 24

Slide 25

Slide 25 text

端的に地獄......( ˘ω˘)スヤァ service = Service('http_service') \ .Title('This is a http service') \ .Method('liveness') \ .Description('liveness probe') \ .Result(str) \ .HTTP(GET, '/liveness') service.generate() # コード生成 25

Slide 26

Slide 26 text

Pythonicに書けるようにしたつもり このPythonファイルをスキーマとする with Service('http_service'): Title('This is a http service') with Method('liveness'): Description('liveness probe') Result(str) HTTP(GET, '/liveness') with Method('readiness'): Description('readiness probe') Result(str) HTTP(GET, '/readiness') 26

Slide 27

Slide 27 text

これを叩くだけ cd nameko-design poetry run python cli/main.py 27

Slide 28

Slide 28 text

これを生成(`・ω・´) from nameko.web.handlers import http class HttpService: name = 'http service' @http('GET', '/liveness') def liveness(self, request) -> str: pass @http('GET', '/readiness') def readiness(self, request) -> str: pass 28

Slide 29

Slide 29 text

まだTODOたくさんあるけれど Configure http url and port Add URL parameter and type Add payload (name, type, description, position etc) Add validation Add gRPC server Generate proto files Generate swagger json 29

Slide 30

Slide 30 text

こんな感じでできたらよさそう(´∀` ) with Service('example_service'): Title('This is an example service') with Method('liveness'): Description('liveness probe') Result(str) HTTP(GET, '/liveness') with Method('add'): Description('a + b') with Payload(): Field(1, "a", int, "left operand") Field(2, "b", int, "right operand") Required("a", "b") Result(int) HTTP(GET, '/add/{a}/{b}') GRPC() 30

Slide 31

Slide 31 text

おわり 31