クックパッドの開発基盤、インフラ環境での開発で心がけているラストワンマイル

f:id:mirakui:20151007180203j:plain

 初めましてインフラや基盤周りの技術が好きなエンジニアの渡辺です。 今回は私がサービス開発を行う上で心がけていることをお話させて頂きます。 (画像は私の好きな言葉で、ここの過去ブログで使われていた物を再掲させて頂いています)

前提

 クックパッドのサービスはクックパッドで整備、運用されている全社共通の開発基盤、インフラ環境上に構築されています。 別に強要されているわけではないのですが、そのレールに乗ることで様々な恩恵を受けることが出来ます。 サービス開発では価値を届ける、検証することにフォーカスしたいのでサービス毎に環境を 1 から構築していては手間が勿体無いです。 そして、セキュリティやバグ等の対応も全社的になるべく共通の環境にすることで環境依存で発生する問題のリスクを分散することが出来ます。

 近年は Microservices 化ということで、新しいサービスを立ち上げる環境整備が進んでいます。 Microservices 化の動向については以下のブログが参考になるかもしれませんので興味がある方は是非読んでみてください。

 大きな方針としてはサービス開発エンジニアの裁量を増やし、今まで依頼ベースで進めていたことを開発基盤、インフラメンバの力を借りずに作業を進められるようにしています。 ただ権限を渡して「はい、どうぞ」ではなく、使いやすくしたり、ミスが起こらないようにツールや環境を整備してくれています。 依頼ベースでの作業が少なくなれば、サービス開発のスピードも上がります。 開発基盤やインフラメンバがボトルネックにならないように、組織としてスケールする為にも個人的にはとてもいい事だと感じています。

 ただ、そこには課題もあると感じています。

課題

 モノリシックな Rails アプリケーションで開発していた時代は作業の分業化が進み、サービス開発エンジニアが開発基盤やインフラ環境をきちんと把握出来ているメンバが少なくなっていました。 これはサービス開発にフォーカス出来るようになったことで発生したジレンマであると思います。

 また、クックパッドはそれなりの規模のサービスということもあり、開発基盤やインフラ環境は色々複雑化、高度化しているのもキャッチアップが難しくなった理由の一つだと思います。 そしてその環境も日々進化、変化していて前に一度触ったことがあっても、後日また利用しようとした時に既に変わっていて、違う使い方を学ばないといけない場合もあります。

 細かくは今回は書きませんが、例としてはどこまでがどう Infrastructor as code で管理されていたかとか、秘匿値の管理が Vault に変わったり、Hako の Cluster の使い分けが増えたり・・・。*1

 もう一つは開発基盤はあくまで全社的な共通部分を受け持ってくれるだけで、個別のカスタマイズは自分たちでやる必要があります。 勿論相談すれば助けてくれますし、場合によっては開発をしてくれることもあります。 ただ、上記にも書いた通りスケールする組織になる為には「自分たちで」出来ることが重要だと考えています。

心がけていること

 クックパッドは非常に優秀なエンジニアが居るので日々開発されている開発基盤や整備されているインフラ環境は素晴らしいです。 ただ、どんなに素晴らしい物でも価値を提供、発揮出来なければ絵に描いた餅になってしまいます。 そこで重要なのがラストワンマイルだと個人的には考えています。

ラストワンマイルとは

 FTTH(Fiber To The Home) とかで使われた言葉で今回の話を表現するのにマッチした言葉だと思っています。 一つの部署(または対象のサービス)であれば知っている人が集中してフォローすればいいですが、課題でも書いた通り日々進化、変化していくので継続的な活動が必要になります。 そして部署が増える度にラストワンマイルを埋める為に必要なコストは増えていきます。 しかし、開発基盤やインフラ環境の価値を正しく余すこと無く開発者(サービス)に伝えるにはこのラストワンマイルを無視することは出来ません。 自分としては以前は開発基盤やインフラの仕事もしていたこともあり、今回の「ラストワンマイルを埋める活動でも貢献出来るのでは」と日々取り組んでいたりします。*2

開発基盤、インフラとして目指している方向性の理解

 ラストワンマイルを埋める活動として、単純に日々開発、整備されているツールや環境を把握するだけなく、会社としての技術の方針や技術部やインフラ部の目標、個人の目標を把握することが重要だと考えています。 それは大きな流れとしてどういう方向に進んでいるかを把握していないと、今後廃止されるツールや環境を選んでしまうことがあるからです。 他にも一つひとつの技術やツールだけを見ていると、どういう意図や目的でそれを採用されたのか、使い方としてどういうものが想定されているのかを理解できなくなるからです。

 そして我々は組織で働いています。 サービス開発エンジニアだけでなく、開発基盤、インフラメンバも成果を上げて行くことが勿論期待されています。 ある側面では直接的な価値を提供していない縁の下の力持ちの人たちが、どういう方向性で自分のキャリアパスを描こうとしているのか、またどういうことが成果として認められるのかを把握しておくことで、彼らのパフォーマンスをより引き出したり、どういう作業をお願いすると彼らの成果に繋がることが想像できるようになります。そうなることでお互い Win/Win になる方法を考えることが出来るようになると思います。

適切なフィードバックをする

 これは自分の経験からなのですが、開発基盤に関して言うと自分達が開発したツールや環境の直接的な利用者で無いことがあります。 そういう状況では、改善点や問題点を把握する為には、利用者からの積極的なフィードバックが非常に重要だと感じています。 *3 例えば、「せっかく作ったツールがサービス開発エンジニアに使われない」という状況の時に、それは使いづらいからなのか?そういうニーズが無いのか?ということを把握出来ないと、どうして良いかが想像の域を出ないからです。 エラーが出ていても「たまにだから良いか」とならず、きちんとフィードバックをすることで改善活動の指針になったり、放っておくと大問題になりかねない事を未然に防げたりもします。 このフィードバックも目指している方向性の理解している上でしないと、「そういう使い方は想定(または推奨)していない」「それは今後こちらの置き換わる予定」ということにもなりかねません。 こういうコミュニケーションが発生する事自体は問題ないのですが、お互いを理解しようとする意識は持っておく方が個人的には良いと思います。

 余談ですが、そういう仕組等を理解していると Hako で動いているアプリケーションなら pull request 毎に環境を作って動かせるのではと思い、提案して開発して貰いました。

部としての方針を理解した上で、必要な技術を活用する

 ここで言う「部」とは私が属している部のことです。 今期は目標として新しいサービスをスピード感を持って開発して行くことが多くなる部署だったりします。 出来る限りチームメンバにはサービス開発にフォーカスして欲しいと考えたので、整備してくれている最新の環境を利用して開発していくのが最適と判断しました。 そして徐々にサービス開発エンジニアに裁量が与えられる領域が増えていく部分に関しては、積極的に権限移譲を受けられるように活動しています。 依頼ベースでは作業者が不慣れな事による作業ミスのリスクは減らせますが、今期に必要としているスピード感が落ちると考えているからです。

 個人としてもなるべく早く、信頼して裁量を任せてもらえるように自らの作業範囲を決めること無く、働く様に心がけています。

使うだけでなく自分でも改善出来るようにコードの理解

 運用に乗せたりした後に、改善したいポイントやエラーの原因を把握したくなることがあります。 その時に必要になって調べていては、すぐに修正、対応が必要になった場合に対処が出来なくなってしまいます。 *4 なので個人的にはツールを使う場合に出来る限りコードを読むようにしています。 それはどういう仕組で動いているのかを把握する事と、その特徴を掴む意味でもあります。 また、用意されている拡張ポイント等も知ることが出来るので、自分たち用のカスタマイズする時にも役立ちます。

 インフラ環境についても Groupad という社内ツール(blog + wiki)に情報があったりするのでその辺を読んだり、実際の環境や Infrastructure as code で管理されているリポジトリを見たりしています。

実際やってきたこと

 サービスレベルや可用性を上げる為や、日々改善されている環境のメリットを享受する為に社内に古くから存在するアプリケーションを Hako アプリケーションとして動くように改修しました。 また、 Codenize.tools を使うにあたって、仕組みや実装を把握する為に私個人でも拙作ですが Applb というのを開発しました。 まだ作ったばかりで荒削りですが、AWS の ELB v2 を Codenize するツールです。 秘匿値の扱いについても権限移譲を受ける為に現在の部で開発しているアプリケーションをまとめて変更しました。

 自分たちである程度サービスを運用出来るようになるためには、ログの見方や監視方法の確立、割れ窓を放置しない、自分たちのアプリケーションが利用しているリソース状況の把握等の活動を自ら率先して行っています。

 また、自分がラストワンマイルを埋める時に得た知識や考えは、部内で行っている定期勉強会で発表して伝えるようにしています。チームビルディングに関しても色々思うところはあるのですが、また機会があれば書きたいと思います。

最後に

 例えるならサービス開発エンジニアは料理人で開発基盤は調理道具を作る人、インフラは水道やガス等を安定供給する人だと思います。 サービス開発エンジニアであれば、求められているまたは目指している料理(=価値)を届ける為に調理道具やインフラの特性を理解し、ケースバイケースで必要とされている最短、最善または最高な開発が出来るエンジニアになりたいものですね。

*1:個人的には色々刺激になって楽しいですが

*2:個人的な技術の興味の方向がそちら向きというのもあります

*3:技術部では色々なメトリクスを収集することで可視化する活動もしてくれていたりします

*4:何度も書いていますが、ありがたいことに開発基盤、インフラメンバは Slack で mention や電話等をかければ緊急時に対応してくれる体制は取ってくれています

Hackarade: MRI Internal Challenge

今年1月に入社した技術部の笹田です。Ruby インタプリタの開発をしています。

少し旧聞になりますが、今年3月の終わりに Hackarade: MRI Internal Challenge という、Ruby インタプリタ(MRI, Matz Ruby Interpreter)をハックするという社内ハッカソン企画を行いましたので、その様子をご紹介します。ハッカソンでは、弊社エンジニアが原則全員参加で Ruby インタプリタをいじりました。今回のハッカソンでは、特別ゲストとして 世界ナンバーワンの Ruby コミッタ(コミット数が世界一)である 中田さん(nobu) に参加していただき、様々な助言をいただきました。

Hackarade って?

f:id:koichi-sasada:20170510112942j:plain

クックパッド社内で、技術力の底上げを目的に、エンジニア全員が1日参加するハッカソンを年に3回程度行っていくことになりました。このハッカソンの名前を、Hack + Parade からとって、 Hackarade と名付けました。

MRI Internal Challenge

f:id:koichi-sasada:20170510112328j:plain

最初の Hackarade の企画は、MRI Internal Challenge と題して、Ruby の中身を触るハッカソンになりました。MRI とは、Matz Ruby Interpreter の略で、いわゆる ruby コマンドです。クックパッドでは Ruby を用いて開発しているので、自分たちが使っているものへの理解を深める狙いもあります。

この企画に決まった直接の理由は、MRIの開発を主務とする私が入社したためです。そして、クックパッドには、私以外にも青木さんや福森さん(sora_h)といった MRI のコミッタがいるので、彼らのサポートを得ることができるというのも理由の一つでした。

開催にあたり、次のような目標をたてました。

  • 参加者全員
    • MRI は普通の人間が開発している、ということを思い出すことができる
    • MRI のビルドを各自行うことができるようになる
    • MRI のチケットの読み方に関する文化を知る
    • MRI のチケットを起こすことができる
    • MRI のソースコードのディレクトリ・ソースコードの構造を知る
  • 希望者
    • MRI の改善チケットを実際に起票する
    • MRI の改善チケットを解決するパッチを作成し提案する
    • MRI の中身に興味をもち、後日 開催する MRI Internal workshop に参加する

最初の「MRI は普通の人間が開発している、ということを思い出すことができる」というのは、実際に MRI をいじることで、Rubyは所与のものであるわけではなく、自分たちで変更可能である、ということを思い出して欲しいという気持ちから最初の目標としました。 MRI はオープンソースソフトウェアですから、改善したければ好きなだけ改善できます。知らないとちょっとハードルが高い気もしますが、わかってしまえば意外と簡単です。このハッカソンでは、そのハードルを下げるお手伝いをしたいと思って臨みました。

そして、ハッカソンで開発した成果は、可能なら MRI 開発コミュニティに提案し、貢献することを目標としました。

一日の流れ

プログラムは次のような流れでした。なお、時間はあまりオンタイムには進みませんでした。

  • 10:00-10:10 オープニング
  • 10:10-10:40 (1) MRI 文化の紹介
  • 10:40-11:20 (2) Ruby のソースコード構造の紹介
  • 11:20-11:30 休憩
  • 11:30-12:30 (3) The Edge of MRI internal
  • 12:30-13:30 お昼休憩
  • 13:30-14:30 (4) ハックのアイデアの紹介
  • 14:30-19:00 (5) 各自チャレンジ
  • 19:00- パーティー & 成果の発表

(1)~(4) までが座学で、(5) が実際に自分の好きなテーマで開発するハッカソンでした。

最後は、打ち上げパーティーであり、弊社キッチンで美味しいご飯を食べながら、参加者それぞれの成果を称え合いました。

座学の紹介

f:id:koichi-sasada:20170510112331j:plain

座学の内容を掻い摘んでご紹介します。座学といいますが、学校の教室のように説明を聞いてもらうよりは、資料を片手に手元のコンピューターをいじりながら、自分のペースで進めてもらうというものでした。弊社には日本語ネイティブでないエンジニアも多数在籍しているため、資料は英語で作成しました(その場での説明は日本語でした)。

時間も短いので、ハッカソンに必要になるビルドの方法や、他の資料の紹介が主になりました。

(1) MRI 文化の紹介

ここでは、開発者がどのような手順で開発をしているのか、意思決定はどのように行われているか、Ruby で用いている Redmine のチケットはどのように書くとよいか、それから、MRIの情報はどのように取得すればよいか、などを紹介しました。

チケットの書き方は、Ruby バグレポートガイドライン の内容を紹介しました。また、開発方法も、開発者の手引き を主に紹介しました。

情報の取得については、青木さんの『Rubyソースコード完全解説』サポートページRubyのしくみ Ruby Under a Microscope の紹介を行いました。その他、まつもとゆきひろさんの Twitter アカウント の紹介や、Ruby 関連イベントやワークショップなどオフラインコミュニケーションを紹介しました。また、社内に 3 人も MRI コミッタがいるので、気兼ねなく聞いてください、という話もしました(例えば、弊社 Slack には #ruby チャンネルがあります)。

細かいルール的な話が多くなりがちでしたが、ここで強調したのは、「ハッキングが全てであり、良い成果を出せば多少の形式は不問とされる」ということでした。たいてい、説得力のある成果があればなんとかなります(回りがサポートします)。

(2) Ruby のソースコード構造の紹介

実際にソースコードをいじるために必要になる、Ruby のソースツリーの大まかな概要をお伝えました。例えば、ここはインタプリタのコア(VMやGCのなど)を記述してあり、ここは ArrayString といった組み込みクラスの定義である、ここはテストが収まっている、などです。MRI は、自身のソースコードを生成しながらビルドを進めていくので、そのあたりの概略もお伝えしました。

ソースコードを説明するにあたり、ソースコードをチェックアウトしてもらって、実際に make コマンドを用いてビルドを行ってもらいました。また、C 言語で新しいメソッドを定義するといった、実際に MRI の中身を改造するような演習を行いました。

(3) The Edge of MRI internal

この時間は、私が行ってきた MRI のハックを紹介し、何が楽しくてこのようなことをやっているのか、ということを紹介しました。また、MRI にどのような課題があるか、という紹介をしました。

自分の行った改善が、世界中の利用者の方に影響をあたえるというのはやり甲斐を感じますよね。

(4) ハックのアイデアの紹介

何もない状態で MRI をハックしてください、というのは大変だと思うので、いつかやりたい、と思っていた(比較的軽い)アイデアを提案したり、チケットを紹介しました。

私はあまり残りチケットをまとめていなかったのですが(資料として、未アサインのチケット = 放置チケットへのリンクを提示するのみだった)、青木さんがその場でやりやすそうなチケットをピックアップしてくれました。

ハッカソンの紹介

f:id:koichi-sasada:20170510112744j:plain

ハッカソンは、最終的に40個ほどのアイデアに挑戦し、10個ほどのパッチの投稿という貢献を行いました。いくつかのハックはまだ継続して行われています。

ハックはグループで行っても良いですし、単独で行ってもよい、ということにしました。結果としては、ほとんどの人が単独で挑戦していました。

ここでは、実際にどのようなハックが行われたかをいくつかご紹介します。ここにある以外にも、挙動やテストの修正や、ドキュメントの追加などが行われました。

More flexible GC parameters

環境変数で GC の挙動を変更するためのパラメータを与えられるのですが、実行時に変更することはできません。しかし、原理的には変更可能であるため、変更できるようにする、という話になります。この話の難しいところは、「本当に変更しても大丈夫なの?」というところで、それを確認するのが面倒で手をつけていなかったのですが、今回のハッカソンで提案してもらったところ、興味を持った方に挑戦してもうことができました。

実装して、チケットとして登録するまでやり遂げることができました。 https://bugs.ruby-lang.org/issues/13388

弊社アプリケーションのような、実際のアプリケーションに応用して成果が出るかどうか、確認して取り込みたいと思っています(取り組んでくれた方々に依頼中です)。

再代入不可なローカル変数をつくってみる

Rubyのローカル変数はいつでも代入可能ですが、新しい文法を導入し、代入不可能なローカル変数(?)を作ろうというものです。 最近流行りのテーマですね。foo := expr とすると、foo への再代入を禁止する、という意欲的な挑戦でした。

とりあえず、foo := expr を受理するパーサを作成し、限定的な場面で再代入を禁止する機能を作成することはできたようですが、すべてのローカル変数への代入を網羅するところまでは至らなかったので、パッチはお蔵入りにしたとのこと。取り組んだ方々は、「パーサが理解できてよかった」という感想をもったそうです。

sprintfの精度のバグを直してみる

[Bug #8916 rb_sprintf への精度指定が正しく機能していない] という報告に対して、数人の方々が取り組みました。C のソースコードをかき分け、問題を追っていって、ある程度目星がついたところで、最終的には nobu がさっと直していった、という結末になりました。

感想を伺ったところ、「仕様の理解に時間がかかりすぎてつらい気持ちになったのに、しゅっと直っていて知識不足を痛感しました」とのことでした。半日しかない中ではしょうがないところだと思います。

Net::FTP で明示的に PORT コマンドを送れるようにする

Net::FTP が明示的に PORT コマンドを送れない関係で、NAT を挟んで active mode で通信できないケースがある、という問題に対するパッチの投稿を行いました https://bugs.ruby-lang.org/issues/13382。社内では渋いテーマだと評判でした。

チケットでは、「そもそも要るのか?」というコメントから議論がはじまりましたが、必要性を示すことで理解を得て、現在は最終的な実装を行っているところです。

Process.uid= validation and casting

[Feature #12410] Process.uid= validation and casting で報告されたバグ報告に対する修正を試みました。

バリデーションを入れたパッチを提案しましたが、チケット上での議論で現在の挙動が適当である、という結論が出たため、リジェクトされることになりました。

Module#source_location

Module#source_location という、モジュールが定義された場所を返すメソッドを実装し、提案しましたhttps://bugs.ruby-lang.org/issues/13383。すでに、Method#source_location というメソッドがありますが、その Module 版ということになります。

チケットでは、モジュール定義の追加が行われたとき(つまり、リオープンされたとき)、どうするか、といった問題や、実装に関する意見が出ています。便利な機能だと思うので、ぜひ Ruby 2.5 で導入されてほしいですね。

おわりに

f:id:koichi-sasada:20170510112632j:plain

このような形で、Hackarade: MRI Internal Challenge は無事に終了することができました。MRI のコア部分は C 言語で記述されており、最初はどうなることかと思っていたのですが、予想以上に皆さん楽しそうにハックを進めてくれており、一安心でした。半日のハックなので、最初は大きな成果が出なくて当然なのですが、これを機に興味を持ってくれる人が増えると嬉しいと思っています。

最後に、開催にあたっての反省点です。ハッカソン形式なので、課題をどのように準備するかがキモだと思います。今回は「未着手のチケットの中から自由に課題を選んでもらおう」と思っていたのですが、膨大なチケットの中から着手可能な難易度の課題を見つけることができずに、ハックの時間を十分にとれない参加者がそこそこ居ました。初めて見るチケット群では、目星もつきづらいですよね。事前にもう少し精査してチャレンジしやすいものを用意しておけばよかったと思います。

次回の Hackarade は、エンジニアみんなで機械学習を使おう、という企画をしているとのことで、今から楽しみです。

というわけで、本記事ではクックパッド社内で行われた、エンジニア全員参加のハッカソンイベント Hackarade の、1回目、MRI Internal Challenge をご紹介しました。

Sisimaiを使ったバウンスメールの管理

最近、Ninja650に乗り換えたSREチームの菅原です。今までマルチばかり乗ってきたんですが、ツインもなかなか面白いですね。シフトペダルをガチャコンいわせながら方々に出かける毎日です。

この記事では、サービスが配信しようとして何らかの理由で差し戻されたメール—バウンスメールの管理をどのように行っているかという話しを書きます。

バウンスメール

サービスがユーザに向けてメールを配信しようとすると、多かれ少なかれバウンスメールは発生します。メールアドレスが間違っている・携帯電話の設定で受信を拒否している・メールボックスが一杯にになっている・IPアドレスがブラックリストに載ってしまったためサーバにメールの受信を拒否されている…etc。完全になくすことは難しいですが、バウンスメールを放置するとメールの到達率を下げたり、送信先から一時的にメールの受信を拒否されたりすることがあるため、差し戻されてしまった宛先はリストに登録して、再送を抑止することが望ましいです。

SendGridのようなサービスを利用している場合、差し戻されたメールは自動的にリストに登録されて再度メールを送っても配送されないようになっていたりするのですが、クックパッドの場合は内部システムのPostfixサーバからメールを配信していたため、バウンスメールの管理をある程度、自前で作り込む必要がありました。

既存のシステム

以前はbounceHammerというOSSとMySQLのバウンスメール管理用のテーブルを使って、バウンスメールの管理が行われていました。

f:id:winebarrel:20170502181740p:plain

  1. アプリケーションはメール送信サーバのPostfixを経由して、メールを配信します
  2. 差し戻されたメールは、バウンスメール管理サーバのPostfixが受け取ります
  3. Postfixが受け取ったメールをbounceHammerが解析して自身のデータベースに入れます
  4. 定期実行されるスクリプトがbounceHammerのデータベースからアプリ用のデータベースにバウンスメール情報をマイグレーションします
  5. アプリケーションはユーザテーブルとバウンスリストを結合して、メールが差し戻された宛先にはメールを送らないようにします
  6. バウンスリストの情報は、管理用アプリケーションから削除することができます

既存システムの問題点

このバウンスメール管理システムには、いくつか問題点がありました。

  • bounceHammerの導入が後付けであったため、バウンスメール情報がbounceHammerとアプリ用データベースで二重管理になっていた
  • 同様に後付けが原因で、管理アプリケーションからアプリ用データベースの情報は削除できるが、bounceHammerの情報を削除(ホワイトリスト登録)できないため、手作業で同期を取る必要があった
  • ユーザテーブルとバウンスリストをSQLで結合して配信対象をフィルタリングする方式であったため、スケーラビリティに問題があった
  • バウンスリストがテーブルという単位で管理されているため、アプリケーションの各機能が個別に配信対象のフィルタリングを実装する必要があった
  • SQLの結合という形でフィルタリングを行うと、メールが配信されなかったユーザがそもそも配信の対象にならなかったのか、バウンスリストに登録されていたため配信されなかったのか、区別を行うことが難しかった
  • バウンスメールの情報がきちんと可視化されていなかったため、配信状況の把握に難があった
  • bounceHammerがEOLになった

問題点を抱えた状態での運用がつらく、またbounceHammerがEOLになったこと、さらにシステムのリニューアル作業が進行中だったこともあり、バウンスメール管理システムを新しく作り直すことにしました。そして、そのコアの部分として利用することになったのがSisimaiです。

Sisimai

SisimaiはbounceHammerの後継として開発されているバウンスメール解析ライブラリです。bounceHammerが管理用Webアプリや集計用のスクリプトも含めた複合的なシステムであったのに対して、シンプルなPerl・Rubyのライブラリです。ライブラリの依存関係も少なく、またわかりやすいAPIで、しかも自分が慣れたRubyのライブラリであったため、とても簡単に新しいシステムに組み込むことができました。

以下はSisimaiを使ってバウンスメールの解析を行うサンプルコードです(※公式ドキュメントより引用)

#! /usr/bin/env ruby
require 'sisimai'
v = Sisimai.make('/path/to/mbox') # or Path to Maildir

if v.is_a? Array
  v.each do |e|
    puts e.addresser.address    # shironeko@example.org # From
    puts e.recipient.address    # kijitora@example.jp   # To
    puts e.recipient.host       # example.jp
    puts e.deliverystatus       # 5.1.1
    puts e.replycode            # 550
    puts e.reason               # userunknown
  end
else
  # There is no bounce message in the mailbox
  # or Sisimai could not parse
end

Sisito

Sisimaiはすばらしいライブラリなのですが、bounceHammerにあったような管理用のWebアプリケーションはなくなってしまいました。エンジニア以外のスタッフが「問い合わせのあったメールアドレスをホワイトリストに登録する」等の作業が発生するため、管理用のWebアプリケーションは必須です。そこで以前の運用経験を踏まえ、バウンスメール情報保存用のデータベースとそれを管理するウェブアプリケーションを作成しました。それがSisitoです。

以下はSisitoの管理画面のスクリーンショットです。

f:id:winebarrel:20170508161014p:plain f:id:winebarrel:20170508161019p:plain

また、バウンスメール管理システム以外からバウンスメールの情報を取得するため、sisito-apiというAPIサーバも作成しました。

新バウンスメール管理システム

SisimaiとSisitoを使った新しいバウンスメール管理システムの構成が以下のようになります。

f:id:winebarrel:20170508153346p:plain

  1. アプリケーションはメール配信サーバのPostfixを経由してメールを配信します。このときPostfixの機能でblacklistに登録されているメールアドレスには配信しないようにします
  2. 差し戻されたメールはメール配信サーバに保存されます
  3. 定期実行されるSisimaiスクリプトがSisitoデータベースにバウンスメール情報をを保存します
  4. 定期実行されるblacklist更新スクリプトが一定の条件に従って(ハードバウンスのみ ・特定の理由のみ、など)blacklistを更新します
  5. Sisitoを使って統計情報の閲覧やホワイトリストへの登録を行います
  6. アプリケーションはsisito-apiを経由してバウンスメールの情報を取得します
  7. メールの送信ステータス・Subject・blacklistによるリジェクト状況などのログはElasticsearchに送信してKibanaで閲覧できるようにします

まとめ

新バウンスメール管理システムの導入により、アプリケーションの各機能で配信制御を行う必要がなくなり、Postfixでフィルタリングを行うことでスケーラビリティの問題も解決することができました。また、Sisitoによる可視化により問題が発生しても(たとえば、特定の理由のバウンスメールが増えているなど)状況をすぐに把握して迅速に対応することができるようになりました。さらにホワイトリストの登録処理がエンジニアを介さずにできるようになったため、業務フローのコストも下げることができました。

差し戻されるメールのエラー内容はサービスによって様々なパターンがあり、人間が解析することはかなりの労力を伴います。SisimaiのようなライブラリがOSSとして公開されていることは大変ありがたいことです。積極的に活用して、フィードバックなどで開発に貢献していきたいと考えています。

バウンスメールの解析で苦労しているかたは、一度Sisimaiを試してみてはどうでしょうか?