大量のエラー通知を垂れ流しつつ、Embulkのサーバー間の環境差異を解消した話

はじめに

こんにちは!
アイスタイルAdvent Calender2021の1日目の記事を担当するsugatoです。
去年のアドカレは、TwitterのAPIで戯れてみました。
TwitterAPIを利用していいね数の遷移を可視化する

現在はデータ分析基盤グループに所属し、全社分析基盤の運用保守を通して、RedashやTableauなどデータの利活用を推進しています。

アイスタイルでは全社分析基盤としてはGCPのBigQueryを採用し、社内のRDBのデータはOSSのミドルウェアであるEmbulkを利用して、BigQuery上に集約を行っています。
Embulkに関する紹介は他にも記事が多くあるため、割愛させていただきます。

現在、BigQueryに同期しているテーブル数は約1100以上存在しています。
アットコスメの歴史は20年以上に渡るため、1つのテーブルのデータ量も非常に多いものもあります。
過去にはサーバー数を増やすなどして、合計6台のEmbulkが稼働するサーバーを運用して、BigQueryにデータを同期しています。

Embulkは各種DBへの接続プラグインをインストールして、データ取得が可能になります。
この際、プラグインをインストール方法が2種類あります。
1. サーバーに直接インストール
2. bundleファイルへ記述してからインストール

過去の運用ではサーバーに直接プラグインをインストールしていたため、サーバー間でバージョン差異が発生していました。
結果として、開発で実行できたバッチが本番ではエラーになったり..

こういった環境差異は運用保守の障害になるので、改善すべくタスクとして取り組みました。
また当時はEmbulkの0.8.21を利用していたため、これを機にバージョンアップもしよう、ということになりました。

その過程で大量のエラー通知を流してしまったり、個人的には反省ばかりの経験になりました。
実際にどういった作業を行ったのか、終了後に振り返ってみてどうすれば良かったのか?をまとめていきます。

目次


サーバー移行対応


環境差異を解消するにあたって、2つの方針がありました。
1. 既存環境でembulkやプラグインのバージョンアップを行い、bundle配下で管理
2. 新しくサーバーを建ててもらい、そちらの環境を整備して各種バッチの移行

先ほども記載した通り、社内では1100以上のテーブルが同期されています。
依存関係を減らして修正を行うため、ansibleを利用してインフラチームに新しくサーバーを建ててもらう2つ目の方針を取りました。

移行前の事前調査


  • 利用されているプラグインの洗い出し
    embulk gem listや embulk bundle listを実行すると、同期されているプラグインを洗い出しできます。
    6台のサーバー間で、利用されているプラグイン自体やそのバージョンがバラバラであったり、不要になっているものが多々あったので、最低限必要なものをリスト化しました。

  • Embulkのバージョンアップに伴うプラグインの依存関係の洗い出し
    今回、Embulkのバージョンを0.9.23に上げる
    かつ各種プラグインは最新版を利用して検証を行いました。
    その際にお世話になった記事をいくつか紹介したいと思います。
    embulk 0.9.XをBundlerで利用している人は、Gemfileでembulk.gemのバージョンを指定しよう
    これまでは0.8系を使っていたので、新たに意識が必要な箇所でした。
    Embulkをバージョンアップする際に気をつけること
    こちらのembulk-input-mysqlのタイムゾーンのオプションの話は参考にさせていただきました。
    embulkのgem installでRuby version >= 2.4.0と出た時は
    representableの最新版は3.1.1(2021年11月現在)ですが、最新版を利用すると上記のエラーが発生しました。
    そのため、現在は古いバージョンを利用しています。

修正したGemfile

gemの部分のみを記載しています

gem 'representable', '= 3.0.4'
gem 'signet', '= 0.11'
gem 'rake', '=13.0.1'
gem 'test-unit', '=3.3.4'
gem 'embulk-decoder-commons-compress', '=0.4.2'
gem 'embulk-input-mysql', '= 0.12.2'
gem 'embulk-input-sqlserver', '= 0.10.0'
gem 'embulk-input-http', '=0.24.0'
gem 'embulk-input-s3', '=0.5.2'
gem 'embulk-input-elasticsearch', '=0.3.6'
gem 'embulk-input-salesforce_bulk', '=0.2.2'
gem 'embulk-filter-eval', '=0.1.0'
gem 'embulk-filter-column', '=0.8.1'
gem 'embulk-filter-gsub', '=0.1.0'
gem 'embulk-filter-hash', '=0.5.0'
gem 'embulk-output-bigquery', '= 0.6.6'
gem 'embulk-output-gcs', '=0.4.4'
gem 'xml-simple', '=1.1.8'
gem 'jruby-openssl', '=0.10.4'

移行前の動作検証


個人的にはここが反省ポイントなのですが、実行検証を手動で行っていました。
日次で実行されるバッチは、下記のようにDB単位でまとめており、多いもので数百以上のテーブルを順次実行します。
数が多いのでコメントアウトして、1つのファイルで1つのテーブルが更新されるのか?を検証しました。

また特定のプラグインは一部のテーブル同期に利用されていたためリポジトリを検索
→ エラーが起きるかどうか、手動コマンドで実行検証
という流れで対応しましたが、漏れが発生する要因になり非効率でした。

バッチを実行して自分宛てにエラーメールを飛ばし、そこから修正箇所を洗い出した方が楽ですし、見落としが発生する可能性も潰せます。

DB単位実行バッチの一例
#!/bin/bash
SHELL_NAME="${0%*.sh}"
LOG_FILE="${SHELL_NAME}.log"
HOST_NAME=`hostname`
MAIL_TITLE="エラーメールのタイトル"
MAIL_BODY="エラーメール本文"
MAIL_TO="エラーメールの通知先"
NUM=0

error() {
  echo "error in $embulk_table_name" 1>&2
  NUM=`expr $NUM + 1`
  echo -e "${MAIL_BODY} $embulk_table_name.yml.liquid" | mail -s "${MAIL_TITLE} ${SHELL_NAME}  失敗 (${HOST_NAME})" ${MAIL_TO}
}

run() {
  export embulk_table_name=$1
  /home/istyle/.embulk/bin/embulk run -b bundleファイルへのパス ./$1.yml.liquid --log $LOG_FILE -l debug
}

: > $LOG_FILE

run 'テーブル1' || error
run 'テーブル2' || error
・
・
exit $NUM

postfixの起動漏れ


エラーハンドリング処理を入れているバッチの実行で動作検証せず、同期処理部分のみのコマンドで確認していたため、エラーメールが飛ぶか?を確認していませんでした。
またリリース作業を行ったところ、テーブルの更新ができていないものが散見されました。
(データの更新チェックは別の検知スクリプトを実行しているため)

確認したところ、新しく建てたサーバーにmailコマンド自体はインストールされていました。
ただしpostfixが起動されていなかったため、何も考えずに起動してしまいました。
その結果..

社内のslackに流れる大量のエラー通知


検証作業の過程で溜まっていたキューが一斉に流れ、大量のエラー通知が飛んでしまいお祭り騒ぎに..

ここまで大量通知を流したのは初めてだったので肝を冷やしましたが、茶化してくれる人が居て個人的には救われたなあと思ってます..

皆さん、postfixの起動時はキューが溜まっているか?を必ず確認しましょう!!

移行後に発生したエラー対応


以下でも言及されていますが、Embulkの0.9系においてembulk-output-bigqueryを利用する際にBigQueryへのテーブル同期が成功するときと、失敗することがある、というエラーがありました。
Sometimes failed to run(can not copy embedded jar to temp directory) #1148

先ほど記載したとおり、バッチの動作検証ではテーブルを1つだけ取り込む形で検証しました。
レビュー時の動作検証では問題ないものとして判断され、そのままリリースを行いました。

この際はキューは溜まっていなかったので、テーブル単位の同期が走るたびにエラーが流れてしまいました。

もし過去に戻って対応をやり直すなら


動作検証は、本番と全く同じファイルを実行して担保をすべきでした。
言い訳に近いですが、なぜ今回の対応を選択したのか?を記載してみます。

  • 現在、EmbulkでのBQ上のテーブル更新は基本的に全上書き
    テーブルの数、データ量も鑑みると、バッチの実行が業務時間内に終わらない。
  • DBへの負荷
    既存で実行されているバッチとは別に、移行検証用のバッチを回すとします。
    すると、取得元の各種RDBに負荷が2重で掛かることになる。

以上から、一部のテーブル同期が成功するか?のみを担保して、新しいサーバーへの切り替え対応を行ってしまいました。

もし、過去に戻って対応をするなら..

  • git上のブランチで切り替える
    本番リリース後に細かいエラーが他にも出たのですが、その度に旧サーバー側でバッチが実行されるように切り戻し対応を行いました。
    環境差異の解消用のブランチを切っておけば、その負荷も減らせたなと思っています。
  • BQ上には検証用のデータセットを切る
  • 動作検証時はEmbulkでのinput側ではLIMITを追加してDBへの負荷を減らし、業務時間内でバッチの実行を完了させる。

もしかするともっと良い方法があったかもしれません。
移行手順の確認はチーム内で行ったのですが、そもそも本当にこの手順で良いんだっけ?という、そもそも論の議論はしなかったなと思っています。

メールの通知方法に関して

現在、実行しているバッチの内部でエラー検知を行っています。
そのため、複数のテーブル同期が失敗すると、その数だけエラーが通知されてしまいました。

今回の事例であれば、DB単位のテーブル同期は1つの通知にまとめて、その内部で失敗したテーブルの一覧が分かる方が好ましいです。

また1時間置きに更新を行っているテーブルも一部存在します。
1時間おきにエラーが通知されると感覚がマヒしがちなので、N時間以内に〇回こけたら通知する、というような自由度を持たせられたらと思っています。

エラーハンドリングは個人的にもまだまだ勉強が足りてない部分ですね..

最後に


少々長くなりましたが、最後までご覧いただきありがとうございました!
最初に少し書きましたが、全社分析基盤の運用保守やTableauを利用したデータの利活用を推進しています。

また、これまでオンプレで構築していたブランドオフィシャルというブランド様向けの分析ツールがあります。
こちらで扱っているデータ量が膨大であるため、クラウドに移行していくことを予定しています。
既存の全社分析基盤への統合も見据えているため、大変ではありますが面白い経験ができそうだと感じています。

また、BigQueryMLの時系列分析を利用してテーブルの件数を検知したり、データの同期時間・利用時間をSLAとして担保するなど、少しずつ改善作業も進めている状態です。

他にもやっていきたいことが多すぎるのですが、データエンジニアが足りておらず全然追いついていないので、興味がある方はぜひ一緒にやっていきましょう!

アイスタイル採用ページはこちら
データエンジニアの採用も行っております!
@cosmeの大規模データを用いたデータ分析基盤を構築するデータエンジニア

2019年4月中途入社。 主にデータ分析業務に携わってきましたが、現在は分析基盤の構築や整備を担当。日本酒好きなヤクルトファン。