sltd, AWS Athenaを使ってLDAPサーバの監査体制を強化した話

こんにちは、VOYAGE GROUP システム本部の @s-tajima です。
今回はVOYAGE GROUPが提供する多くのサービスをより安全に運用するために、LDAPサーバの監査体制を強化したお話です。

VOYAGE GROUPでは、 サーバにログインするためのシェルアカウントや、各種管理画面のアカウント等、様々なアカウントの統合管理のためのシステムとしてLDAPサーバ(OpenLDAPのslapd)を利用しています。つまり、このLDAPサーバに対して誤った操作や悪意のある操作が行われることで、それぞれのシステムに意図しない認証や認可が行われてしまうことになります。

そんな状況を予防, 検知するために、

  • LDAPサーバに対する参照・更新の記録をすべてログに残す。(自動)
  • ログの中身を確認し、必要なものがあれば通知する。(自動)
  • 通知の内容を元に詳しい調査を行い、問題がないか確認する。(人力)

という環境を用意しました。 こんな環境を実現するために、どんなシステムを構築したかというのを紹介します。

f:id:s_tajima:20170824214527p:plain

Step1: LDAPサーバ(slapd)による監査ログの出力

slapdで扱えるログは大まかに分けて3種類あります。
それぞれのログの良い点/悪い点は以下の通りです。

  1. Log (通常rsyslog経由で出力される)
    • [+] 1行毎に意味を持つログファイルベースで出力可能
    • [-] LDAPのデータベースに対する参照・更新の内容が完全に出力されるわけではない
  2. AccessLog (Overlay)
    • [+] LDAPのデータベースに対する参照・更新の内容が完全に出力される
    • [-] この情報自体がLDAPのデータベースに記録されるので取り扱いが面倒
  3. AuditLog (Overlay)
    • [+] 指定したファイルに出力可能
    • [-] ただしフォーマットがldifなので複数行をまとめて扱ってはじめて意味のある情報になる
    • [-] 出力されるのは更新の情報のみで、参照については出力されない

今回の要件だと、参照・更新の情報を完全に出力しておきたかっため、AccessLog を使うのが妥当そうだと判断しました。

Step2: sltdによる監査ログのS3への転送

VOYAGE GROUPで管理しているLDAPサーバは1台だけではないので、ログを出力したそのサーバ上で中身のチェックをするのではなく、どこか別の場所に転送し一箇所にまとめておくとその後の調査がしやすいです。しかし、ここで課題になるのがログの収集方法です。前述の通り、slapdのAccessLogはそれ自体がLDAPのデータベースに書き込まれていきます。

最初に思いついたのは、AccessLog用のLDAPデータベースをレプリケーション等で集約するためのLDAPサーバを用意することですが、実際にはすべてのLDAPサーバと直接通信することのできるサーバを用意するのが難しかったために断念しました。

そこで今回は、後述するAmazon Athenaによるクエリを利用することを目論んですべてのLDAPサーバのログをAmazon S3に転送することにしました。

これを実現するための方法はいくつか考えられますが今回は、

  • AccessLog用のLDAPデータベースを LDIF backend にする。
  • 出力されたLDIFファイルを読み込みJSONに変換(Athenaによるクエリのため)し、S3に転送する機能を持ったソフトウェアを作成する。

という方法をとりました。 このソフトウェアは、sltdという名前でVOYAGE GROUPのGitHubリポジトリにて公開しているので興味があればご覧ください。

Step3: AWS Athenaによる操作内容の通知

sltdによってS3に転送したログをAthenaにて集計し、特定の条件に合致するものがあればSlackに通知する仕組みを作りました。セキュリティの都合上、通知の条件については割愛しますが、以下のように通知されます。

f:id:s_tajima:20170824214444p:plain

最後に

最近では、Zero Trust Networks にかかれているような、LDAPサーバのような中央集権型のアカウント管理システムを使わずにすむように、オンデマンドでテンポラリなアカウントを発行するシステムの設計が便利そうではありますが、長い歴史を持つサービスを数多く運用しているとなかなかそのような環境に切り替えていくのは難しかったりします。

今回の監査体制の強化によって、今までよりも安心してLDAPサーバを運用できるようになりました。また、今まで限られたメンバーでのみ行っていたLDAPのデータの更新を、もう少し広い範囲に広げていくこともやりやすくなりました。

監査体制の構築はサービスを運用する上でとても大切な話だと思いますが、セキュリティ上の問題などもあるためか実運用に関する話をインターネットで見かけることがあまりないような気がしています。このような話に興味がある方は、是非 #ajiting でお話しましょう!

SIer時代に学んで今でも役に立っていること、Web系の会社に入ってから考えを改めたこと

こんにちは、株式会社fluctで fluctSSPの広告配信に関わっている坂本です。

私はfluctでは新しい広告(PMP, NativeAd)の追加をしたこともありましたが、ここ1年半くらいは自分で追加した広告機能や既存機能のサポートエンジニアとして働いています。
(インフラの保守・運用は専門部署があるのでおまかせしています)

経歴

  • SIer(金融・ECサイト開発等)のピラミッド構造で開発に従事していた6年
  • Web系の会社(VOYAGE GROUPとは別会社)にてSESとして仕事をしていた4年(ここ終わりでVOYAGE GROUPに転職)
  • Web系の会社(VOYAGE GROUP/fluct)にて正社員として2年半~現在に至る

今の自分の行動指針はSIer時代に培われたものがほとんどですが、Web系の会社に入って考えを改めたものもあるので、自分のスキルの棚卸しを兼ねてそれを整理してみます。(サポートエンジニアに限った話ではなく、いちエンジニアとして心がけているものです)

現SIerの方が転職する際のお役に立つかもしれませんので、自分が転職した時のことも少し書いてみます。

おそらく殆どの人にとっては当たり前すぎることしか書いてないと思います。
技術的な話を期待されている方、ゴメンナサイ。

目次

Web系会社に入ってからSIer時代の経験が役に立っていると思うこと

1. 自分が関わっているサービスを使っている人と綿密なコミュニケーションをとること

 幾つか理由があるので箇条書きにします。

  • 理由1. 使う人(社内・社外問わず)がどう使っているのか?を把握するため。
    ヒアリングをして分かる話だけではなく、問い合わせや小さな障害対応などを通じて業務理解・ユースケースをどれだけ深めるか。という二つの軸で情報を集めることが大事だと考え行動しています。どちらかだけでは機能改修の時に、微妙な結果(※)になったことが過去にあったためです。
    • 微妙な事例1 : 社内向け画面の改修時にユーザ部門の意見を取りまとめてしまったがゆえにニュアンスがぼやけてしまいユーザ部門の実業務と乖離してしまった
    • 微妙な事例2 : 保守対応時にユーザの意見をそのまま聞いてしまい、ツギハギだらけの機能になってメンテナンスしづらくなった
    また、こまめにコミュニケーションをとっていると分からない言葉が出てきた時に都度確認できるので、DDD界隈でよく話題に上がる「ユビキタス言語の整理(辞書化)」を自然に進めていくことになると考えています。
    Backlogなどを使いノウハウや問い合わせ内容を貯めていければ同じ問い合わせは減らすことも出来ます。
  • 理由2. 相談しやすい関係性を構築するため
    誰もが思っていると思いますがエンジニアと非エンジニアの間には壁ができやすいものです。自分が過去に関わって壁を放置したプロジェクトはうまくいっているものは無かったです。
    「管理画面の余り使わない画面の使い方がわからない」みたいな比較的影響度が小さいことがわかっている問い合わせだったとしても、積極的に関わりに行くことで「この人には相談してもいいんだ。」という気持ちになってもらい、アラート検知システムに引っかからない何かがあった時に迅速に情報をあげてもらえるような関係性を保つことが重要。(検知システムが無くていいとかザルでいいという意味ではありません。)
    では、その時にどう振る舞えばいいのか?というのは以下にまとまっています。特に5. 常に機嫌よくいる。(≒聞きやすい雰囲気を作る)が一番大事だと思っています。が、忙しいとすぐに崩れてしまうので注意が必要(自戒を込めて...)。

    techlog.voyagegroup.com

  • 理由3.普段の小さなお問い合わせに対応することで、障害対応のスキル/ドメイン知識を少しずつ身につける
    • 影響範囲の特定
    • 調査力
    • 一次対応(ビジネス上システムが適切にに動くためのパッチ)の立案
    • 根本対応の立案と補償等の影響
    • サービスとしての危険度の判断力(ドメイン知識)

2. 全体像を図示して関係者間の認識齟齬を少なくすること

SIer時代、新しい部署や会社に入った時に担当するシステムの概要図すらなくて概要を把握するのが大変だったという経験があったことから、自分で色々書くようにしています。
私は文章ではなく図示することの方が好きです。理由は図のほうが情報量が多く誤解を招かないためです。
自分で書いてみて、以下の価値があるのではないかと考えています。
(存在する事自体の価値と、自分が書く過程において生まれる価値の二種類)

  • 機能追加のときに関係者全員で同意をとるのに理解してもらいやすくなります
  • 他のエンジニアにコードレビューしてもらう時に概要把握してもらいやすくなります
  • プロジェクトに参入してきた誰かに説明するときに使うことで人に説明するコストが下がります。(自分が思い出すコストも下がります)
  • 書くことで、俯瞰的な視点でサービスを捉えやすくなり、障害対応時の影響範囲の確定や、計画・見積もりが正確になると考えています

注意点

細かく書きすぎないこと。
細かすぎるとコードとドキュメントの乖離問題につながるので、私は以下で十分だと思っています。(これらでもメンテナンスしないとすぐに乖離していくので常に更新するようにしています)

  • 業務フロー図(プロセス図)
  • ユースケース図
  • ER図 <= エンジニア向け

fluctでは新卒・中途に関わらず新しくエンジニアが入ってきた時、「エンジニアクルーラリー」という
`機能毎の説明を一番詳しい人がリレー形式で行い、システム全体の理解を深めてもらう` という取り組み( 全体概要 -> 管理画面 -> 配信 -> バッチ -> インフラ ... と説明)をしていて、自分が説明する際にこの取組で書いた図を使うようにしています。 

3. 自分が作ったものを見届ける(サービスクローズに立ち会う)こと。

SIer時代に追加開発するチームと保守するチームが違っていることが多く、追加開発するチームの品質が一向に上がらないことがありました。
保守チームにいた自分がその当時の先輩に「どうして追加開発チームの品質が良くならないのか?」と質問したときの答えが「自分で作ったものを見届けないエンジニアは成長しづらい。」と言うものでした。
新規開発だけしている人は運用時のPDCAに参加するのが難しいので、自分の作るものの品質を改善する機会を失っていることになります。
私は保守運用側の立場なので、開発だけをやって離れてしまったエンジニアにこんな改修があったよ。ということをなるべく伝えるようにしています。そのエンジニアが次の開発を行う時に頭の片隅においてもらえるように。
Web系の会社では比較的スクラップ・アンド・ビルドが多いのでその機会には恵まれているのではないかと思います。
使われなくなった機能のコードを消すことが `尊い` と言われますが、その機能が何故使われなくなったのか?まできちんとふりかえるとより効果があるのでは?と私は思っています。
fluctに入ってからは中々見届けることが出来ていないのですが、若い子たちにそういう動きを取ってもらいたいと思っているので、促すようにしています。

Web系の会社に入ってから考えを改めたこと

1. バージョンアップへの追随を怠らないこと

コアシステムのバージョンアップでも社内向け管理画面でも、テスト/監視系/リリースフローが充実していれば同じように積極的に動くべきだと思います(メジャーバージョンアップについては検討の必要あり)。
SIer時代は最初に作った時のバージョンで塩漬けにする事が多く、塩漬けにしたことで後々辛くなることが多かったのでこの点は考え方を改めました。

具体的には以下参考。

techlog.voyagegroup.com

 

2. 小さく・早くはじめるために、さくっと作る

「どうすれば、新しい広告(サービス)をユーザに提供できるのか?」をビジネスサイドの人間と一緒に考え、「最初は手数を減らしてでもとりあえず動くものを作れるか?」を考えること。
SIer時代はどうしても「納品」という概念があったので(チームが別なことが多い)納品後のことまでセットで自分で調整するのは難しかったのであまり意識していなかったのですが、
全て自分でやらないといけないWeb系ではリリース時に必要最低限なものだけを作って後は機能追加でカバーするということを最初はうまく切り分ける事ができていませんでした。
今は少しずつ出来るようになってきているかなと思っています。

おまけ1 : SIerからWeb系に転職しようと思った時にどう動いたか?

転職エージェントさんに相談した上で、自分の強みが何か?を考えてアピールするようにしてみました。
(転職しようと思ったのは2014年6月、実際に転職したのは2015年2月のことです)

  • VOYAGE GROUPではない広告配信している会社に合計で4年程SESとして仕事をしていたので広告配信の業務知識があった(SESの前に少しやっていたのも併せると5年弱の経験があった) => そこを押してみようと考えた。
  • SIer時代に10Projectほど追加開発・保守をバランス良く経験して、このプロジェクトが何故、成功・失敗したのか?を色々見る事ができた。
    そこから汎化できるようになっていた(当時の上司から、「失敗したときだけではなく、うまく行ったときにもきちんとふりかえりを行い、うまく行った理由も考えるといい」と言われ、それを自分の中に積み重ねることが出来ていたので自分の中で汎化できていたのではないかと思っています)
  • 私は「XX(言語、インフラ等)がやりたい <<<< 使ってくれる人の声を聞いて仕事がしたい(裁量権をもって仕事がしたい)」と普段から考えています。
    保守・運用っぽいことをやりたがるエンジニアは少ないのでそこもアピールしてみました。

おまけ2 : SIerから今の会社に転職して良かったこと、微妙だと思っていること

  • 良かったこと
    • 自分と同じような考え方を持っている人と一緒に仕事ができること
    • エンジニアにかかわらず若くて優秀な社員が多い
      が、運用経験値は多いとはいえないので彼らをサポートするという自分の立ち位置を見つけることが出来た。(若い子たちからすると口うるさいおっさんだと思ってるだろうけど...)
    • 正しい(≒やりたい)と思ったことを実行に移すのが比較的容易
      VOYAGE GROUPはかなりハードルが低い方だと思う。特にfluctはVOYAGE GROUPの中でも低く、Rustが本番サービスで動いていたりします。
    • 勉強会に行った時に、取引のある同業種のエンジニアさんたちと交流が取りやすい(SES時代にはビジネス的な話をするのは難しかった)。
    • VOYAGE GROUPに猛者がいっぱいいるので、お話する(あるいは誰かと誰かが話しているのを聞く)たびにいろんな刺激を受ける事ができる。
      (猛者 : 機械学習に強い人、言語の最新動向に精通してる人、@t_wada (※)等々) ※ @t_wadaはVOYAGE GROUP内のfluctを含む幾つかの子会社に週に一度ずつ来ていただいています
    • 事務方が素晴らしい。(改善要望を出すと早いと当日中に改善されたりする)
  • 微妙だと思っていること
    • 状況の変化(ビジネスの話)が激しく、キャッチアップしきれないことがある
    • 少ない人数でいかに回すか?という(うまくタスクを委譲する)スキルが自分に欠けていたためプロジェクトメンバーに迷惑をかけてしまったことがある
    • 個人的に好きな(手に馴染む)Scalaを使う機会をまだ作れてない
      (Scalaで書くより違う方法で解決したほうが早く、サービスレベル向上のためにはそちらを選択する事になっているため)

SIer出身でも自分の立ち位置(武器)を見つけ、当たり前にやるべきことをきちんと遂行できれば何とかなるかなというのが私の転職に関する感想です。

もし(特に猛者たちに)興味が沸いたら、fluctに限らずVOYAGE GROUPは中途採用募集中ですのでお気軽にご応募ください。社内バーAJITOでのカジュアル面談もご用意しています!

voyagegroup.com

 

人工知能学会誌の特集「広告とAI」にZucks Ad Networkの取り組みを寄稿しました

hagino3000です。今週はカナダで開催中のKDD2017に参加しています。

人工知能学会誌の7月号の特集「広告とAI」に「アドネットワークにおける広告配信計画の最適化」という記事を寄稿しました。軽く内容を紹介します。

【会誌発行】人工知能学会誌 Vol. 32 No. 4 (2017/07) – 人工知能学会 (The Japanese Society for Artificial Intelligence)

内容について

導入はインターネット広告事業のタイプ(SSP, DSP, アドネットワーク, etc.)とそれぞれの役割を紹介。中でもアドネットワークは媒体社と広告主の両者の要望を満す必要があるため、広告効果と媒体社収益の2つが要件になる事を説明しました。
インターネット広告を例に出す論文は「クリック課金型広告においてはクリック率の高い広告を出せば良い」という設定をしばしば持ちだします。そのせいかアカデミアの人と話をしていると「ネット広告って、バンディットでクリック率の高いバナーを出していくだけでしょ?」と言われる事があります、が実際はそんな事無いです。

配信する広告を選択する方策については「活用と探索のジレンマ」に軽く触れた後に、広告配信の設定に適用しやすいバンディットアルゴリズムの手法であるThompson Samplingを紹介。報酬を クリック単価×クリック率 とした時の手続きを例に挙げました。

最後にCPA1を制約として媒体社収益を最大化する方策についてまとめています。 記事中では簡単のためにCPAを制約としましたが、広告主が求める効果はCPAに限らず流入ユーザーの継続率やROAS2であったりもします。後者の方がサンプルサイズが小さいため、制約の難易度はより高くなります。

まとめ

今回は地味な内容となりましたが、プロダクション未投入の新奇性のある内容も出せていければと思っています。 「広告とAI」特集の他の記事だと、理研AIP 前原氏による「ディスプレイ広告に対するリアルタイム入札」はRTBの入札戦略を離散最適化で解いているのが面白かったです。RTBの入札戦略を俯瞰している日本語の文章は他に見た事がないので、その点でも貴重だと感じました。

AI書庫へのリンク


  1. コンバージョン獲得1件あたりのコスト

  2. 広告経由で流入したユーザーによる売上を広告費用で割った物

builderscon tokyo 2017 で #ajitofm の公開収録をしました!

こんにちはこんにちは!株式会社 fluct で Web 広告配信のお手伝いをしている @jewel_x12 です!

皆さんは builderscon tokyo 2017 へ参加されましたでしょうか!?様々なセッションがあり、The・ギークのお祭りでしたね!楽しかった!

VOYAGE GROUP はランチセッションスポンサーとして、1日目のランチセッションに ajitofm 公開収録をさせていただきました。

ajitofm って?

ajito.fm

ajitofm とは VOYAGE GROUP の社内バー AJITO での語らいを収録したポッドキャストです(実は AJITO で収録していないという爆弾発言が公開収録でありました)。 すずけんさん(@suzu_v) を中心として、精力的に更新されているので興味がある方は聞いてみてください!VOYAGE GROUP のエンジニア事情や面白い話が色々聞けます。

ランチセッションで公開収録をすることになった経緯

せっかくランチセッションの枠をいただいたので、VOYAGE GROUP をより知っていただくために何かコンテンツがないかと考えていました。下記のようにぼやいていたところ

f:id:jewel12:20170807192400p:plain

すずけんさんがやりますよーと言ってくださったのでその方向でよしなにやることになりました。YAPC (Yet Another Perl Conference)発なのかどうかは要出典。

最初はトークセッションをするだけのつもりだったのですが、社内PAさん(@brtriver)の存在もあり収録にもチャレンジしてみることに。 会場にマイク 4 本とミキサー、ケーブル類を持ち込み、前セッションとの入れ替わり時にセッティングという流れでした。こういう入れ替わり時にセッティングするというような、余裕のないデプロイは大体失敗すると思っているのですが、PAさんの腕が確かなのかうまくいってよかったです!どきどきでした。

音響周りをカスタマイズしたり、机や椅子を用意していただくなどいろいろなワガママに付き合っていただいたスタッフの皆様には本当に感謝しております。

当日の様子

#builderscon ランチセッション始まりました! #ajitofm

builderscon.ioさん(@builderscon.io)がシェアした投稿 -

収録に参加したすずけんさん・ajiyoshi さん・トミールさん・yowcow さん

f:id:jewel12:20170808114541j:plain

無理やりトークパネルを埋めました。

f:id:jewel12:20170808114327j:plain

PAさん(@brtriver)の様子。手つきが素早いですね。私は主にスポンサー業としてスタッフさんとやり取りをしたりチラシを作ったりだったので当日はたいしてやることが無く、イベントホールをブラブラしてました。朝食が美味しかったですね!

30 分あっという間でしたが Twitter などで様々な反応をいただいたので、やってよかったです!

twitter.com

スタッフさんや会場の柔軟さ、腕のいいPAがいるなど、いくつかの条件が重ならないと実現は難しいかもしれないですが、テック系ラジオをされている方はこのような登壇方法もいかかでしょうか!?

最後に、スタッフの皆様、登壇された皆様、とても楽しいイベントを提供してくださりありがとうございました!

今回収録した内容は ↓ から聞けます!公開収録で ajitofm に興味を持ってくださった方は他の回も聞いてみてください!

ajito.fm

大量データの転送にEmbulkを使ってみたら本当に楽だった

はじめまして。Zucks Affiliateでエンジニアをしている宗岡です。 今回は、リアルタイム性は求めないけど、簡単に大量のデータをどこか別の場所に転送したい。 という要望に答えてくれるEmbulkを紹介したいと思います。

実際に導入に至ったきっかけや、運用上よくある課題なども触れていきたいと思います。 同じ境遇の人が「簡単そうだしEmbulk使ってみようかな」となっていただければ幸いです。

目次

背景

  • 私の所属する事業部でも、BigQueryを使って色々と解析する機会が増えてきました
    • (広告効果向上のためにさまざまなデータを取り扱い、分析等を行っています)
  • ところが、BigQueryに乗っているデータは今のところアクセスログやアプリケーションログのみ
  • これだとデータとして足りず、DBに乗ってるコンバージョンしたユーザのデータもBigQueryに上げられれば、解析がより楽になりそうだ。というのがきっかけでした。

Embulk以外にも出てきた案

  • re:dashを導入し、「Query Results」を使って、BigQueryとDBのデータをJOINして解析
    • これなら特に努力する必要なく、re:dashの導入と、データソースの追加だけで完結です
    • ただ、残念ながら「Query Results」機能はまだオープンソースとしては公開されていないという悲しい現実がありました
    • Pythonをデータソースに追加することで、無理やり別々のデータソースから取得した結果をマージする方法もありますが、中々辛みがあり、なるべく避けたかったです
  • MySQLのデータをBigQueryに上げるという選択肢が挙がり、(弊社的には)割りと一般的な「MySQL > S3 > GCS > BigQuery」という方法もありましたが、下記のような理由から今回はEmbulkを採用してみました
    • 定期的にバッチとしてデータを取り込み続けたい
    • 今後も同じ事をしていくテーブルが増えそう
    • 社内の他事業部でも導入実績があった

などの理由で、今回はEmbulkを使ってみました

実際のEmbulkの導入と使い方

見ていただければわかると思いますが、非常に簡単です。

1. Embulkのインストールとセットアップ

具体的なセットアップ周りでは、embulkの公式をご参照下さい。

インストールはとても簡単です。

$ curl --create-dirs -o bin/embulk -L "http://dl.embulk.org/embulk-latest.jar
$ chmod +x bin/embulk

これだけ完了です。 もしグローバルでEmbulkを使いたければ、パスを通してあげればOKです。

2. 必要なプラグインのインストール

今回はMySQLからinputして、GoogleのBigQueryにoutputしたかったので、 次の2つのプラグインをインストールします。

  • embulk-input-mysql
  • embulk-output-bigquery

まずはGemfileを用意してあげます。

# For Embulk
source 'https://rubygems.org'

# input mysql plugin
gem 'embulk-input-mysql'

# ouput bq plugin
gem 'embulk-output-bigquery'

そして、embulk bundle

$ ./bin/embulk bundle --path=bundle
2017-07-18 02:15:05.887 +0000: Embulk v0.8.27
Fetching gem metadata from https://rubygems.org/...........
Fetching version metadata from https://rubygems.org/..
Resolving dependencies......
Installing public_suffix 2.0.5
Installing addressable 2.5.1
Installing declarative 0.0.9
Installing declarative-option 0.1.0
Installing embulk-input-mysql 0.8.4
Installing multipart-post 2.0.0
Installing faraday 0.12.1
Installing jwt 1.5.6
Installing little-plugger 1.1.4
Installing multi_json 1.12.1
Installing logging 2.2.2
Installing memoist 0.16.0
Installing os 0.9.6
Installing signet 0.7.3
Installing googleauth 0.5.1
Installing httpclient 2.8.3
Installing mime-types-data 3.2016.0521
Installing mime-types 3.1
Installing uber 0.1.0
Installing representable 3.0.4
Installing retriable 3.0.2
Installing google-api-client 0.13.1
Installing thread_safe 0.3.6
Installing tzinfo 1.2.3
Installing time_with_zone 0.3.1
Installing embulk-output-bigquery 0.4.5
Using bundler 1.10.6
Bundle complete! 2 Gemfile dependencies, 27 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

これで準備はOKです。

3. 設定ファイルを書く

次はEmbulkの設定ファイルを書いていきます。 設定ファイルは基本的にyaml形式で書きます。

Embulkでは、Liquidというテンプレートエンジンを搭載(v0.7.7以降)しているので、この機能を使いたい場合は、 ファイル名をxxxx.yml.liquidのようにします。

また、今回の例ではDBの接続情報は環境変数(.envファイル)を使ってみる事にしましたが、 実務ではAWSのCodeCommitで管理しており、大分便利ですのでオススメです。

実務でcodecommitを使った例

下記のようにすることで、.envを使うよりもより簡単にクレデンシャルを扱う事が可能になります

  • codecommitのリポジトリを特定ディレクトリ(今回は/credential)にclone
  • Makefileで必要な環境変数はexportし、設定ファイルで読み込めるようにする
  • (codecommitでクレデンシャル情報を管理出来るので、リリース時の.envファイルの扱い等は考える必要なくなります)
# rdsのクレデンシャル情報
include /credentials/rds/ro_connection_info.txt

export MYSQL_HOST      := $(host)
export MYSQL_USER      := $(user)
export MYSQL_PWD       := $(password)

# gcpのクレデンシャル情報
export CREDENTIA_JSON  := /credentials/gcp/credential.json

設定ファイルの書き方

in:
  type: mysql
  host: {{ env.MYSQL_HOST_NAME }}
  user: {{ env.MYSQL_USER }}
  password: {{ env.MYSQL_PASSWORD }}
  database: embulk_test
  table: user
out:
  type: bigquery
  auth_method: json_key
  json_keyfile: ./credentials/bq_project.json
  path_prefix: tmp
  file_ext: .csv.gz
  source_format: CSV
  project: test-bigquery-project-166901
  dataset: test_for_embulk
  auto_create_table: true
  table: users
  formatter: {type: csv, charset: UTF-8, delimiter: ',', header_line: false}
  encoders:
  - {type: gzip}

BQ側のプロジェクトや、データセットは今回の為にテスト的に作ったものを指定してます。

また、input/outputのそれぞれの設定は各プラグインのページを見ると詳細が載ってますので、イロイロ試してみると面白いです。

参考: embulk-input-mysql embulk-output-bigquery

4. まずはpreviewで問題なさそうか確認

previewは、dry-run機能として使えます。 実際にEmbulkを実行する前に、どんなデータが転送されそうかを確認出来ます。

$ ./bin/embulk preview -b . user.yml.liquid
2017-07-19 23:27:13.766 +0000: Embulk v0.8.27

... 中略 ...

2017-07-19 23:27:15.841 +0000 [INFO] (0001:preview): SQL: SELECT id,name,created_at FROM `user`
2017-07-19 23:27:15.854 +0000 [INFO] (0001:preview): > 0.01 seconds
2017-07-19 23:27:15.922 +0000 [INFO] (0001:preview): Fetched 500 rows.
+---------+------------------------+-------------------------+
| id:long |            name:string |    created_at:timestamp |
+---------+------------------------+-------------------------+
|       1 |            Valerie Cox | 2017-07-11 09:31:15 UTC |
|       2 |         Jennifer Perry | 2017-07-15 10:02:30 UTC |
|       3 |           James Garcia | 2017-07-01 05:56:45 UTC |
|       4 |         Haley Calderon | 2017-07-28 21:08:42 UTC |
|       5 |     Cindy Williams DDS | 2017-07-06 17:26:38 UTC |
|       6 |       Christina Mathis | 2017-07-27 22:45:17 UTC |
|       7 |         Timothy Barker | 2017-07-28 12:12:40 UTC |
|       8 |             Thomas Lee | 2017-07-01 01:29:39 UTC |
|       9 |            Jamie Smith | 2017-07-09 03:47:16 UTC |
|      10 |         Wesley Santana | 2017-07-23 18:42:06 UTC |

~長いので省略~

5. 問題なさそうなのでrunして実行

$ ./bin/embulk run -b . user.yml.liquid
2017-07-19 23:27:53.166 +0000: Embulk v0.8.27

... 中略 ...

2017-07-19 23:28:12.897 +0000 [INFO] (0016:task-0000): Fetched 1,000 rows.
2017-07-19 23:28:14.252 +0000 [INFO] (0016:task-0000): Fetched 2,000 rows.
2017-07-19 23:28:15.966 +0000 [INFO] (0016:task-0000): Fetched 4,000 rows.
2017-07-19 23:28:18.242 +0000 [INFO] (0016:task-0000): Fetched 8,000 rows.
2017-07-19 23:28:19.496 +0000 [INFO] (0001:transaction): {done:  1 / 1, running: 0}

... 中略 ...

2017-07-19 23:28:55.069 +0000 [INFO] (main): Committed.
2017-07-19 23:28:55.069 +0000 [INFO] (main): Next config diff: {"in":{},"out":{}}

終わりました。 今回対象件数は1万件程でしたが、 約1分 で完了です。 とっても楽ちんですね。

BigQuery上でもこのようにちゃんと1万件分のデータがアップロードされてるのが確認出来ます。

スクリーンショット 2017-07-20 08.30.37.png

Embulkの運用上、よくぶつかる課題

Embulkを使うと簡単に転送出来る事が分かった所で、運用上ハマりどこになりそうな所を上げておきます。 ハマったとしても、大体やりたい事はプラグインを見れば解決出来る設定方法が書いてあるので参考にしてみると良いと思います。

今回であれば下記2つのプラグインを参照してます。

embulk-input-mysql embulk-output-bigquery

1. 重複に気付け無い

Embulkを使う機会は、大量のデータを定期的にインサートしたい場合だと思いますので、 cron等で動かす事になるかと思います。

その場合、cronでも動かしてるものを、誤って手動でインサートした場合、そのまま重複した状態でデータがインサートされてしまいます。

対応 - prevent_duplicate_insertをtrueに設定する

これが困ってしまうという場合は、BigQueryの場合は、prevent_duplicate_insertをtrueにしておく事で、重複を弾けます。

実際に、この設定を追加してやってみましょう。

$ git diff user.yml.liquid
diff --git a/user.yml.liquid b/user.yml.liquid
index 37a7074..e1980ff 100644
--- a/user.yml.liquid
+++ b/user.yml.liquid
@@ -16,6 +16,7 @@ out:
   project: test-bigquery-project-166901
   dataset: test_for_embulk
   auto_create_table: true
+  prevent_duplicate_insert: true
   table: user
   formatter: {type: csv, charset: UTF-8, delimiter: ',', header_line: false}
   encoders:

prevent_duplicate_insertをtrueにしてみます。

$ ./bin/embulk run -b . user.yml.liquid
2017-07-19 23:44:22.042 +0000: Embulk v0.8.27
...中略 ...
Error: org.jruby.exceptions.RaiseException: (Error) failed to load tmp.30148.5930.csv.gz to test-bigquery-project-166901:test_for_embulk.LOAD_TEMP_0b5d4c5e_e440_407e_8a2d_92caeb232bd5_user, response:{:status_code=>409, :message=>"duplicate: Already Exists: Job test-bigquery-project-166901:embulk_load_job_dcec11ac837806a8524fcecf0b5b12a5", :error_class=>Google::Apis::ClientError}

Already Exists: Job test-bigquery-project-166901:embulk_load_job_dcec11ac837806a8524fcecf0b5b12a5"という事でちゃんと重複データを入れようとするとエラーで落としてくれました。

2. 転送元のスキーマを変更するとエラーで落ちる

転送元をデータベースにしている場合などは、スキーマの変更は珍しくないと思います。 この場合、デフォルト設定のままだとカラム追加時にEmbulkはエラーで落ちます。

理由は2つあります。

  • 設定ファイルのinputの設定に、テーブル名だけを指定している事
    • テーブル名だけの指定だと、全てのカラムを転送しようとするので、今回追加するageカラムも追加しようとします
  • BigQuery上に作ったスキーマと異なるデータを転送しようとしている事

今対象としてるサンプルで使ったテーブルはuserというシンプルなテーブルです。

mysql> desc user;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name       | varchar(32)      | NO   |     | NULL    |                |
| created_at | timestamp        | NO   |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

こちらのテーブルに、ageカラムを追加してみます。

mysql> ALTER TABLE user ADD column age INT UNSIGNED NOT NULL AFTER name
    -> ;
Query OK, 0 rows affected (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc user;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name       | varchar(32)      | NO   |     | NULL    |                |
| age        | int(10) unsigned | NO   |     | NULL    |                |
| created_at | timestamp        | NO   |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

この状態でEmbulkを走らせてみます。

$ ./bin/embulk run -b . user.yml.liquid
2017-07-20 00:01:08.421 +0000: Embulk v0.8.27

... 中略 ...

Error: org.jruby.exceptions.RaiseException: (Error) failed during waiting a Copy job, get_job(test-bigquery-project-166901, embulk_copy_job_dedd3a2b-d82d-4bc8-af97-e68aff6540fa), errors:[{:reason=>"invalid", :message=>"Provided Schema does not match Table test-bigquery-project-166901:test_for_embulk.user. Cannot add fields (field: age)."}]

BigQuery上のテーブルスキーマと異なるデータ(今回だと新しく追加したageカラム)も転送しようとして落ちてしまいます。

対策 - 転送するカラムを指定しておく

これを防ぐには、予め転送するカラムを指定しておく事で解決できます。 詳しくは、mySQL input pluginの設定に記載してありますが、今回の場合は、テーブル名だけでなく、カラムも指定してあげるようにしておけばOKです。

$ git diff user.yml.liquid
diff --git a/user.yml.liquid b/user.yml.liquid
index d6a52e9..37a7074 100644
--- a/user.yml.liquid
+++ b/user.yml.liquid
@@ -5,6 +5,7 @@ in:
   password: {{ env.MYSQL_PASSWORD }}
   database: embulk_test
   table: user
+  select: id,name,created_at
 out:
   type: bigquery
   auth_method: json_key

この状態でもう一度Embulkを実行してみましょう。

$ ./bin/embulk run -b . user.yml.liquid
2017-07-20 23:07:31.758 +0000: Embulk v0.8.27

...中略...

2017-07-20 23:07:51.614 +0000 [INFO] (0016:task-0000): Fetched 1,000 rows.
2017-07-20 23:07:52.890 +0000 [INFO] (0016:task-0000): Fetched 2,000 rows.
2017-07-20 23:07:54.619 +0000 [INFO] (0016:task-0000): Fetched 4,000 rows.
2017-07-20 23:07:56.872 +0000 [INFO] (0016:task-0000): Fetched 8,000 rows.
2017-07-20 23:07:58.057 +0000 [INFO] (0001:transaction): {done:  1 / 1, running: 0}

...中略...

2017-07-20 23:08:33.688 +0000 [INFO] (0001:transaction): embulk-output-bigquery: 
Copy job response... job_id:[embulk_copy_job_38ef8d00-a904-456d-824f-3cd36698c16f] response.statistics:{:creation_time=>1500592103079, :start_time=>1500592103219, :end_time=>1500592105762}
2017-07-20 23:08:33.688 +0000 [INFO] (0001:transaction): embulk-output-bigquery: Delete table... test-bigquery-project-166901:test_for_embulk.LOAD_TEMP_282263eb_ec81_4868_9115_bee413408092_user
2017-07-20 23:08:34.322 +0000 [INFO] (0001:transaction): embulk-output-bigquery: delete tmp.32592.5930.csv.gz
2017-07-20 23:08:34.333 +0000 [INFO] (main): Committed.
2017-07-20 23:08:34.333 +0000 [INFO] (main): Next config diff: {"in":{},"out":{}}

問題なく転送出来ている事が分かります。 このように、予めスキーマが変わりそうな場合は、 selectで対象カラムを指定し、想定内のカラムのみが転送される状態にしておくのが望ましいと思います。

また、SQLの取得には下記の様に2種類ありますので、直接Queryを書いてしまうというのもありです。

  • query にクエリを直接書く
  • query は書かずに、table/select / where / order_by を記載

3. 設定ファイルが増えてくる(対象テーブルが増えてくる)と重複が多くなる

設定ファイルの中でも、実際変わるのはテーブル名・カラム名・Project名とかだけだと思います。 その他の接続情報などはほとんど同じです。 これらを、転送テーブルを増やす度に、都度設定ファイルに書くのは面倒です。

対応 - liquidテンプレートのinclude機能を使う

実務では、liquidテンプレートの機能を使い、下記のような構成で運用しています。

- templates
    - common
        - _conf_mysql_include.yml.liquid
        - _conf_bigquery_include.yml.liquid
    - user.yml.liquid

で、実際にuser.yml.liquidは、下記のように、 commonに書いたmysqlとBigQueryの設定ファイルをincludeして使っています。 (includeされるファイルは、ファイル名の先頭に_(アンダースコア)を付けてあげます)

in:
  {% include 'common/conf_mysql_include' %}
  table: user
out:
  {% include 'common/conf_bigquery_include' %}
  table: user

これでテーブル毎に異なる設定をする場合も、 共通項はcommoに切り出せているので重複を防ぐ事が可能となります。

4. エラーメッセージが分かりづらいので整理しておく

このように、非常に簡単に、あらゆる大規模なデータソースを、短時間で転送出来るという便利な代物なEmbulkですが、若干エラーメッセージが分かり辛いので、よく遭遇するエラーメッセージを紹介しておきます。

4-1. Setting null to a task field is not allowed

これは、特定のフィールドにnull値は入れられないよ。

という意味ですが、よくあるのは、.envを使って環境変数で設定ファイルを書いている場合、 .envに書いた環境変数をexportしてない可能性が高いです。 (ここで再現した例も、環境変数のexportをしてないのが原因です)

$ ./bin/embulk run -b . user.yml.liquid
2017-07-20 23:19:19.560 +0000: Embulk v0.8.27
2017-07-20 23:19:23.055 +0000 [INFO] (0001:transaction): Loaded plugin embulk-input-mysql (0.8.4)

... 中略 ...

Error: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Setting null to a task field is not allowed. Use Optional<T> (com.google.common.base.Optional) to represent null.

4-2. Can not deserialize instance of java.lang.String out of START_OBJECT

これは、liquidテンプレートを使っているのに、ファイル名にliquidがついてない時にでます。

今回あえて、これまでのuser.yml.liquiduser.yamlに変えてみます。

$ ./bin/embulk run -b . user.yml
2017-07-20 23:13:47.582 +0000: Embulk v0.8.27
2017-07-20 23:13:50.058 +0000 [INFO] (0001:transaction): Loaded plugin embulk-input-mysql (0.8.4)
2017-07-20 23:14:01.579 +0000 [INFO] (0001:transaction): Loaded plugin embulk-output-bigquery (0.4.5)
org.embulk.exec.PartialExecutionException: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: N/A; line: -1, column: -1]

... 中略 ...
    
    Error: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: N/A; line: -1, column: -1]

4-3 Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token

これは、配列で指定する必要がある所を、文字列で指定している為に出るエラーです。 例えば、incremental_columnsは配列で指定する必要がありますが、 わざと配列指定じゃなくしてみます。 (正しくは、incremental_columns: [id]とする必要があります)

$ git diff user.yml.liquid
diff --git a/user.yml.liquid b/user.yml.liquid
index 37a7074..a30d64d 100644
--- a/user.yml.liquid
+++ b/user.yml.liquid
@@ -6,6 +6,9 @@ in:
   database: embulk_test
   table: user
   select: id,name,created_at
+  incremental: true
+  incremental_columns: id
 out:
   type: bigquery
   auth_method: json_key

これで実行してみます。

$ ./bin/embulk run -b . user.yml.liquid
2017-07-20 23:39:54.033 +0000: Embulk v0.8.27
2017-07-20 23:39:57.515 +0000 [INFO] (0001:transaction): Loaded plugin embulk-input-mysql (0.8.4)
2017-07-20 23:40:08.258 +0000 [INFO] (0001:transaction): Loaded plugin embulk-output-bigquery (0.4.5)
... 中略 ...
Error: org.embulk.config.ConfigException: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
 at [Source: N/A; line: -1, column: -1]

まとめ

今回は、大量データを簡単に別のDWHに転送する方法としてEmbulkを紹介しました。 また、実際に運用上に困ってくる事も話してみました。

冒頭に書いた様に、リアルタイム性は求めないものの、簡単にデータ転送したいな。 という欲求をお持ちの方は、一度試してみてはいかがでしょうか。 御覧頂いた通り、非常に簡単に始められますので非常にオススメです。

技術サポートするときに気をつけている5つのこと

株式会社fluctのエンジニア長谷川です。

弊社はフルスクラッチで開発,提供をしているfluct SSPというプロダクト以外にも、Googleの認定パートナーとしてGoogleのプロダクトを利用したメディアのマネタイズのお手伝いも行っています。主なプロダクトはGoogle AdSenseDoubleClick AdExchange, DoubleClick for Publishersです。

これらのプロダクトは非常に高機能な反面、効果的に活用するにはネット広告一般やプロダクト自体に関する高度な知識が不可欠です。そこでfluctがプロダクト運用のお手伝いをしています(詳しくはこちら)。

私のfluctでのミッションとして、Googleの商材の技術的なサポートというものがあります。具体的には…

  • コンサルティングサポート
    • お客様あるいは弊社コンサルのアイディアのフィジビリティ調査
    • アドが出ないなどのトラブル対応
  • 運用サポート
    • レポーティングツール開発
    • 広告運用のシステム化検討及び開発

コンサルティングサポートはお客様のアドテクに関する知識レベルややりたいことは多種多様であるため、相手の意図を正しく理解し、かつこちらの意図をいかに正しく伝えるかが重要となってきます。

このミッションに携わってほぼ1年近く経ち、ちょうどブログ記事を書く機会を得られたのでせっかくなのでこの仕事をする上で心がけていたことをメソッド集という体で書いてみたいと思います。

目次

メソッド1: 相手の話を自分の言葉で再翻訳する

以前同僚に「あなたは相手の意図を確認する習慣があって、それがとてもよい」と指摘されたことがあります。あまり意識せずにやってたのですが、言われてから意識的にやるようになりました。

みんなではじめるデザイン批評」という書籍ではこれを「アクティブリスニング」と紹介されています。会話例がとても良かったので引用します。

アクティブ・リスニングには、受けたフィードバックに対してことばを返すときに、相手の話を別のことばに言い換えて繰り返すというやり方がある。そうすればフィードバックを与える側は、私達の理解と自分が伝えたいポイントとが一致しているかを確かめることができる。以下に例を示す。

製品のオーナー「スクリーン上のニュースフィードの配置が気になります。とても目立ちますが、実際に顧客がそれほどひんぱんに使うとは思いません。」

デザイナー「わかりました。私の理解が正しければ、心配なのは、あまり使われないかもしれないニュースフィードが注目を引きすぎて、ユーザーの注意が他のもっと重要な要素からそれてしまうのではないか、ということですね?よろしいでしょうか?」

製品のオーナー「はい、顧客の使用頻度の高いものに、より高い優先順位をつけられるよう、別の処理の仕方を検討するべきです。」

理解が正しくない場合は、さらに明確にするためにフィードバックを掘り下げる質問をすればいい。

「ニュースフィードが目立つという心配について、もう少し詳しく説明していただけますか?」

あるいは、

「わかりました。その点をもう少し考えてみましょう。このまま目立たせておいたとしたら、どうなるでしょうか?」

(「みんなではじめるデザイン批評」 158ページより。一部編集)

アクティブリスニングは相手に対して「私はあなたの話を聞いています」という姿勢を見せると同時に話者の中でも理解できたこと、わからなかったことを整理するのに役立ちます。これは次節で紹介する「目に見える形」とセットでやっています。

メソッド2: 目に見える形で共有しながら話をする

人間、あれとこれを話そうと考えていてもいざ会話を始めると必ず漏れが発生します。それを防ぐには喋っている内容を即時文字にして目に見えるようにすることです。

- 起こっていること
    - XXで広告タグを追加設置したが広告が出ない
- いつから
    - 昨日の夜ぐらいから
- 誰が観測したか
    - メディアの閲覧者からメールで連絡があり、我々に問い合わせが来た
    - 現在もタグが貼られているので我々でも確認は可能

こんな風に、聞き手がテキストを起こすのと同時に、情報が足りない箇所や深掘りしたいところを都度質問していき内容を増強していきます。あとはこれをそのままissueトラッカーに貼り付ければトラッカーベースでやり取りしていけます。

この方法は特にトラブルが発生した時に以下のような効果があると思います。

  • 一緒にトラブルを解決しよう、という姿勢により報告者を落ち着かせられる
  • 余裕がない状況でも思考を整理できる

メソッド3: 思考をまとめるための自分のベストツールを揃える

自分の中で思考を目に見える形で整理する方法を確立しておきます。日頃から可視化するようにしておくと、前述の相手との状況の可視化をするためのトレーニングにもなります。これはデジタルでもアナログでも構いません。

なお、私はevernoteなどのメモツールを挑戦してみましたがどうしてもしっくり来なくてノート + ペン というオールドスタイルで落ち着いています。

弊社でよく目にするツールはこのノートとホワイトボードが一体化したノートです。ちょっとしたお話を図にして共有するのに使っているようです。

私はアピカの方眼紙のノートがお気に入りです。ノートに1500円…と驚く方もいますが、このノートは紙質がとても良くて、メモを書いてて気持ちが良いのです。

f:id:hasegawas:20170713113654j:plain

このようにキーボードの前に置いて、考え事する時はノートにひたすら書いて、コードを書いてるときはディスプレイを見てっていう風に仕事をしています。(実際にメモが書いてるページは業務内容そのままだったので真っ白のページにしています…)

メソッド4: お客様とのミーティングに同行し積極的に発言する

自社プロダクトをサーブしている会社だとエンジニアはなかなか表に出てこない存在になりがちですが、案件の難易度によっては自らお客様との打ち合わせに同行することを願い出ます。これは以下のような効果があります。

  • 関係者が多い場合に伝言ゲームにならないようにすることができる
  • 自分の中の経験を増やす効果がある
    • 会社によってまるで文化や共通言語が違うことを体感し、コミュニケーションの仕方の手札を増やすきっかけとなる
    • 勉強会だと文化が似ている会社の人が集まりがち

メソッド5: 常に機嫌よくいる

最後は精神論かよ…と言われてしまいそうですが、これが一番大事だと思います。トラブルが発生した!!って言う時もその状況を面白がれるぐらいのメンタルが欲しいです(ライトな雰囲気は報告してきた相手の気持ちも落ち着かせる効果があると私は思っています。)

  • 優れた,面白いアイディアは機嫌が良い人に集まってくる
    • 思いついたことを気軽にSlackに投入できてそれ面白いねって反応が出るぐらいだとよい
  • 機嫌が悪いと「話しかけないでおこう」という雰囲気が出来てしまい、聞けば即時解決できたトラブルが後回しにされてしまう
    • その結果また機嫌が悪くなるという悪循環

また、話を聞いた時にそれ前も同じことあったよね、と突っ返さないのも大事です。話しかけにくい、機嫌が悪いって思われて損です。トラブルが終わって落ち着いたときの振り返りにしれっと話してみるのがよいと思います。

まとめ

以上、1つ1つは小さなことかもしれませんがコミュニケーションを円滑にするためのあなたのヒントになればと思います。

この記事を読んで弊社に興味を持ったあなた、ポジションを用意してお待ちしております!もちろん、その他のポジションも用意しておりますので興味があったら是非ご応募を!(露骨な宣伝)

チーム状態をスムーズに変えて障害対応のコストと精神的負荷を抑える

こんにちは。 @at_grandpa です。普段はバッチを書いたりメンテナンスをしています。

今回は、先日起きた障害対応の時、チームの状態をスムーズに変えることで対応コストと精神的負荷を抑えられた、ということを書きます。

 

目次

障害発生

先日の朝に「レポートの数値がおかしい」という連絡がきて確認したところ、とあることが原因で、バッチの自動実行が約半日行われていないことがわかりました。

 

f:id:at_grandpa:20170713114354p:plain:w400

 

普段の対応

普段の対応は以下のような形です。

  1. エラー発生をSlackの全体チャンネルで報告
  2. バッチ系チャンネルにて、考えや現状を垂れ流す
  3. わからないことがあれば有識者にメンションを飛ばす
  4. 実際に叩いたコマンドをどんどんSlackに書き込む
  5. 対応完了を全体チャンネルで報告
  6. チケットにまとめる

小規模なエラーの場合は上記の対応で事足ります。この方法のメリットは以下です。

  • 対応は一人ででき、他のメンバーのコストが発生しない
  • Slackに垂れ流すことでプチレビューの役割を果たす
    • 精神的負荷が多少軽減される
  • Slackにコマンドの履歴が残る

しかし、しばらく運用していると、デメリットが大きくなってきました。

  • 「一番知っている人はat_grandpa」という理由からか、指摘されることが少なくなった
  • 上記が理由で「孤独感」が強くなった
  • 孤独感から「この対応で大丈夫か?」という気持ちが生まれ、精神的負荷が増える
  • 対応方法が伝授されない
    • 記録はあるが、他のメンバーが同じ対応をできるかと問われるとなかなか難しそう
    • 実践で行わないと自信を持って対応することは難しい

信頼されることは嬉しいですが、この信頼はチームを腐敗させます。今までは「対応すること」を優先で行ってきましたが、実際はチームの動きを鈍らせてしまう原因になっているのだと気付きました。この点は反省すべき点です。(自分の共有方法やドキュメント記述方法にも問題があります。それはまた別の問題として認識します。)

しかし今回、新しいアプローチで障害対応を行ったところ、上記の懸念点も解決し、かつスムーズに対応が行えたので、ブログに記録したいと考えました。

 

今回の対応

今回の対応の流れは以下です。

  1. 原因究明と現状把握
  2. 関係者が会議室に集まる
  3. 対応用Slackチャンネルを開設
  4. ペアワークで実対応
  5. 落ち着いたら自席&Slackコミュニケーションへ移る
  6. 対応完了の確認と報告・チケットまとめ

一つずつ見ていきます。

原因究明と現状把握

あるバッチがエラーを吐くことはたまにあるのですが、今回は約半日動いていません。普段と異なる時間帯に数十のバッチを手動で叩かなければなりません。普段と規模が違います。

関係者が会議室に集まる

流石にひとりだと荷が重いと判断し、バッチ周りに触れたことのある新卒2年目の @saxsir256(以下@saxsir)と新卒1年目の@__himu__(以下@himu)にリカバリタスクをお願いしました。と、そこで以下の提案がきます。

f:id:at_grandpa:20170713085202p:plain

これはなるほどと思いました。関係者がガッと会議室に集まることで、以下のメリットがあります。

  • メイン対応者が明確になる
    • メインの対応者が明確でないと、チーム全体が「何かしないといけないのかな。。。」と不安定な状態になってしまいます。これを阻止するためにも「今回はこの3人で対応します」と名言できたのは良かったです
  • 初動が重要な場面で対面コミュニケーションできる
    • Slackは便利ですが、対面コミュニケーションの速度には敵いません
    • 初動の場面でスピーディに共有・方針決定できたのは大きかったです
  • 精神的負荷の軽減
    • 障害対応の初動は何かと不安が大きいですが、対面でのやり取りは精神的負荷をかなり抑えます

実際、この判断はとても良く、自分も把握しきれていないバッチを@himuに解読してもらいつつ、@saxsirと共にホワイトボードを用いて対応スケジュールを組み立てました。わからないことはすぐに共有、わかる人とペアワーク、という軽快な動きが可能でした。

対応用Slackチャンネルを開設

対応用Slackチャンネルの開設は、社内でも一時期話題になっていましたが、実際にメインの運用にはなっていませんでした。自分ひとりの対応の時に、試しに開設したりしていましたが、メモ程度の役割にしかなっていませんでした。しかし今回は3人がメイン対応者です。対面コミュニケーションのスピードは良いですが、記録を残すことも重要なので、とにかく、

  • 実行したコマンドとその結果
  • 現在何をしているかを投稿(他の外部メンバー向け)

を書いていきました。「このチャンネルで対応実況しています」ということを全体チャンネルで発言することで、続々と覗きにくる方が増え、メモ&外部共有が両立できたのは大きかったです。

ペアワークで実対応

先程も書きましたが、会議室に集まることでペアワークが可能となり、精神的負荷を抑えることができました。かつ、対応のちょっとしたノウハウなども詳細に伝えることができたり、実際にコマンドを叩く経験ができるため、今後の対応への自信につながるというメリットもあります。実際、自分でも詳細を知らないバッチを@saxsirと@himuが解読してくれ、それをSlackに残してくれているのでとても助かりました。

落ち着いたら自席&Slackコミュニケーションへ移る

バッチを順に叩いていきDBの状態を確認、次のバッチへ・・・という作業を続けていくと、「依存関係はもう無いし、後は順に叩いていくだけ」という場面になりました。この段階で会議室に集まっているメリットはもうあまりないと判断し、「自席に戻って他のタスクを行いつつ、Slackにて共有」という形に移りました。このようにチーム状態を変えたおかげで、午後からは他タスクに移ることができましたし、ちゃんとSlack上での確認も行え、無事対応を終えることができました。

対応完了の確認と報告・チケットまとめ

対応完了を全体チャンネルに報告し、チケットにまとめます。チケットへのまとめは、Slackの対応用チャンネルの重要部分のリンクを貼るだけです。まとめ直すのは結構コストが高いので、実対応が記録できる対応用チャンネルは便利だなと思いました。

 

まとめ

今回の対応は、以下の点がよかったと思います。

  • メイン対応者を明確にすることで、他メンバーのコストを抑えられた
  • 対応用Slackチャンネルのおかげで、記録と共有を両立できた
  • 障害対応の初動で対面コミュニケーションを取れた
    • 精神的負荷の一番大きなフェーズで負荷を抑えることができた
    • 方針決定までの速さが今までよりも早かった
    • 対応メンバー全員がバッチ周りに詳しくなった
  • 落ち着いたら自席&Slackコミュニケーションに移った
    • ずっと1日中対応しているのではなく、他のタスクにも移行できた
    • 結果、対応に対する時間を削減できた

個人的に学びがあったのは、対応の状況によって 会議室&対面 → 自席&Slack に移行したことで、対応の総コストと精神的負荷を抑えることができたという点です。

ともあれ、「何かあったら会議室」というのもおかしな話なので、臨機応変に活用していきたいと思います。

今回、「障害対応にはフェーズがある」ということと「新しい障害対応の方法」に気づけたのは非常に価値がありました。この経験を踏まえ、今後に活かしたいと思います。

 


 

VOYAGE GROUP では最近Podcastを始めました。 → Ajitofm

社内バー AJITO での語らいを収録したポッドキャストです。技術談義で盛り上がったり、もしかたら今後、障害対応の裏話なども聞けるかもしれません。