技術部データ基盤チームの@tosh2230です。
2023/04/11(火)に、ファインディ株式会社主催のLTとパネルで学ぶ データ基盤アーキテクチャトレンド 2023にてChange Data Capture(CDC)の事例を紹介しました。関係者の皆様に感謝を申し上げます。
この記事では、発表した内容と質疑応答への回答、その後の動向についてお伝えします。
発表内容
ニアリアルタイム分析の実現に向けた取り組みの概要と、本番稼働したばかりのCDCデータパイプラインの詳細についてご紹介しました。
CDCを行うツールとして、今回はDebezium Serverを選びました。Debezium Serverは日本国内では事例が少ないのですが、コンテナで軽量にCDCを実現できる良い手段だと思います。
質疑応答
発表後にいただいた質問への回答を記載します。当日うまく答えられた自信はありませんが、下記の内容をお伝えしたかった次第です。
Airbyteを採用しなかったのはなぜですか?
ペパボのデータ基盤『Bigfoot』におけるAirbyteの本番運用でご紹介したように、Zendesk Talkのデータ抽出のためにAirbyteを使っています。今回もAirbyteが候補として挙がりました。Airbyteは多機能でありGUIもついていて大変便利なのですが、見方を変えると、今回の主目的であるCDC以外の機能がついてくるという言い方もできます。
この観点でDebezium Serverを見ると、必要な十分な機能が備わっておりコンテナで軽量に動かせることが魅力的でした。今回はデプロイ先がAWSであるという点も踏まえて、まずはDebezium Serverを第一候補として進めて、不測の事態が起きた場合にはAirbyteに変更する、という方針で進めました。
EC2のインスタンスタイプはなにを使っていますか?
1つの事業DBに対し、4vCPU,8-16GiBのインスタンスを使用しています。データベースでの変更分を対象に送信するため、当初想定していたよりもリソースの使用量は少なく済んでいます。ただ、これは一般的に言えることではなく、データベースを使用しているアプリケーションの性質に依存すると思います。
時間の都合で割愛したトピック
発表時間が10分でしたので、本当は話したかったトピックを泣く泣く削りました。このブログ記事の本当の目的は、それらを公開することにあります。
環境変数経由での設定の上書き
今回作成したAWSのリソース群は、AWS CloudFormationで管理しています。
Debezium Serverはapplication.propertiesファイルで読み込み先と書き込み先に関する設定をしますが、これは環境変数を経由して設定を上書きできます。
以下は、AWS::ECS::TaskDefinition ContainerDefinitions.Environment
のコード例です。デプロイ先の環境ごとに設定を変更したり、データベースやGoogle Cloudサービスアカウントの認証情報をAWS Secrets Managerから取得して、環境変数を設定しています。
Environment:
- Name: DEBEZIUM_SOURCE_DATABASE_SERVER_ID
Value: !FindInMap [ Database, ServerId, !Ref Env ]
- Name: DEBEZIUM_SOURCE_TOPIC_PREFIX
Value: !FindInMap [ TopicPrefix, Name, !Ref Env ]
- Name: DEBEZIUM_SOURCE_DATABASE_INCLUDE_LIST
Value: !FindInMap [ Database, Name, !Ref Env ]
- Name: DEBEZIUM_SOURCE_TABLE_INCLUDE_LIST
Value:
!Sub
- |
${DatabaseName}.table_a,
${DatabaseName}.table_b,
${DatabaseName}.table_c,
- DatabaseName: !FindInMap [ Database, Name, !Ref Env ]
- Name: GOOGLE_APPLICATION_CREDENTIALS
Value: /debezium/credentials.json
Secrets:
- Name: DEBEZIUM_SOURCE_DATABASE_USER
ValueFrom:
!Sub
- "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretArnSuffixRds}:username::"
- SecretArnSuffixRds: !FindInMap [ SecretArnSuffix, Rds, !Ref Env ]
- Name: DEBEZIUM_SOURCE_DATABASE_PASSWORD
ValueFrom:
!Sub
- "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretArnSuffixRds}:password::"
- SecretArnSuffixRds: !FindInMap [ SecretArnSuffix, Rds, !Ref Env ]
- Name: DEBEZIUM_SOURCE_DATABASE_HOSTNAME
ValueFrom:
!Sub
- "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretArnSuffixRds}:host::"
- SecretArnSuffixRds: !FindInMap [ SecretArnSuffix, Rds, !Ref Env ]
- Name: DEBEZIUM_SOURCE_DATABASE_PORT
ValueFrom:
!Sub
- "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretArnSuffixRds}:port::"
- SecretArnSuffixRds: !FindInMap [ SecretArnSuffix, Rds, !Ref Env ]
- Name: GOOGLE_APPLICATION_CREDENTIALS_VALUE
ValueFrom:
!Sub
- "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretArnSuffixGcp}"
- SecretArnSuffixGcp: !FindInMap [ SecretArnSuffix, Gcp, !Ref Env ]
デプロイ
Debezium Serverは、いまのところPod1台で動かしています。複数のPodが起動すると同一の変更メッセージがPub/Subへ送信されてしまうため、設定変更を行いたい場合には、実行中のPodを完全に停止してから新しいPodを起動しなければなりません。
この課題への対応として、ECS ServiceのDeploymentConfigurationの設定値を変更しました。MaximumPercentを100、MinimumHealthyPercentを0にすることで、デプロイ時にPodの停止と起動が自動的に行われるようにしています。
コンピューティングリソース
コスト最適化を目的に、コンピューティングリソースはAWS FargateではなくAmazon EC2を選択しました。EC2はインスタンスの管理が大変なイメージがありましたが、Amazon ECS-optimized AMIを使うことで構築や運用の手間を少なくできました。
下記のように、CloudFormationでSSM Parameterを参照することで、最新のAMIを使うようにしています。
LatestECSOptimizedAMI:
Description: AMI ID
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id
その後の動向
発表後も、大きな問題は起きずに安定した運用ができています。 また、テーブルの設定を追加するだけで必要なリソースが生成されるようにIaCを実装したため、処理対象テーブルの追加がスムーズで、チーム一同、大変喜んでおります。
ひとつ、Debezium Server特有の事象として対応したIssueがありますのでご紹介します。tombstone イベントの送信停止です。
あるときから、Debezium ServerがSEVEREレベルで下記のエラーログを出力するようになりました。
io.grpc.StatusRuntimeException: INVALID_ARGUMENT: One or more messages in the publish request is empty. Each message must contain either non-empty data, or at least one attribute.
調査した結果、これはtombstoneイベントの送信に失敗している事象であることがわかりました。 データベース(今回の場合はMySQL)においてDELETE操作が行われた場合、Debeziumはdeleteイベントのあとにtombstoneイベントを送信します。これは宛先がKafkaであることを前提にした仕様なので、宛先がKafka以外であれば送信されないのが妥当です。
tombstones.on.deleteをfalse
にすることで、上記のエラーを解消できました。
今後の展開
先日公開された記事、データエンジニアリングが今熱い2002夏で紹介されていたように、データベースのレコードのみならず、ユーザー行動ログもストリームで処理できる構成に変更しています。 2023年は変革の年です。即応性のあるデータ活用サービスを社内へ提供するため、スピード感をもって新しいチャレンジをどんどんしていきたいと思います。