前回に続き、gRPCとProxy配下の環境ネタ。
ちなみに、前回ハマってた内容はこちら。
mzryuka.hatenablog.jp
前提として、Proxy配下の環境。
localhost内でGo言語以外の言語で動かしたgRPCのクライアントが、gRPCのサーバ側にアクセスできなかった。
ホストOS上で直接実行した時と、Docker環境上で実行した時とでそれぞれ発生。
結論から言うとタイトル通りに「no_proxy」が原因。対応方法には差異があったので、それぞれについて説明する。
なお、解決については、以下のページの情報を参考にした。
https://stackoverflow.com/questions/45183076/grpc-c-client-14-connect-failed
https://github.com/grpc/grpc/issues/9989
https://github.com/grpc/grpc/issues/12815
ホストOS上で実行した時:
例えば、Rubyで書かれたgRPCのクライアントを実行。その際に出たエラーは以下の通り。
$ ruby greeter_client.rb bundler: failed to load command: greeter_client.rb (greeter_client.rb) GRPC::Unavailable: 14:Connect Failed /home/username/.rvm/gems/ruby-2.4.0/gems/grpc-1.18.0-x86_64-linux/src/ruby/lib/grpc/generic/active_call.rb:31:in `check_status' 〜(中略)〜 greeter_client.rb:31:in `main' greeter_client.rb:35:in `<top (required)>'
ここで出ている「14:Connect Failed」は、サーバ側にアクセスできないために発生したエラー。
他の言語(PythonとPHP)でも試してみたが、同じエラーコード(14)が発生していた。
この問題の原因は、「no_proxy」の値を設定していなかったと言うもの。
今回の場合、localhost内で通信するはずがProxy側に通信が流れてしまい、Proxy側からlocalhostに通信が戻ってこなかったために発生した。
そのため、localhost同士の通信にはProxyを経由しないようにno_proxyの値を設定してやれば良い。
コンソール上で、以下のようにlocalhostと127.0.0.1はproxyを利用しないように設定。
$ export no_proxy=localhost,127.0.0.1
ちなみに、自分の環境だとno_proxyに空の値を設定しても動作した。どう言う理屈なのかいまいちよくわからない。
$ export no_proxy=""
もしかして、環境変数にno_proxyがあるかだけを見ているのだろうか?
Docker環境で実行した時:
Docker環境上で、Go言語以外のクライアントを実行しようとした場合の話。
ホストOSの場合とは異なり、localhostに対してのno_proxy設定は、Docker実行時に設定されていた。にも関わらず、サーバに繋がらないエラーが発生。
以下、gRPCのクライアントにPHPを利用している場合の例。わかりやすく、クライアント側のコンテナに入って確認してみる。
まず、no_proxyの設定があるか。
root@a05b93c7cb28:/grpc-training/example/php/client# printenv | grep localhost no_proxy=127.0.0.1,localhost NO_PROXY=127.0.0.1,localhost
しかし、実行してみると、以下のようにエラーとなる。
root@a05b93c7cb28:/grpc-training/example/php/client# php client.php object(stdClass)#12 (3) { ["metadata"]=> array(0) { } ["code"]=> int(14) ["details"]=> string(14) "Connect Failed" } Fatal error: Uncaught Error: Call to a member function getMessage() on null in /grpc-training/example/php/client/client.php:21 Stack trace: #0 /grpc-training/example/php/client/client.php(24): request('World.') #1 {main} thrown in /grpc-training/example/php/client/client.php on line 21 root@a05b93c7cb28:/grpc-training/example/php/client#
出力内容の「metadata」云々の箇所は、わかりやすくエラーメッセージの詳細を出力してみたもの。ここでもエラーコード14の「Connect Failed」が出ているので、サーバに通信が届いていない状況になっている。しかし、最初にも書いたがDocker内では環境変数「no_proxy」は設定されていた。
調べてみた結果、gRPCはDocker内のno_proxyは正しく判断できていないらしい。
このため、ホストOSのProxy設定を引き継いで動作していると、通信がProxy側に向かってしまいlocalhostに戻って来ない。
ではどうするかというと、Docker内で設定しているProxyの設定である環境変数「http_proxy」「https_proxy」を除去する。環境変数の削除には「unset」を利用する。(※空文字の設定では環境変数は残るので効果がない)
なお、これは実行の都度必要になってくるので、gRPCのクライアントをラップしたスクリプトを用意し、ラップしたスクリプト経由でクライアントを実行してやれば良い。
以下、ラップするスクリプトのイメージ。
なお、自分の環境では大文字の環境変数「HTTP_PROXY」「HTTPS_PROXY」は残っていても影響がなかった。そのためスクリプト内では削除せず残したままになっている。
root@a05b93c7cb28:/grpc-training/example/php/client# cat wrap_client.sh #!/bin/bash echo "-------------------------------------------------" printenv | grep proxy echo "-------------------------------------------------" unset http_proxy unset https_proxy echo "-------------------------------------------------" printenv |grep proxy echo "-------------------------------------------------" php client.php
ラップしたスクリプトを実行して、gRPCのクライアントを動かす。
root@a05b93c7cb28:/grpc-training/example/php/client# bash wrap_client.sh ------------------------------------------------- HTTP_PROXY=http://proxy.example.jp:8080 https_proxy=http://proxy.example.jp:8080 http_proxy=http://proxy.example.jp:8080 no_proxy=127.0.0.1,localhost HTTPS_PROXY=http://proxy.example.jp:8080 ------------------------------------------------- ------------------------------------------------- HTTP_PROXY=http://proxy.example.jp:8080 no_proxy=127.0.0.1,localhost HTTPS_PROXY=http://proxy.example.jp:8080 ------------------------------------------------- object(stdClass)#12 (3) { ["metadata"]=> array(0) { } ["code"]=> int(0) ["details"]=> string(0) "" } Hello World.
環境変数のhttp_proxyとhttps_proxyが無くなったことにより、gRPCのサーバに通信が届いて処理結果が戻ってきたのが確認できた。