簡単に既存の監視プラグインにPROXY Protocol機能を追加できるツール mikoi をリリースしました

https://github.com/nabeken/mikoi

1つ前の記事でgo-check-smtpを作った話をしましたが、 もう1つ必要な機能がありました。 それがPROXY Protocol対応です。

PROXY Protocolについて

PROXY Protocolはロードバランサーなどのリバースプロキシ環境でオリジナルのピア情報をアップストリームへ伝送するためのL4(TCP)で動作するプロトコルです。 HTTPにおける X-Forwarded-For のL4版です。

http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt

もともとはHAProxyに実装されていた機能ですが、仕様が文書化されていることもあり ELBやPostfixにも実装されています。

例えば、Goでネットワークサーバを書いた場合、go-proxyprotoパッケージを使うことで簡単にPROXY Protocol対応ができます。

PROXY Protocolに対応したアプリケーションはすべてのR/Wの前にまずPROXY Protocolヘッダーを受信し、 オリジナルのピア情報を再構成してからアプリケーション固有の通信を開始する必要があります。

つまり、クライアントがPROXY Protocolヘッダーを送信しないかぎり アプリケーションの通信が開始されません。

この挙動は監視を考える時に問題となります。基本的な監視ではPROXY Protocolを実装した ロードバランサー(たとえばHAProxyやELB)経由でサービスポートを監視することになりますが、 この場合、全サーバに監視用のトラフィックが流れるかはプロキシサーバまかせになります (〜ベースロードバランシング的な)。

さすがにこれはちょっと雑すぎるので各サーバ上で監視エージェントを動かす(sensu/nagios-nrpe)ことになりますが、この時も監視プラグイン(SMTPならcheck_smtp)はPROXY Protocolをしゃべる必要があります。

解決案としては

  1. アプリケーションの設定を変更してPROXY Protocol対応ポートと通常ポートの2つのポートをlisten
  2. ローカルに監視用(あるいは流用)のhaproxyを追加し、それ経由で監視
  3. check_smtpなどのプラグインを改変してPROXY Protocol対応を追加

あたりが思いつきます。

1はアプリケーション固有ですが、実現可能であれば一番手軽な案です。 Postfixであればmaster.cfに追加することで実現可能です。

2は流用可能であれば現実的な解決案だと思われます。 流用できない場合はhaproxyのメンテが増えるので1よりかは複雑になりますが、開発は不要です。 HAProxyのPROXY Protocol対応は1.5からなので割と最近のディストリビューションでない限り、 ソースコードからビルドする必要があります。

3はPROXY Protocol対応したいプラグインすべてに改変が必要です。 nagios-pluginsの場合、ディストリビューション側の パッケージでサクっと入れられる利点を失ないます。

mikoiの提供する解決案

mikoiはephemeralなポート上で動作するPROXY Protocol対応プロキシサーバ機能と それを利用するための監視プラグインに対するラッパーとして動作します。

プロキシとして動作するため既存の監視プラグインへの改変は不要です。 また、監視のためだけのアプリケーションの設定変更やhaproxyの導入も不要です。

もちろんGoで書いたのでバイナリを配置するだけで動きます。

        +----------+
  +---> |  plugin  | forked by mikoi
  |     +----------+
  |       /|\   |
  |        |    |
  |        |   \|/
  |      +---------+            +----------+
  |      |         | <--------- |          |
  +----- |  mikoi  |            |  server  | (proxy protocol enabled)
         |         | ---------> |          |
         +---------+ w/ header  +----------+

例えば、nagios-pluginsに付属の check_smtp にPROXY Protocol対応を追加したい場合:

$ mikoi \
  -P \
  -H smtp.example.com \
  -p 25 \
  -- /usr/lib/nagios/plugins/check_smtp -H 127.0.0.1 -p {} -w 0.5 -c 1.0

{}の部分はmikoiがephemeralポート上に起動させたプロキシサーバのポート番号に置換されます。 プロキシサーバを起動したあと、もともとのプラグインを起動します。 監視トラフィックはmikoiの提供するプロキシ経由で対象サーバへ送られます。

Postfixで試してみる

PROXY Protocolを有効にしたPostfixをmikoi経由のcheck_smtpで監視してみます。 PostfixはDockerで用意します。

git clone git://github.com/nabeken/mikoi.git
cd mikoi/postfix-example
docker build -t local/mikoi-postfix-example .
docker run -it -p 10025:25 local/mikoi-postfix-example

Postfixのコンテナを起動させるとmaillogが流れてくるので別のシェルでプラグインを実行してみます。

/usr/lib/nagios/plugins/check_smtp -H 127.0.0.1 -p 10025
recv() failed

また、maillogにはwarningが出ているはずです。

Jun 14 08:07:32 c00a65e5cc25 postfix/smtpd[161]: warning: haproxy read: timeout error

SMTPでは接続するとサーバ側がgreetingを送ってくるのでクライアントはそれを待ちます。 一方、サーバ側ではPROXY Protocolヘッダーがクライアントから来るのを待つため、 お見合い状態になります。

mikoiを使ってみましょう。check_smtpに与えていた監視対象のアドレスとポートはmikoiへ渡します。 そのかわりに check_smtp には127.0.0.1と {} を与えます。 -PはPROXY Protocol機能を有効にするフラグです。

curl \
  -L https://github.com/nabeken/mikoi/releases/download/v20150613191746/mikoi_linux_amd64.tar.gz \
  | tar -C /usr/local/bin --strip-components=1 -zxvf - mikoi_linux_amd64/mikoi

mikoi \
  -P \
  -H 127.0.0.1 \
  -p 10025 \
  -- /usr/lib/nagios/plugins/check_smtp -H 127.0.0.1 -p {}
SMTP OK - 0.002 sec. response time|time=0.002467s;;;0.000000

うまくいきました。

はたしてどれだけの人がこれを必要としているのかは謎ですが、プログラムの構造としては ひとつのプログラムでサーバの実行とコマンドの実行をする必要があるのでGoっぽさが出たと思います。

機会があればmikoiちゃんをどうぞ。