🚚

OPAのデプロイアーキテクチャ例

2021/12/15に公開

この記事はOPA/Regoアドベントカレンダーの15日目です。

OPAは様々な利用形態がありますが、複数のサービスなどから1つのポリシー群を参照したいなどのケースでは実行用の環境をクラウドなどへデプロイする場合があります。

今回はOPAをクラウド環境などで実際に利用する際、どのようなデプロイのパターンがあるかについて解説したいと思います。今回は特定のプロダクトに依存せず、汎用的なアーキテクチャを考えています。筆者の経験からAWSをベースに紹介したいと思います。

パターン1) ポリシーを実行用のアセットに同梱する

ポリシーデータを実行用のアセット(実行のためのランタイムやコード、バイナリなどを含むアーカイブ)にそのまま入れ込んでしまう方法です。今回はデプロイ先にAWS LambdaやAmazon ECS (Elastic Container Service)を想定しています。

ECSの場合は非常にシンプルで、コンテナイメージをビルドする際にポリシーファイルをコピーすれば問題ありません。実行用のバイナリなども公式から提供されているコンテナイメージをもとに自分たちの環境へデプロイするためのイメージを容易に作成することができます。

LambdaはCDK(Cloud Development Kit)など利用することで、Lambda用のアセットを作成する際にコードやバイナリだけではなく任意のファイルを入れ込むことができます。また、OPA + Lambdaでデプロイする場合はopaのバイナリを直接使うのはやや難しく[1]、Go言語でOPAのランタイムを組み込んだ自前のバイナリを用意するケースが多いと思われます。その場合はバージョン1.16から導入されたembedパッケージを利用することでも同梱させることができます。

メリット

  • バージョン管理が容易かつ明確: デプロイするアセットとポリシーが密に紐付いているため、現在どのバージョンのポリシーが使われているかということが明確にわかります。また、何らかの問題がおきて切り戻しをする場合も、アセットのバージョンを指定してデプロイが完了した時点で明確にポリシーが切り替わります。トラブルシュートをするときにも、構造がシンプルになっていることで無用な混乱を抑制できることが期待されます。

デメリット

  • ポリシーのデプロイに時間がかかる: ポリシーを同梱する場合、基本的には実装そのもののCI/CDパイプラインで処理されることになるため、相対的にデプロイにかかる時間は長くなりがちです。ポリシーのちょっとした変更でも適用しようとしてから数分から10分以上かかるというような構成も考えられ、心理的負荷があがる側面はありそうです。
  • 実装とポリシーの変更に関する権限の分離ができない: 実装とポリシーが一定分離されているとはいえ、デプロイの過程でサービスの実行に関する部分とポリシーそのものに関わる部分の両方に影響が及ぶ可能性があります。この構成のメリットの一つはバージョン管理にあるため、たとえばGitなどのバージョン管理リポジトリでは1つのリポジトリに実装とポリシーをまとめて扱うようになるでしょう。工夫の仕方はあると思いますが、サービスを提供する責任を持つメンバーとポリシーを管理する責任を持つメンバーが別れている場合に、きれいに変更権限を分離するのはやや難しくなると考えられます。

パターン2) ポリシーを外部ストレージに配置する

もう一つのパターンとして実行環境の外部ストレージに保管する方法があります。AWSの場合はS3に配置するのが一般的かと思います。

OPAにはポリシーやデータをひとまとめにしたアーカイブを bundle として扱う機能が備わっています。CLIで指定したディレクトリのポリシーやデータを1つのファイルに纏めたり、bundle ファイルを読み込むことで含まれるポリシーやデータを一括して読み込むことができます。外部ストレージに配置する場合、細切れのファイルの取扱が面倒&扱いがシンプルになることから bundle ファイルを使うことを想定します。

S3からファイルを読み込む場合、環境ごとにアプローチが異なります。

  • ECS(fargate含む): OPAのバイナリを直接使える場合、bundleファイルの置き場としてS3を指定することができます。この機能は定期的[2]にS3バケットをpollingし、変更があった場合に自動的にポリシーを更新します。
  • Lambda: 前述したとおりLambdaではOPAのバイナリを直接利用するのがやや難しく、Go言語でランタイムを自分で組み込むことになります。その都合上、OPAコマンドの恩恵を受けられないためS3からダウンロード&Regoに取り込む部分を自前で実装しないといけないケースが多いと考えられます。

メリット

  • ポリシーのデプロイ作業自体は早い: 同梱するパターンに比べ、こちらはポリシーをアーカイブしS3などへコピーするだけで完了するため比較的早くデプロイが完了するというメリットがあります。
  • 権限の分離が可能: LambdaやECSに対して実装をデプロイするロールと、S3にポリシーをアップロードするIAMロールをそれぞれ用意することで、サービス提供とポリシー管理をするメンバー(ないしワークフロー)の権限を完全に分離できます。これによってポリシーの変更による影響が極小化[3]され、事故を防ぎやすくなります。特にOPAがセキュリティのポリシーを扱う場合はサービス側のメンバーにも書き込み権限を渡さないことでより限られたメンバーだけでのポリシーを管理するということも可能になります。

デメリット

  • ポリシーのバージョン管理がやや難しい: S3にデプロイされたポリシーとLambdaやECS上の実装のバージョンが必ずしも同期しなくなるという弊害があります。
    • ポリシーの更新タイミングが読みにくい: Lambdaは比較的短期に実行環境が切り替わりますが、ECSでOPAバイナリを動かす場合は更新タイミングが非同期的であり、いつ更新されるのかがわかりません。
    • Lambda・ECS側とのバージョンの食い違いが起きる:ポリシーや実装の更新に非互換性がある場合、慎重にデプロイのタイミングを見計らう必要があります。また、revertする必要がでてきた際にも実装とポリシーのバージョンを噛み合わせる必要があります。特にトラブル対応の際などに混乱が発生しがちなため、このあたりのコストをのむ心構えが必要になります。

Lambda v.s. ECS

やや一般論に近くなりますが、OPA運用の視点からLambdaとECSのどちらを選択するかを検討する際のポイントを列挙してみたいと思います。LambdaはGoで自前の実装を用意する前提です。

検討事項 Lambda ECS
サービス料金 呼び出し回数が少ない(例えば1日数千程度以下)であれば低コスト 常に起動しているため定額のコストがかかる
パフォーマンス S3を使う場合のみ起動するたびにダウンロードが発生するので比較的不利 起動にかかるコストがない分、連続したリクエストに対してはやや有利
スケールアウト 特になにも設定しなくても一定までスケールする CloudWatch alarmなどの設定が必要&スケールアウトのトリガなどを検討する必要がある
実装コスト Goでの実装が必要なため比較的高い OPAの公式コンテナイメージなどを利用できるため、比較的低い
HTTPからの利用 API gatewayを使って可[4]
各種AWSサービスからの呼び出し[5] 不可
脚注
  1. 詳しい理由や実装方法については後日改めて別の記事で解説します ↩︎

  2. デフォルトで最短60秒、最大120秒に設定されています ↩︎

  3. 例えば組み合わせ処理が爆発して評価の処理が劇的に重くなるようなポリシーも記述は可能なので、必ずしもサービスの可用性が担保されるわけではありません ↩︎

  4. ただしもとのOPAのAPIスキーマを再現するには作り込みが必要 ↩︎

  5. 例えばLambda@EdgeやKinesis Data Firehoseからの呼び出しなどLambdaを直接呼び出すようなパターン ↩︎

Discussion