DataZone本番運用3ヶ月|メタデータ管理の地獄から脱出した実体験

AWSのDataZoneを導入して3ヶ月。メタデータ管理の落とし穴と、ようやく機能し始めた運用パターンを実体験ベースで紹介します。

DataZone導入のきっかけ|データの「どこにあるか問題」が深刻化した

先日、うちのチームで3ヶ月ぶりにDataZoneの振り返りをしたんですけど、正直に言うと「導入して良かった」と「これはしんどい」がめっちゃ混在してるんですよね。

背景としては、去年の終わり頃から会社全体のデータソースが増えすぎてて。S3、RDS、Redshift、Athena、SageMakerパイプライン…あちこちにデータが散在してるし、誰がどのテーブルを持ってるのか、どのデータセットが信頼できるのかみたいな情報が完全に喪失状態だったんです。

Analyticsチーム、DataEngineeringチーム、MLチームが別々に管理してて、Slackで「このデータ使えますか?」ってメッセージがひっきりなしに来てた。月30時間くらい無駄な問い合わせ対応してたと思う。

そこで「DataZone入れてメタデータ管理を一元化しよう」って話になったわけです。CollibraやAlationも検討したんですけど、AWS環境前提だしコスト面でもDataZoneが現実的だったんですよね。

DataZone構成を実装して見えた現実

graph TB
    subgraph "AWS Organization"
        subgraph "Data Account"
            RDS["RDS Aurora"]
            S3[("S3 Data Lake")]
            Redshift["Redshift Cluster"]
            Athena["Athena"]
        end
        
        subgraph "Analytics Account"
            DZ["DataZone<br/>Project"]
            DZCatalog["Asset Catalog"]
            DZAssets["Business Assets"]
        end
        
        subgraph "Governance"
            LakeFormation["Lake Formation"]
            Glue["Glue Catalog"]
        end
    end
    
    RDS -->|metadata| DZ
    S3 -->|metadata| DZ
    Redshift -->|metadata| DZ
    Athena -->|metadata| DZ
    
    DZ --> DZCatalog
    DZCatalog --> DZAssets
    
    Glue -.->|sync| DZCatalog
    LakeFormation -.->|integrate| DZ
    
    style DZ fill:#FF9900
    style DZCatalog fill:#FF9900
    style DZAssets fill:#FF9900

実装を始めたのは3月初旬で、まずDataZoneのプロジェクトを立ち上げて、各チームのドメイン(Business Domain)を切ったんです。うちの場合、Analytics Domain、Platform Domain、ML Domainって3つ。

DataSourceの登録は思ったより簡単でした。RDS、Redshift、S3あたりはコンソールからサクサク繋げられる。ただし、Glue Catalogとの連携はちょっと工夫が必要だった。

# DataZoneのメタデータを自動取得するLambda
import boto3
import json
from datetime import datetime

datazone_client = boto3.client('datazone')
glue_client = boto3.client('glue')

def sync_glue_metadata():
    # Glueカタログからテーブル情報を取得
    databases = glue_client.get_databases()
    
    for db in databases['DatabaseList']:
        tables = glue_client.get_tables(
            DatabaseName=db['Name']
        )
        
        for table in tables['TableList']:
            # DataZoneのAssetして登録
            asset_data = {
                'name': table['Name'],
                'description': table.get('Description', ''),
                'typeIdentifier': 'arn:aws:datazone:ap-northeast-1::asset-type:glue-table',
                'owningProjectIdentifier': 'your-project-id',
                'domainIdentifier': 'your-domain-id',
                'metadata': {
                    'database': db['Name'],
                    'columns': json.dumps([{
                        'name': col['Name'],
                        'type': col['Type']
                    } for col in table['StorageDescriptor']['Columns']])
                }
            }
            
            try:
                response = datazone_client.create_asset(
                    **asset_data
                )
                print(f"Created asset: {table['Name']}")
            except Exception as e:
                print(f"Error: {e}")

if __name__ == '__main__':
    sync_glue_metadata()

このLambdaを毎日走らせてGlueカタログとの同期を自動化したんですけど、ここで最初にハマったのが「メタデータの鮮度」だったんですよ。

最初の3週間で直面した地獄|データオーナーシップと品質管理

正直、ここが一番しんどかったですね。DataZoneを導入したはいいけど、「誰がこのテーブルの所有者か」「このデータはいつ最後に更新されたのか」みたいな基本情報が全然登録されてない状態だった。

Asset登録時にメタデータとして所有者情報とか更新日時を必須にしようとしたんですけど、各チームから「そこまで細かく管理できない」ってフィードバックが来てしまって。最初の設計が甘かったんです。

だから最初の3週間は、DataZoneのProject Lead(うちの場合はデータエンジニアリング部長)が手作業で各Assetのメタデータ修正して回ってました。マジで月20時間くらい浪費した。

そこで2週目くらいに気づいたのが、DataZoneのAsset Profileってやつです。これを使うと、各Assetに対して品質スコア、タグ、説明を後付けできるんですよね。

# DataZoneのAsset Profileを自動更新するやつ
def update_asset_profile(asset_id, quality_score, owner_info):
    try:
        response = datazone_client.update_asset(
            identifier=asset_id,
            domainIdentifier='your-domain-id',
            metadata={
                'quality_score': quality_score,
                'owner_team': owner_info['team'],
                'owner_email': owner_info['email'],
                'last_updated': datetime.now().isoformat(),
                'sla_status': 'active' if quality_score >= 80 else 'review',
                'sensitivity_level': 'public' if quality_score >= 90 else 'internal'
            }
        )
        return True
    except Exception as e:
        print(f"Profile update failed: {e}")
        return False

これとGlueの統計情報を組み合わせて、「最後に更新されたのが90日以上前のテーブル」とか「レコード数が0のテーブル」みたいなのを自動検出することにしたんです。

2ヶ月目で実感した、データディスカバリーの威力と落とし穴

DataZoneのUI、正直めっちゃ良くなってます。2026年時点だと、検索機能が日本語に対応して、キーワード検索するだけで関連するテーブルとか列とかが一緒に出てくるんですよね。

2月下旬にアナリティクスチームが「売上関連のデータセット全部見たい」ってリクエストを出したんですけど、DataZone使ったら3分で該当する7個のテーブルが全部検索できちゃった。それまではSlackで「だいたい◯◯担当者に聞いてくれ」みたいな運用だったんで、めっちゃ助かったんですよ。

ただし、ここで困ったのが「検索精度のばらつき」だった。DataZoneはメタデータのタグとか説明文を機械学習で解析して検索結果を優先順位付けするんですけど、タグが不統一だとこれが機能しないんですよね。

例えば「Customer」「Customer ID」「顧客」「cust_id」みたいな感じで、同じカラムなのに複数の表記ゆれがあったんです。うちのGlueテーブルが数百個あって、全部手直しするわけにはいかないし…。

そこで2月末に「タグ統一化プロジェクト」を始めたんですけど、これが長い。DataZone上で業務用語(Business Glossary)を定義して、GlueカラムとマッピングするTermベースのアプローチに切り替えたんです。

# DataZoneのBusiness Glossaryを自動構築
def create_business_glossary():
    # 重要なドメイン用語を一元定義
    glossary_terms = [
        {
            'name': 'Customer ID',
            'shortDescription': '顧客を一意に識別するID',
            'classifications': ['customer', 'key-column'],
            'relatedTerms': ['Customer Name', 'Customer Email']
        },
        {
            'name': 'Order Amount',
            'shortDescription': '注文金額(税別)',
            'classifications': ['financial', 'monetary'],
            'unit': 'JPY'
        },
        {
            'name': 'Product Category',
            'shortDescription': '商品カテゴリ',
            'classifications': ['dimension'],
            'allowedValues': ['Electronics', 'Clothing', 'Books', 'Other']
        }
    ]
    
    for term in glossary_terms:
        try:
            datazone_client.create_glossary_term(
                domainIdentifier='your-domain-id',
                glossaryIdentifier='your-glossary-id',
                **term
            )
        except Exception as e:
            print(f"Glossary term creation failed: {e}")

3月第1週にこれを導入したら、検索精度が一気に上がりました。「Customer」で検索すると、Customer IDだけじゃなくて関連する全部のカラムが出てくるようになったんですよね。チーム全体のデータディスカバリー効率が30%上がったって自己計測してます。

3ヶ月目で衝撃を受けた|データガバナンスとアクセス制御の現実

DataZoneはメタデータ管理だけじゃなくて、データアクセスのガバナンスもできるんです。Asset単位でアクセスリクエストを承認・却下するフロー、Datalineageの可視化、データ利用者の追跡とか。これがめっちゃ便利なんですけど、同時に面倒くさい部分も出てきた。

うちの場合、Redshift上の顧客データテーブルにアクセス権限を持ってないユーザーが「このテーブル使いたい」ってDataZone経由でリクエストを出すと、データオーナーに通知が来る仕組みにしたんです。

# DataZoneのアクセスリクエストを自動レビュー
def handle_asset_access_request(request_id, asset_id, requester):
    try:
        # リクエスト情報を取得
        request_info = datazone_client.get_subscription_request_details(
            identifier=request_id,
            domainIdentifier='your-domain-id'
        )
        
        # リスク評価(簡易版)
        risk_score = calculate_risk(
            asset_id=asset_id,
            requester_team=requester['team'],
            data_sensitivity=request_info['asset']['sensitivity']
        )
        
        # リスク高い場合は追加レビューフロー
        if risk_score > 70:
            # セキュリティチームに自動エスカレーション
            send_slack_notification(
                channel='#data-security',
                message=f"High-risk data access request: {asset_id} by {requester['name']}",
                risk_score=risk_score
            )
        
        # リスク低い場合は自動承認
        elif risk_score < 30:
            datazone_client.accept_subscription_request(
                identifier=request_id,
                domainIdentifier='your-domain-id'
            )
        
        # 中程度の場合はオーナーに委譲
        else:
            notify_asset_owner(
                asset_id=asset_id,
                request_id=request_id,
                requester=requester
            )
    except Exception as e:
        print(f"Access request handling failed: {e}")

やってみたら、毎日10件くらいのアクセスリクエストが来るようになってしまって(それまで無かったから気づかなかった需要があった)、各チームのデータオーナーが「レビュー対応だけで時間がない」って悲鳴を上げてました。

解決策として、リスク評価を自動化したら、自動承認できるリクエストが全体の65%になったんです。残りの35%は人間レビューが必要なんですけど、これでオーナーの負担を1/3くらいまで減らせた。

同時に気づいたのが、「DataZoneのアクセス制御」と「Lake FormationのData Access Control」を組み合わせる必要があるってこと。DataZoneは「誰がどのデータにアクセスしたいのか」の管理で、実際のIAM権限はLake Formationで制御しないと機能しないんですよね。

うちは最終的にこのパターンにしました:

  1. DataZone側:アクセスリクエストの受け付けと承認フロー
  2. Lake Formation側:実際のIAM権限付与とセッション管理
  3. EventBridge連携:DataZoneの承認イベントをLake Formationのタスクにして自動化
# EventBridgeでDataZone承認をLake Formationに自動反映
def eventbridge_rule_handler(event, context):
    """
    DataZoneの承認イベント -> Lake Formation権限付与
    """
    detail = event['detail']
    
    if detail['eventType'] == 'SubscriptionRequestAccepted':
        # Lake Formationの権限を自動付与
        lakeformation_client = boto3.client('lakeformation')
        
        principal_arn = f"arn:aws:iam::{detail['requester_account']}:user/{detail['requester_id']}"
        resource_arn = f"arn:aws:s3:::{detail['s3_bucket']}/{detail['s3_prefix']}"
        
        try:
            lakeformation_client.grant_permissions(
                CatalogId=detail['account_id'],
                Principal={
                    'DataLakePrincipalIdentifier': principal_arn
                },
                Resource={
                    'DataLocationResource': {
                        'Arn': resource_arn
                    }
                },
                Permissions=['DATA_LOCATION_ACCESS'],
                PermissionsWithGrantOption=['DATA_LOCATION_ACCESS']
            )
            
            # タイムアウト設定(90日のアクセス権限)
            set_expiration_timer(
                principal_arn=principal_arn,
                resource_arn=resource_arn,
                days=90
            )
            
            return {'statusCode': 200, 'body': 'Permission granted'}
        except Exception as e:
            print(f"Lake Formation grant failed: {e}")
            return {'statusCode': 500, 'body': str(e)}

これで「DataZoneでリクエスト承認されたら、自動的にLake Formationで権限が付与される」って流れになったんです。めっちゃスムーズになりました。

データリネージの可視化|ようやく全体像が見えた瞬間

3月中旬くらいから、DataZoneのDatalineage機能を本格的に使い始めたんですけど、これが「ああ、これをやりたかったんだ」って感じで感動しました。

S3のRaw Data → GlueでETL → Redshiftの分析テーブル → QuickSightのダッシュボード、みたいな一連のパイプラインが1画面で可視化されるんですよね。

うちの場合、Glueパイプライン50個くらい走ってるんですけど、「このテーブルって最終的にどこで使われてるのか」「このダッシュボードはどのデータが元になってるのか」みたいなのが簡単に追跡できるようになった。

特に有難かったのが「この列が削除されたら、どのダッシュボードが壊れるのか」を事前に把握できるようになったこと。過去にRedshiftのカラムを削除したら、QuickSight側でいくつかダッシュボードが真っ白になってしまったことがあるんですけど、今はそういうミスが防げます。

graph LR
    S3Raw["S3<br/>raw-data"] -->|Glue Job| GlueETL["Glue ETL<br/>transform"]
    
    GlueETL -->|writes| S3Curated["S3<br/>curated-layer"]
    
    S3Curated -->|COPY| Redshift["Redshift<br/>analytics_db"]
    
    Redshift -->|query| QuickSight["QuickSight<br/>dashboards"]
    
    style S3Raw fill:#569297
    style GlueETL fill:#FF9900
    style S3Curated fill:#569297
    style Redshift fill:#527FFF
    style QuickSight fill:#759C3E

ただし、ここで注意点が1つあります。DataZone側がGlueやLambdaのコード変更を「自動的には」検出できないんです。メタデータベースだから、パイプラインコードを修正しても、DataZoneが知るのはコード修正を「誰かが登録した時」なんですよね。

# CodePipeline完了時にDataZoneのLinageを自動更新
def update_datazone_lineage_on_deploy(event, context):
    """
    CodePipelineが新しいGlueコードをデプロイしたら、
    DataZoneのLineageも自動更新
    """
    codepipeline = boto3.client('codepipeline')
    datazone = boto3.client('datazone')
    glue = boto3.client('glue')
    
    # CodePipelineの成功を確認
    if event['detail']['state'] == 'SUCCEEDED':
        job_name = event['detail']['additional-artifacts'][0]['name']
        
        # 該当するGlueジョブの入出力を取得
        glue_job = glue.get_job(Name=job_name)
        
        # DataZoneのLineageを更新
        try:
            # 新しいLineage情報を構築
            lineage_data = extract_glue_lineage(glue_job)
            
            # DataZoneに登録
            datazone.put_data_lineage_events(
                domainIdentifier='your-domain-id',
                dataLineageEvents=lineage_data
            )
            
            print(f"Lineage updated for {job_name}")
        except Exception as e:
            print(f"Lineage update failed: {e}")

これをCodePipelineのEventBridge通知と組み合わせてオートメーションしたので、デプロイがラグなくDataZoneに反映されるようになりました。

正直なところ|DataZone導入で得たこと・失ったこと

3ヶ月使ってみて、素直に「導入して良かった」という結論です。ただし、完全な解決策ではないっていうのも実感してます。

得たこと:

  • データディスカバリー時間が3時間→15分に短縮。月30時間の無駄問い合わせが90%削減
  • データリネージの可視化で、パイプライン修正の影響範囲が事前把握できるように
  • アクセス制御の一元化で、セキュリティ申請〜承認のリードタイムが1週間→1日に短縮
  • Business Glossary統一で、チーム間のデータ用語の齟齬が消えた

失ったこと(コスト・運用負荷):

  • DataZoneのライセンス費用。うちの規模(ユーザー200名、テーブル500個)だと月8万円くらい
  • メタデータ品質管理の運用負荷。自動化してもやっぱり人間レビューが30%必要
  • アクセスリクエスト処理の工数。月50件の承認・却下判定が発生。ただし自動承認で65%削減済み

使って気づいたベストプラクティス:

  1. 最初からメタデータの品質基準を決める → 後付けは地獄。タグ統一、説明文の必須化とか最初に強制すべき

  2. Business Glossaryは最初の1ヶ月が勝負 → 業界用語、社内用語を徹底的に定義する。後からの修正は地獄

  3. Lake FormationやSecrets Managerと組み合わせて初めて機能する → DataZone単体では「メタデータカタログ」に過ぎない。権限管理と合わせて初めて”データガバナンス”

  4. アクセスリクエストの自動承認ルールを最初に設計 → 「リスク低い、かつ社内チーム間のアクセス」みたいな条件で自動承認。運用負荷激減

  5. Lineage自動更新を絶対やる → パイプライン修正→DataZone手動更新のフローは100%忘れられる。CodePipelineと連携一択

比較:他のデータカタログとの立場

うちが検討した3つを本当にざっくり比較すると:

項目DataZoneCollibraAlation
AWS連携ネイティブプラグインプラグイン
初期導入期間1-2ヶ月3-4ヶ月3-4ヶ月
月額費用8-15万円50万円+60万円+
メタデータ自動抽出限定的良好良好
アクセス制御統合Lake Formation対応別途実装別途実装
UI/UXの学習コスト低い中程度中程度
日本語対応2026年版で対応していないしていない

正直なところ、AWS完全内製なら迷わずDataZone。マルチクラウド環境なら Collibra/Alation。というのが2026年時点の正解だと思う。

まとめ

DataZone導入3ヶ月で、うちのデータガバナンスは劇的に改善されました。特に以下の3点が大きい:

  1. ディスカバリー効率:データを探す時間が月30時間削減。検索精度はBusiness Glossary統一がカギ
  2. アクセス制御の一元化:Lake Formationと組み合わせて、セキュリティと利便性を両立
  3. リネージ可視化:パイプライン影響範囲の事前把握で、本番事故が減った

ただし、「メタデータ品質の継続管理」「アクセスリクエスト処理」みたいに、削減できない運用負荷がある。それでも月30時間の削減効果を考えると、十分にROI出てますね。

次のステップとしては、SageMaker Pipeline や Lambda のlineageも自動検出させたいんですけど、その辺は2026年後半のロードマップに含まれてるみたいなので、期待して待ってます。DataZone本気で使い込むなら、AWS Glue・Lake Formation・Secrets Manager との「セット導入」は絶対です。

誰か同じような課題抱えてたら、是非試してみてください。前払いで後悔するより、月額払いで段階的に改善する方が心理的にも実装的にもいいと思いますよ。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事