この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ども、大瀧です。
前回の記事(gRPCアプリをAWS ELBで負荷分散してみた)では、ELBのL4転送による負荷分散をご紹介しました。
この構成では手軽に負荷分散ができる一方で、ELBを介すためにサーバ側でクライアントのリモートIPを取得することができません。そこで今回は、ELBが
サポートするPROXY ProtocolによってリクエストにリモートIPを付与し、それをgRPCサーバーで取得するサンプルコードをご紹介します。
PROXY Protocolの有効化
ELBのClassic Load BalancerはHAProxyのPROXY Protocol version 1をサポートしますが、既定では無効になっているため有効化します。aws elb create-load-balancer-policy
コマンドでPROXY Protocolサポートが有効なポリシーを作成、aws elb set-load-balancer-policies-for-backend-server
コマンドでポリシーをELBのリスナに設定します。
$ aws elb create-load-balancer-policy \
--load-balancer-name <ELB名> \
--policy-name ProxyProtocol-policy \
--policy-type-name ProxyProtocolPolicyType \
--policy-attributes AttributeName=ProxyProtocol,AttributeValue=true
$ aws elb set-load-balancer-policies-for-backend-server \
--load-balancer-name <ELB名> \
--instance-port <リスナのポート番号> \
--policy-names ProxyProtocol-policy
$
これでOKです。PROXY Protocolサポートが有効なリスナからはリモートIPが先頭に付与されたトラフィックが転送されるため、PROXY Protocolをサポートしないターゲット(gRPCサーバー)ではリクエストを正常に処理できないことに注意しましょう。
gRPCサーバーのPROXY Protocol対応
今回は、Go版gRPCサーバーで対応させるため、Go標準のnetパッケージのラッパーとして動作するgo-proxyprotoを利用しました。あらかじめgo get
でライブラリを取得しておきます。
$ go get github.com/armon/go-proxyproto
gRPCサーバーのコードは、おなじみのサンプルhelloworldのgreeter_server/main.go
です。
main関数ではTCPのリスナをproxyproto.Listenerメソッドでラップすることで、トラフィックからのリモートアドレスの除去とgRPCピアのリモートアドレスの置換が行われます。
greeter_server/main.go
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
proxyLis := &proxyproto.Listener{Listener: lis}
s.Serve(proxyLis)
//s.Serve(lis)
}
リクエストに対応するSayHello関数では、置換されたピアのリモートアドレスを取り出してコンソールに表示します。
greeter_server/main.go
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
var addr string
if pr, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := pr.Addr.(*net.TCPAddr); ok {
addr = tcpAddr.IP.String()
} else {
addr = pr.Addr.String()
}
}
fmt.Printf("Remote Addr: %s\n", addr)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
実行し、クライアントから接続すると、以下のようにELBのIPアドレスの代わりにクライアントのリモートアドレスが表示されます。
Remote Addr: 54.250.XXX.XXX
Remote Addr: 54.250.XXX.XXX
ちゃんと動いていますね!
まとめ
gRPCサーバーでAWS ELBのPROXY Protocolに対応する例をご紹介しました。↓の参考URLにあるメルカリさんのスライドに書いてあることそのままなのですが、コードに書こうとして躓いてしまったので誰かの参考にあれば幸いです。