センシティブ データの移行ソリューションのデプロイ

このドキュメントでは、Dataflow と Cloud Data Loss Prevention を使用して BigQuery でセンシティブ データを移行するのリファレンス パターンを実装して、リアルタイム分析のユースケースにおけるセンシティブ データの移行と処理に関する主な課題のいくつかを解決する方法について説明します。

概要

多くの組織が、オンライン サービスで利用可能な高度なデータと分析機能を使用するために、オンプレミスのデータ プラットフォームをクラウドに移行しています。たとえば、クレジット カード番号、医療識別子と履歴、車両識別番号(VIN)などの機密データを移行します。Dataflow と Cloud Data Loss Prevention を使用して BigQuery で機密データを移行するのリファレンス パターンは、このタイプの機密データを含む大規模なデータセットを移行する際に役立ちます。

この記事では、リファレンス パターンの使用について説明します。組織にパターンをデプロイする際に役立つ主要な設計と実装の詳細について説明します。

データ移行タスク

このパターンで対処される主要なデータ移行タスク:

  • ペタバイト規模のデータセットを検査して機密情報が含まれているかどうかを確認します。
  • センシティブ データを移行して匿名化します。
  • 匿名化された結果を再識別し、元のデータを必要とするダウンストリーム アプリケーションを有効にします。

データの匿名化

匿名化によりセンシティブ データが変換されるため、漏洩のリスクを軽減しながら引き続きデータを使用できます。この変換では、暗号ハッシュマスキングなどの不可逆的な方法と、確定的暗号化などの可逆的な方法の両方を使用します。

たとえば、個人を特定できる情報(PII)を公開せずに、複数の顧客アカウントに影響を与える問題を分析する方法を検討してみます。匿名化を使用すれば、このデータを取り扱う一方で、データを難読化できます。

以下の表を比較してください。

センシティブ データと匿名化されたデータの 2 つのテーブルの比較。

左側の表では、PII が表示されています。右側の匿名化表では、この機密データは不明瞭になっていますが、行 1 と行 3 の account_id 値は同じですが、メールアドレスは異なっています。また、特定の年齢を公開せずに、個人の年齢の範囲を把握することもできます。

リファレンス パターン アーキテクチャ

リファレンス パターンを使用すると、データ移行タスクの実行に使用できる自動処理パイプラインを構築できます。

次の図に、このパターン アーキテクチャを示します。

パターンで使用されているプロダクトと、それらの間の処理の流れを表すイラスト。

実装されたパイプラインの有向非巡回グラフ(DAG)は、次のようになります。

パターンの実装によって作成されるパイプラインの有向非巡回グラフ(DAG)。

データソースの指定

リファレンス パターンでは、さまざまなソースからのデータの取り込みがサポートされています。Cloud Storage にデータをインポートできます。また、コンプライアンスやコストの理由で必要な場合は、元の場所にデータを残しておくこともできます。その場合、オンプレミスか別のクラウドかにかかわらず、どこにあるデータでも処理できます。

リファレンス パターンは、Apache Beam オープンソース フレームワークを使用して実装されているため、Beam で利用可能な任意のランナーを使用してパイプラインを実行できます。さまざまな Beam I/O 変換を使用して、さまざまなデータソースを統合することもできます。これにより、Amazon S3 バケットからデータを読み込むことも、必要に応じて他のコネクタと連携するようにパターンを拡張することもできます。この柔軟なアプローチにより、最適なデータトークン化プロダクトを使用することもできます。

データファイルの処理

このリファレンス パターンでは、CSV、Avro、JSON などの複数の形式のデータを処理できます。Beam の DynamicDestinations 機能を使用して、BigQuery で適切なテーブル スキーマを自動的に作成します。大規模な CSV ファイルの処理には、Splittable DoFn を使用し、行区切り文字を使用してバイトサイズでファイルを分割します。以下のコード スニペットは、このアプローチを示しています。

@ProcessElement
public void processElement(ProcessContext c, RestrictionTracker<OffsetRange, Long> tracker)
    throws IOException {
  String fileName = c.element().getKey();
  try (SeekableByteChannel channel = getReader(c.element().getValue())) {
    FileReader reader =
        new FileReader(
            channel, tracker.currentRestriction().getFrom(), recordDelimiter.getBytes());
    while (tracker.tryClaim(reader.getStartOfNextRecord())) {
      reader.readNextRecord();
      String contents = reader.getCurrent().toStringUtf8();
      String key = String.format("%s~%d", fileName, new Random().nextInt(keyRange));
      numberOfRowsRead.inc();
      c.outputWithTimestamp(KV.of(key, contents), Instant.now());
    }
  }
}

データセットに小さなファイルが数多く含まれている場合は、ファイルのチャンク化が不要になる場合があります。このようなデータセットの場合、ContextualTextIO 変換を使用し、ファイル名や行番号などのメタデータ情報をトラッキングやレポート作成のために利用することができます以下のコード スニペットは、このアプローチを示しています。

case CSV:
  records =
      inputFiles
          .apply(
              "SplitCSVFile",
              ParDo.of(
                  new CSVFileReaderSplitDoFn(
                      options.getKeyRange(),
                      options.getRecordDelimiter(),
                      options.getSplitSize())))
          .apply(
              "ConvertToDLPRow",
              ParDo.of(new ConvertCSVRecordToDLPRow(options.getColumnDelimiter(), header))
                  .withSideInputs(header));
  break;
@ProcessElement
public void processElement(ProcessContext context) throws IOException {
  Table.Row.Builder rowBuilder = Table.Row.newBuilder();
  Row nonTokenizedRow = context.element();
  String filename = Util.sanitizeCSVFileName(
      nonTokenizedRow.getLogicalTypeValue("resourceId", ResourceId.class).getFilename());
  String input = nonTokenizedRow.getString("value");
  LOG.debug("File Name: {} Value: {}", filename, input);
  List<String> csvHeader = context.sideInput(header);

レイテンシと割り当ての管理

パイプラインで個々のレコードを取り込むと、そのレコードは、Beam の BatchRequestForDLP を使用して小さなバッチにグループ化されます。バッチサイズは、バイトを指定する構成可能なしきい値によって決まります。バッチサイズのしきい値に達すると、グループ化されたレコードが 1 回のリクエストで Cloud Data Loss Prevention API に一緒に送信されます。DLP API は送信されたバッチ内のすべてのレコードを分析し、そのバッチのすべての結果をレスポンスで返します。

一括処理を使用すると、DLP API リクエストの数を減らせるため、割り当てを維持できます。また、複数のバッチを並列処理することで、スループットを向上させ、全体的なレイテンシを短縮できます。

キー範囲パラメータの使用

レイテンシと DLP API の割り当てのバランスをとるには、--keyRange パイプライン パラメータを使用します。--keyrange 値を増やすと処理速度が上がり、レイテンシが短縮されますが、リソースの使用量も多くなるため、使用量上限により早く到達できます。

たとえば、リファレンス パターンをテストする際に、5 ワーカーのデフォルトのパイプライン設定で 200 万行、0.5 MB のバッチサイズのしきい値、100 のキー範囲を持つ 400 MB の CSV ファイルが処理されました。次のスクリーンショットは、パイプラインがプロジェクトの DLP API の割り当て (1 分あたり 10,000 回の呼び出し)の範囲内にとどまり、10 分未満でファイルを正常に処理したことを示しています。

割り当てに対するパイプラインのスループットを示すグラフ。

キーサイズのパラメータを 1,000 に増やすことで、このデータを迅速に処理しました。必要な API 呼び出しの最大数は 800(400 MB/0.5 MB = 800)でしたが、それでもプロジェクト割り当ての 1 分あたり 10,000 API 呼び出しを下回りました。以下のスクリーンショットでは、同じデータが 1 分未満で処理されたことがわかります。

キーサイズが 1,000 に増加した場合のパイプラインのスループットを示すグラフ。

このベンチマーク分析の結果は、処理されたデータと使用された Cloud DLP の構成によって異なります。特定の要件に対するパフォーマンスを微調整するために、リファレンス パターンで利用できる追加のパイプライン パラメータの調整が必要になることがあります。

バッチサイズ パラメータの使用

Cloud DLP には、infoType と呼ばれる一連の組み込み情報タイプが用意されています。DATEDATE_OF_BIRTHFIRST_NAMELAST_NAMELOCATION などの一部の infoType 検出器は、オペレーションのレイテンシに影響することがあります。レイテンシの影響を受けやすい infoType を含むリクエストを処理する際のパフォーマンスを向上させるには、--batchSize パラメータ値を小さくして、小さいバッチサイズを使用します。これにより、Cloud DLP はリクエストを分散した小さなチャンクで処理します。その代わりに、リクエストの処理に時間がかかります。

たとえば、500 KB/ペイロードのバッチサイズを使用して、レイテンシがあまり問題にならない infoType の 1 TB のデータを検査するには、約 8 分かかります。50 KB/ペイロードのバッチサイズを使用して、レイテンシの影響を受けやすい infoType で同じデータを調べる場合、約 15 分かかります。

フレックス テンプレートを使用したパイプラインの実行

リファレンス パターンでは、Dataflow Flex テンプレートを使用して、ランタイム時にパイプラインの構造を構成します。これにより、異なる目的で同じパイプラインを再利用できます。パイプラインをトリガーしてさまざまなユースケースに対応する方法の例を次に示します。

  • Amazon S3 バケットの CSV ファイルを検査するには:

    --filePattern=s3://<myBucket>/file.csv --DLPMethod=INSPECT --awsRegion=<myAWSRegion> --awsCredentialsProvider=$AWS_CRED
    
  • Cloud Storage バケット内の Avro ファイルのデータを匿名化するには:

    --filePattern=gs://<myBucket>/file.avro --DLPMethod=DEID
    
  • BigQuery のデータを再識別するには:

    --tableRef=<myProjectId>:<myDataset>.<myTable> --queryPath=gs://<myBucket>/reid_query.sql"
    

検査精度の処理

検査を行う際には、入力データと一致する結果の数を検証することで、結果の精度をモニタリングすることが重要です。ほとんどの場合、組み込みの Cloud DLP infoType では十分な精度が提供されていますが、カスタム infoType を使用して、さらに結果を改善できます。

金融サービス業界の事例は、その一例です。この業界の一部の組織では、検査時にクレジット カード番号を内部または外部に分類する必要があります。これに対処するため、正規表現(regex)を使用して、特定のクレジット カード番号のセットのパターンに基づいて一致を検出するカスタム infoType 検出器を作成できます。また、検査ルールセットを使用してコンテキスト ルールを使用することにより、組み込みとカスタムの両方の infoType 検出器をカスタマイズできます。この例では、除外ルールを使用して次のことを行えます。

  • CardType#1 や CardType#2 などの特定のカード範囲のセットについて、有効なクレジット カード番号を検査します。これを行うには、カスタムの正規表現ベースの infoType 検出器と、逆一致を用いた組み込みの CREDIT_CARD_NUMBER infoType 検出器を組み合わせて使用します。

    逆一致を実行するための検査ルールセットを表示します。

  • カスタムの正規表現ベースの infoType 検出器の重複による誤検出の数を減らします。これを行うには、組み込みの CREDIT_CARD_NUMBER infoType 検出器を使用し、完全一致を使用して、指定されたカード範囲のセットに含まれないカードを識別します。

    完全一致を実行するための検査ルールセットを表示する

Cloud DLP の高密度の検出が予想されるパイプラインでは、カスタム Counter 指標を使用して、Cloud DLP 検査コンテンツのレスポンスから切り捨てられた検出結果を特定します。以下のコード スニペットは、このアプローチを示しています。

@ProcessElement
public void processElement(
    @Element KV<String, InspectContentResponse> element, MultiOutputReceiver out) {
  String fileName = element.getKey().split("\\~")[0];
  String timeStamp = Util.getTimeStamp();

  if (element.getValue().getResult().getFindingsTruncated()) {
    numberofTimesFindingsTruncated.inc();
  }

  numberofTimesFindingsTruncated.inc();

カスタムの Counter 指標を使用して、結果が切り捨てられる頻度を確認し、必要に応じてパイプラインのバッチサイズを削減し切り捨てを低減します。

次のステップ