読者です 読者をやめる 読者になる 読者になる

Mercari Engineering Blog

We're the engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

Slackプロキシサーバ〜slackboard〜を利用したメルカリのSlack活用法

Go Slack

f:id:cubicdaiya:20150706183029p:plain

最近原稿の締め切りが追いかけてくる夢をよく見る@cubicdaiyaです。今回はその逃避の一環として定番のチャットツールであるSlackのメルカリでの活用法について紹介します。

メルカリでのSlack活用

多くのエンジニア組織がそうであるようにメルカリではSlackを単なる社内チャットに留まらず、所謂ChatOps的な用途にも活用しています。

たとえばメルカリではサーバの監視のためにZabbixを利用していますが、bot ack allとSlackで入力すると現在起きているすべての障害をAcknowledgedな状態にできます。

f:id:cubicdaiya:20150703233647p:plain

さらに、bot test masterとSlackで入力するとAPIサーバのテストが走ります。

f:id:cubicdaiya:20150703234155p:plain

さらにさらに、bot helloとSlackで入力すると自宅からでも出勤したことにできます。

f:id:cubicdaiya:20150703234211p:plain

なお、この時はうるう秒に備えて自宅で待機していたのでした。

f:id:cubicdaiya:20150703234233p:plain

ほかにもSlackからデプロイを行うための仕組みなどもありますが、長くなるのでそれはまた次の機会に紹介したいと思います。

Slackプロキシサーバの需要

上記のSlackボットからだけでなくメルカリではプロダクションに投入されているさまざまなサーバからSlackにメッセージがポストされます。以下はその一例です。

  • crontabに登録されたバッチの実行エラーの通知とそのエラー内容
  • Norikraが検知したイベントとその内容
  • Zabbixが検知したアラートとその内容

これらに関連した処理や設定は複数のサーバに散らばっているので各サーバから直接Slackにメッセージをポストするのは運用管理上の問題を抱えることになります。何故ならクライアントがSlackにメッセージをポストするにはAPIトークンかIncoming WebhooksのURLを知っていなければならないからです。

そのため、このどちらかを各サーバに配置するか、HTTPサーバ等を利用して各サーバから参照可能な場所に配置する必要があります。

Incoming WebhooksのURL情報を各サーバに配置する

f:id:cubicdaiya:20150703234950p:plain

Incoming WebhooksのURL情報を各サーバから参照可能な場所に配置する

f:id:cubicdaiya:20150703235046p:plain

Slackプロキシサーバの導入

昨年まではメルカリでも各サーバ毎にIncoming WebhooksのURLを埋め込んだ各通知プログラムが散らばっていました。そして、ある日諸事情により大元のIncoming WebhooksのURLを変更した数日後いくつかのサーバで変更漏れがあり、とあるログをストレージサービスにインポートするプログラムの実行エラーがSlackに通知されていないのが発覚するなどインシデントが発生しやすい状態でした。

また、Slackへメッセージをポストする各プログラムの実装言語がバラバラなことも運用管理の煩雑さに拍車をかけていました。

こういった事情もあり、現在はSlackのプロキシサーバを立ててそのプロキシと通信するクライアントコマンドを各サーバに配置するという構成に移行しています。

f:id:cubicdaiya:20150703235939p:plain

このプロキシサーバとその付属ツール群が次に紹介するslackboardです。

slackboard

github.com

slackboardはGoで書かれたSlackのプロキシサーバで以下の3つのプログラムで構成されています。

プログラム名 解説
slackboard Slackのプロキシサーバ
slackboard-cli slackboardにメッセージをポストするプログラム
slackboard-log プログラムの実行に失敗した時だけslackboardにメッセージをポストするプログラム

slackboardを起動するには設定ファイル(TOML)が必要です。最小設定では以下のようにslackboardがlistenするポート番号(port)とSlackのIncoming WebhooksのURL(slack_url)を記述します。

# slackboard.toml
[core]
port = "29800"
slack_url = "https://hooks.slack.com/services/..."

チャンネルにタグを付けたり通知アイコンを変更することもできるのですが、もっと詳しく知りたい方はslackboard/CONFIGURATION.mdを参照してください。

-cで設定ファイルを指定してslackboardを起動します。

$ slackboard -c slackboard.toml

slackboardはSlackのIncoming WebhooksのURLを知っているので、各サーバに配置されたslackboard-cliは 単にSlackにポストしたいメッセージをslackboardにポストするだけでよく、通知プログラム側でIncoming WebhooksのURLを埋め込む必要はありません。

実際にslackboard-clislackboardを経由してSlackにメッセージをポストするのは以下のようにパイプ経由でメッセージをslackboard-cliに渡します。

$ echo slack-proxy | slackboard-cli -s slackboard-host:29800 -c #tech-test -u cubicdaiya

上記のコマンドを実行するとslackboard(slackboard-host:29800)がslackboard-cliからのメッセージを受信してSlackの#tech-testチャンネルにメッセージをポストします。

slackboard-cli-sample.png

これで通知プログラム側でSlackのIncoming WebhooksのURLを知らなくてもSlackに簡単に通知できるようになりました。

slackboard-logでプログラムの実行失敗時にのみSlackに通知する

slackboardslackboard-cliを利用することでSlackに気軽にメッセージをポストできるようになりました。 しかし、時々プログラムの実行に失敗した場合にのみSlackに通知してほしいことがあります。

例えばgsutilでGoogle Cloud Storageにログをインポートする場合を考えてみましょう。 滅多に起こりませんが、外部サービスとネットワーク通信するので失敗する場合を考慮しなければなりません。 なので失敗した場合だけslackboard-cliでSlackに通知する処理をシェルスクリプトで書いてみます。

# copy log file to Google Cloud Storage
gsutil cp /data/${data_log}_20150505.log.gz gs://${bucket}/
 
result=`echo $?`
if [ $result -ne 0 ];
then
    echo "@channel: failed to copy log to Google Cloud Storage(${date_log})." | \
    slackboard-cli -s slackboard-host:29800 -t error-gcs
    exit
fi

このやり方は汎用的である一方少し煩雑です。そして私は記憶力が悪く未だにシェルスクリプトのif文を素で書くことができないので毎回ググる羽目になります。

次にcrontabに書かれたスクリプトの実行が失敗した際にSlackに通知したいとします。

0 6 * * * some-command some-args

some-commandをラップしてsome-commandが失敗した際にSlackに通知するロジックを埋め込ることができれば便利です。 似たようなことをするツールにcronlogがあります。

0 6 * * * cronlog -- some-command some-args

cronlogQ4Mh2oの開発者である@kazuhoさんがgithubで公開しているkaztoolsに含まれるコマンドユーティリティです。上記のようにcronlogをはさむとsome-commndが失敗した(リターンコードが非ゼロの)ときにだけ実行結果を標準出力や標準エラー出力に吐くことができます。

slackboard-logsome-commandが失敗した(リターンコードが非ゼロの)ときにだけslackboard経由でSlackにエラー内容を通知するプログラムです。

0 6 * * * slackboard-log -s slackboard-host:29800 -t error-some -- some-command some-args

例えば、存在しないファイルをlsしようとするとエラーになりますが、

$ ls notfound
ls: notfound: No such file or directory
$ echo $?
1
$

これにslackboard-logをはさんでみます。

$ slackboard-log -s slackboard:29800 -c #tech-test -u cubicdaiya -- ls notfound

するとこんな風にSlackに通知されます。

f:id:cubicdaiya:20150706104016p:plain

ところで、メルカリでは@kazeburoさんがPerlで書いたslacklogというslackboard-logと同じ用途のプログラムの方が主流だったりします。(slacklogの方がslackboard-logよりも先に実装されて投入されたので)

slackboard-clislackboard-logslacklogslackboardのHTTPベースのAPIを元に通信しているので新たにslackboardと通信するクライアントを作るのは難しくありません。興味のある方はslackboard/SPEC.mdをご覧下さい。

まとめ

メルカリでのSlackとSlackのプロキシサーバであるslackboardの活用の仕方について解説しました。

Slackへの通知を直接ではなくプロキシを介して行うのには以下のメリットがあります。

  • Slackへの通知設定をプロキシサーバで一元管理できる
  • 各サーバに散らばったSlackへの通知プログラムを一本化できる
  • Slackへのメッセージのポストをロギングできる

slackboardはこういった目的を達成するために開発しました。元々は昨年末の冬休み初日に設定変更漏れに気付いた直後ついカッとなって1時間で書いたものですが、使ってみたら思いのほか便利だったので年明けに本番投入して今に至ります。

それでは快適なSlackライフを。