概要
自分メモ用
rust で grpc サーバを実装する
実装するサーバは以下のような機能を持つ
- カスタム定義のサービス
- healthcheck の挙動確認が主のためこの部分の実装はかなり適当
- Healthcheck サービス
このような grpc サーバを実装し、k8s 上にデプロイして grpc の readinessProbe が成功する様子を確認する
以下のレポジトリでサンプルを保存
grpc-healthcheck (https://github.com/kronos1209/grpc-healthcheck)
前提
以下の環境で確認
それぞれのツールの導入については省略
- wsl (Ubuntu 22.04)
- docker
- minikube
- k8s クラスタ作成用
- kind などでもよいがダッシュボードの追加を容易にできるため minikube がおすすめ
- kubectl
実装
proto 定義
以下のようなサービスを定義してコンパイルする
syntax = "proto3";
package my_service;
message MyRequest {}
message MyResponse {}
service MyService {
rpc Get(MyRequest) returns(MyResponse);
}
proto
のコンパイル用の build.rs
use std::path::PathBuf;
fn main() {
// 指定したディレクトリを再帰的に検索し、proto ファイルを探す
let proto_dir = vec!["proto"];
let protos: Vec<_> = proto_dir.iter().fold(vec![], |mut acc, base| {
acc.append(&mut find_protos(base));
acc
});
tonic_build::configure()
.build_server(true)
.build_client(true)
.compile_protos(&protos, &vec![""])
.unwrap();
}
fn find_protos(base: &str) -> Vec<PathBuf> {
let mut protos = Vec::new();
for entry in walkdir::WalkDir::new(base).into_iter() {
let entry = entry.unwrap();
// Directory はスキップ
if entry.path().is_dir() {
continue;
}
// 拡張子が proto であるならば返り値に追加する
let path = entry.into_path();
if path.extension().unwrap() == "proto" {
protos.push(path)
}
}
protos
}
コンパイルした proto
をライブラリに追加
tonic::include_proto!("my_service");
grpc サーバ実装
前段でコンパイルしたコードを利用して grpc サーバを実装する。
healthcheck サービスは tonic-health
クレートが実装を提供してくれているため、そちらを利用する
#[tokio::main]
async fn main() {
// ヘルスチェックのステータスマップに MyService を追加する
// この時点では MyService は NotServing として登録する
let (mut reporter, health_server) = tonic_health::server::health_reporter();
reporter
.set_not_serving::<MyServiceServer<MyService>>()
.await;
let my_service = MyService {};
let my_server =
proto_definition::proto::my_service::my_service_server::MyServiceServer::new(my_service);
// 30秒後にサービスが利用可能になっているようにする
let mut clone_reporter = reporter.clone();
tokio::spawn(async move {
sleep(Duration::from_secs(30)).await;
clone_reporter
.set_serving::<MyServiceServer<MyService>>()
.await;
});
// サーバーを起動する
let addr = "0.0.0.0:8080".parse().unwrap();
tonic::transport::Server::builder()
.add_service(my_server)
.add_service(health_server)
.serve(addr)
.await
.unwrap();
}
k8s リソース
イメージ化
まずは実装した grpc サーバをイメージ化する
FROM rust as build
WORKDIR /app
# ビルドに必要な protoc をインストールする
RUN apt update \
&& apt upgrade -y \
&& apt install -y protobuf-compiler libprotobuf-dev
# ビルド
COPY . .
RUN cargo build --release --package grpc-server --bin server
#############################################################################
# scrach イメージしたかったが、うまく実行できなかったため ubuntu イメージを利用
# おそらくリンクとかの問題?
FROM ubuntu as runtime
WORKDIR /server
RUN apt update \
&& apt upgrade -y
COPY --from=build /app/target/release/server /server
ENTRYPOINT [ "/server/server" ]
イメージを利用した pod マニフェスト
pod マニフェストを作成する
readinessProbe
は spec.containers[].readinessProbe
で定義できる
ここで重要なのは readinessProbe.grpc.service
で grpc サーバで実装されているサービスのうち、特定のサービスが利用可能になっているかどうかを確認することができる
HealthService はハッシュマップで各サービスが準備完了状態であるかどうか管理しており、今回は MyService だけ30秒間立たないと準備完了状態にならないため、
service
を指定していない場合は一回目の readinessProbe で成功し、
service
をmy_service.MyServie
に指定すると一定期間 readinessProbe は失敗する
また、ここで指定するサービス名というのは tonic_build で proto をコンパイルした際に {サービス名}Server
という構造体が自動で生成されており、この構造体に実装されている NamedService トレイトの SERVICE_NAME
がそれにあたる
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: test:v1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 8080
livenessProbe:
grpc:
port: 8080
service: my_service.MyService
initialDelaySeconds: 60
# MyService grpc サービスが準備完了状態になっているかどうか healtchcheck を行う
readinessProbe:
grpc:
port: 8080
# NamedService のサービス名の指定する
service: my_service.MyService
initialDelaySeconds: 10
periodSeconds: 10
挙動確認
挙動を確認するためにリソースをデプロイしていく。
デプロイ
-
サンプルイメージの作成
docker build . -f ./grpc-server/dockerfile -t test:v1
-
k8s クラスタの用意
minkube dashboard
を実行したときに、ダッシュボードにアクセスするための url が表示されるためのそれを使ってダッシュボードにアクセスする# クラスタ作成 minikube start #クラスタ内にイメージを取り込む minikube image load test:v1 # ダッシュボードの起動 minikube dashboard
-
k8s リソース追加
kubectl apply -f ./mamifest/pod.yaml
確認
k8s のダッシュボードからポッドのページを開き、myapp
のポッドのステータスを確認する
正常に動作していれば、以下のような ReadinessProbe 失敗のログが3回発生したのち、ポッドのステータスが準備完了状態に移行しているはず
Readiness probe failed: service unhealthy (responded with "NOT_SERVING")
まとめ
rust で grpc-healtch-check を実装するには tonic-health
クレートを使えばよさそう。
ただし、crate.io の概要を見ると grpc-health-check の仕様をすべて実装しているわけではないようなので要調査
k8s のポッドの grpc の readinessProbe は k8s にビルドインで実装されている。
以前見たときは別途 grpc-health-probe をイメージに含めないといけなかったが k8a 内で完結できそうなので良かった
参考文献
tonic-health
Configure Liveness, Readiness and Startup Probes