Pythonでいい感じの「Hello World !」 ~grpc編~
0. 対象読者
1年前の自分に向けて書くつもりで書きます!
1. はじめに
はじめまして!なおずみと申します!
Zenn初投稿なので、まずはここからってことで、「Hello World」から始めます〜
最近grpc初めて触ったので使ってみる!次はfastAPIでやろうかな〜
2. 今回やること
今回のスコープは以下の通り
- grpc
- poetry
- Docker
- pytest
3. 前提
- 開発環境はMac
- Docker入ってること
- poetry入ってること
4. poetryで新しくプロジェクトの作成
以下コマンドで、プロジェクトを作る
poetry new grpc
5. 必要なものを追加していく
poetry add grpcio
poetry add grpcio-tools
poetry add pytest
6. protoファイルから、grpcコードの作成
protoファイルは、基本的に公式のやつを丸ぱくりして、protoファイルのversionだけ追加する。
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
次に、以下のコマンドをターミナルで実行して、grpcコードの作成。
poetry run python -m grpc_tools.protoc -I ./proto \
--python_out=./src/pb \
--pyi_out=./src/pb \
--grpc_python_out=./src/pb \
./proto/hello_world.proto
作成完了すると、ディレクトリはこんな感じ
.
├── README.md
└── grpc
├── README.md
├── poetry.lock
├── proto
│ └── hello_world.proto
├── pyproject.toml
├── src
│ ├── grpc
│ │ └── __init__.py
│ └── pb
│ ├── hello_world_pb2.py
│ ├── hello_world_pb2.pyi
│ └── hello_world_pb2_grpc.py
└── tests
└── __init__.py
pbフォルダに以下の__init__.pyを追加してpathを通す
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent))
7. サーバの立ち上げ
grpcサーバを立ち上げるファイルを作成して、
from concurrent import futures
import grpc
from pb import hello_world_pb2_grpc
from pb import hello_world_pb2
class Greeter(hello_world_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return hello_world_pb2.HelloReply(message=f"Hello, {request.name}!")
def SayHelloAgain(self, request, context):
return hello_world_pb2.HelloReply(message=f"Hello again, {request.name}!")
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
hello_world_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port("[::]:50051")
server.start()
print("Server started on port 50051")
server.wait_for_termination()
if __name__ == "__main__":
serve()
以下のコマンドでサーバーを立ち上げる
poetry run python ./src/grpc/server.py
結果確認→良さそ~
Server started on port 50051
8. クライアント側の立ち上げ
クライアント側のファイルを作成して、
import grpc
from pb import hello_world_pb2
from pb import hello_world_pb2_grpc
def run():
with grpc.insecure_channel("localhost:50051") as channel:
stub = hello_world_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(hello_world_pb2.HelloRequest(name="naozumi"))
print("Greeter client received: " + response.message)
response = stub.SayHelloAgain(hello_world_pb2.HelloRequest(name="naozumi"))
print("Greeter client received: " + response.message)
if __name__ == "__main__":
run()
以下のコマンドでサーバー叩いてみる
poetry run python ./src/grpc/client.py
結果確認→良さそ〜
Greeter client received: Hello, naozumi!
Greeter client received: Hello again, naozumi!
9. Dockerにまとめる
Dockerで動かせるように、Dockerfileを作成する
FROM public.ecr.aws/docker/library/python:3.13.0-slim-bookworm
WORKDIR /app
COPY pyproject.toml poetry.lock ./
ENV POETRY_REQUESTS_TIMEOUT=10800
RUN python -m pip install --upgrade pip && \
pip install poetry --no-cache-dir && \
poetry config virtualenvs.create false && \
poetry install --no-interaction --no-ansi --no-root && \
poetry cache clear --all pypi
COPY src /app/src
CMD ["poetry", "run", "python", "src/grpc/server.py"]
EXPOSE 50051
そのままだと、ModuleImportでこけて動かないので、server.pyでpathを通す
from concurrent import futures
import grpc
# 以下が追加したとこ
import sys
sys.path.append("./src")
from pb import hello_world_pb2_grpc
from pb import hello_world_pb2
以下のコマンドで動作確認
docker build -t grpc .
docker run -d -p 50051:50051 grpc
一応リクエスト投げてみる
poetry run python ./src/grpc/client.py
結果確認→良さそ〜
Greeter client received: Hello, naozumi!
Greeter client received: Hello again, naozumi!
基本的なのは、ここまで!
こっからは、テストとか、CICDとかやっていく!
10. testコードの作成
pytest-grpcを追加する。本番環境では使わないので、--devつけて。
poetry add --dev pytest-grpc
testコードを以下のように記述する
想定通りのレスポンスが返ってくるかどうかのユニットテストだけかく〜
import pytest
from src.pb import hello_world_pb2
from src.pb import hello_world_pb2_grpc
from src.grpc.server import Greeter
@pytest.fixture(scope="module")
def grpc_add_to_server():
return hello_world_pb2_grpc.add_GreeterServicer_to_server
@pytest.fixture(scope="module")
def grpc_servicer():
return Greeter()
@pytest.fixture(scope="module")
def grpc_stub(grpc_channel):
return hello_world_pb2_grpc.GreeterStub(grpc_channel)
def test_say_hello(grpc_stub):
response = grpc_stub.SayHello(hello_world_pb2.HelloRequest(name="test user"))
assert response.message == "Hello, test user!"
def test_say_hello_again(grpc_stub):
response = grpc_stub.SayHelloAgain(hello_world_pb2.HelloRequest(name="test user"))
assert response.message == "Hello again, test user!"
以下のコマンドでテストを走らせる
poetry run pytest
結果確認→良さそ〜
================================================================== test session starts ==================================================================
platform darwin -- Python 3.13.2, pytest-8.3.5, pluggy-1.5.0
rootdir: ~/grpc
configfile: pyproject.toml
plugins: grpc-0.8.0
collected 2 items
tests/test_server.py .. [100%]
=================================================================== 2 passed in 0.04s ===================================================================
まとめ
いかがだったでしょうか??
ソースコードも載せるので良かったら覗いてください!
今回はgrpcでのhello worldでしたが、fastapiとかも記事にしようかと思います〜
誰かの何かの参考になれば幸いです〜
Discussion