
Shell
イベント
該当するコンテンツが見つかりませんでした
マガジン
技術ブログ
こんにちは。SCSKの岡尾です。 今回は、AWS Glueを利用したETL処理を実装していた中でハマったポイントを紹介したいと思います。 私自身、ETLの実装は初めてでした。これからGlueを使ったETL処理実装していこうとしている方が同じようにつまずかないようにハマりポイントをご紹介できればと思います。 目次 はじめに ハマりどころ ネットワーク:Glueセキュリティグループの「自己参照」 トランザクション:Commit Failed Exception PySpark:メモリ不足エラー まとめ 1. はじめに 今回のプロジェクトでは、Amazon RDS上の業務データをS3 Tablesで構築したデータレイクへ同期するパイプラインを構築しました。 システム構成を簡略化した図が以下の通りです。ポイントとしてはRDSはVPC内のプライベートサブネットに配置されているというところです。 一見シンプルな構成ですが、実際に構築してみると思わぬ落とし穴がありました。 2.1【ハマりどころ①】 ネットワーク:Glueセキュリティグループの「自己参照」 最初のハマりどころは、GlueでのRDSへの接続設定です。 VPC内にあるRDSへGlueから接続する場合、Glue Connection(接続情報)を作成し、VPC・サブネット・セキュリティグループ(SG)を指定する必要があります。 ここで、Glue特有の要件として 自己参照ルール というものがあります。Glueジョブは、内部的にドライバーとワーカーノード間で通信を行います。この通信はVPC内に作成されたENIを経由して行われます。 そのため、 Glueに割り当てたセキュリティグループ自身が、そのセキュリティグループからの全TCP通信を許可している 必要があります。 そのため、Glueにアタッチするセキュリティグループのインバウンドルールには以下を追加する必要があります。 タイプ : すべてのTCP ポート範囲 : 0 – 65535 ソース : カスタム(自分自身のセキュリティグループID sg-xxxxxx) この設定がないと、Glueジョブの実装ができないようです。 2.2 【ハマりどころ②】トランザクション:Commit Failed Exceptionエラー 続いてのハマりどころは、icebergテーブルの書き込み競合です。 今回の実装では、Glueジョブは連携するテーブルの数だけ作成し同時に複数のジョブが起動するような構成としていました。このとき、偶発的にCommitの競合を示すような以下のエラーが発生しました。 「pyiceberg.exceptions.CommitFailedException: CommitFailedException: Request doesn’t meet the requirement condition: Requirement failed: branch main has changed: expected id …..」 調べてみると、以下の公式ブログにもある通り、共通のカタログを利用していると異なるテーブルであってもCommitFailedException が発生する仕様となっているようでした。 Manage concurrent write conflicts in Apache Iceberg on the AWS Glue Data Catalog これを回避するためには、このエラーが発生した場合にリトライ処理を実施するような実装が必要でした。異なるテーブルであれば同時にジョブ実行しても問題ないと思っていましたが、カタログが共通だと書き込みの競合が発生してしまうんですね。 2.3 【ハマりどころ③】PySpark:メモリ不足エラー 最後のハマりどころは、Glueジョブの処理性能です。 最初はコスト効率のいい Python Shell でpythonのpyicebergライブラリを利用した実装をしていました。 しかし、データ量が増え、数万行レベルになった際に、データをDataFrameとしてメモリに展開しようとした際に落ちてしまうMemoryErrorが発生 しました。 Python Shellで利用したpyicebergライブラリのupsert処理では、一度に処理できるデータの件数に制約があるようです。 そこで、 Python Shellでの戦いを諦め、分散処理が可能な Glue ETL (Spark) へ切り替えました。これにより、Sparkの分散処理により数十万件のデータも一度にupsertできるようになりました。また、 Worker Typeの選定も柔軟になり、DPUサイズも調整することで安定してデータ連携が可能になりました。 最初は小さくPythonで、将来的なデータ増加に応じてSpark構成を検討するというのがいいのではないかなと感じました。 3. まとめ 今回は、RDSのデータをGlueジョブを使ってS3 Tablesへ連携する際のハマりどころを紹介しました。 Glueはサーバレスのサービスであるために便利な側面が多い反面、そこで利用される仕組みを理解した上での実装が必要になると勉強になりました。 皆様もGlueを使う際にはぜひ参考にしてみてください!
こんにちは、SCSK林です! 今回は、AWS、Snowflakeで実現したニアリアルタイムデータ連携について解説します。 本記事では、実際に構築した例をベースにアーキテクチャ選定の背景と、構成や技術的に気をつけるポイントについて共有していきたいと思います。 構成の背景(いわゆる要件) 今回の主要件は、オンプレミスのシステムから出力される業務データを、AWSを経由してSnowflakeへ連携し、数分以内(ニアリアルタイム)に分析可能にすることでした。 主な要件と制約は以下の通りです。 セキュリティ: 秘匿性の高いデータを扱うため、インターネット経由の転送は不可。閉域網のみを通すこと。 データ特性: 1リクエストあたり約10MB(圧縮前)。頻度は1日100件程度だが、データの欠損は許されない。 クライアント制約: 送信元システムはHTTPリクエストの送出のみ対応。 既存資産: 組織内で実績のあるSnowflake連携用スクリプト(Shell)を流用したい。 アーキテクチャ概要 データ取り込み処理 : Client (On-prem) → Direct Connect → VPC Endpoint (Interface) → Amazon API Gateway (HTTP API) → AWS Lambda 一時保存 : Amazon S3 (Staging Bucket) ロード処理 : S3 Event Notification → AWS Lambda → Snowflake (COPY INTO) このアーキテクチャのポイントは、「データ受信」と「データ処理」をS3を介して完全に切り離した点にあります。 クライアントからのデータ受信を行うLambda(データ取り込み処理)は、データの検証とS3への保存のみを行い、即座にレスポンスを返します。一方、Snowflakeへのロードを行うLambda(ロード処理)は、S3イベントをトリガーに非同期で実行されます。 これにより、仮にSnowflake側の処理に時間がかかったとしても、クライアント側のHTTP通信がタイムアウトすることはありません。 アーキテクチャのポイント セキュリティ要件(閉域網)の実現 オンプレミスからのHTTPリクエストを安全に受け取るため、API Gatewayの前段にInterface VPC Endpoint (PrivateLink) を配置しました。 Private API Gatewayを使用する選択肢もありましたが、今回はネットワーク経路を厳密に制御するため、VPC Endpointのリソースポリシーを活用しました。これにより、特定のDirect Connect経由のトラフィックのみを許可し、それ以外のアクセスをネットワークレベルで遮断することを実現しています。 Lambdaの6MB制約とその回避 今回、技術的なハードルとなったのが、AWS Lambdaのペイロード制限です。 API Gatewayの制限: 最大10MB Lambda(同期呼び出し)の制限: 最大6MB クライアントから上記制限を越えるデータが送られてくると、そのままではLambdaに引き渡す段階で 413 Request Entity Too Large エラーが発生してしまいます。 これを回避するために、S3署名付きURLを発行してクライアントから直接S3へアップロードさせる方式も検討しましたが、クライアント側の実装負荷が複雑になるため、アーキテクチャは変更せず「データ圧縮」で解決する方針を決定しました。 今回は、クライアント側でデータをGZIP圧縮することで、ペイロードサイズを数MBまで削減し、これによりLambdaの6MB制限をクリアしました。ただ、そういうわけにも毎回いかないと思いますので同様の構成を検討する際はぜひご注意ください。 「取り込み」と「ロード処理」の責務の分離による耐障害性の確保 今回は、API Gatewayから直接Snowflakeへデータを流し込むのではなく、S3を境界として「Ingest(取り込み)」と「Process(ロード処理)」の責務を明確に分離しました。 データ取り込み処理層 (同期): 役割: クライアントからのリクエストを高速に受け付け、S3へ永続化することだけに集中する。 効果: Snowflake側の状態(一時的なパフォーマンス低下など)の影響をクライアントに与えない。クライアントへは即座に 200 OK を返却し、接続タイムアウトのリスクを排除。 データロード層 (非同期): 役割: S3へのオブジェクト作成イベントをトリガーに、非同期でSnowflakeへの COPY INTO を実行する。 効果: 重い処理(DB接続・ロード)をここへ集約。もしロード処理が失敗しても、データはS3上に「ファイル」として安全に残っているため、データロード層(クライアント)に影響を与えることなくリトライやリカバリが可能。 この「S3をバッファとした疎結合アーキテクチャ」を採用したことで、クライアントに対するレスポンス性能(レイテンシ)を一定に保ちつつ、バックエンド処理の安定性を高めることを実現しました。 運用を見据えた設計 データ連携基盤においてもっとも考慮が必要なことは「データのロスト」です。 今回は、万が一Snowflakeへのロードが失敗した場合(データフォーマット不正やウェアハウスの一時的な問題など)に備え、以下の仕組みを導入しました。 エラーハンドリング : Lambda内で例外をキャッチした場合、対象のオブジェクトをS3上の「Error」フォルダへ移動(Move)。 監視 : エラーフォルダへの配置をトリガーに、管理者へ即時通知。 これにより、失敗したデータが「どこにあるか分からない」状態を防ぎ、リカバリが必要なデータを明確に分離する運用を設計しました。 まとめ 今回の構成では、マネージドサービスベースのデータ連携基盤(ニアリアルタイム)を実現しました。 データ連携は頻度をあげることでより難易度が増していきます。 今回の構成、事例がどなたかのお役に立つと幸いです。
Amazon Relational Database Service(以下、RDS)や Amazon Aurora(以下、Aurora)のリザーブドインスタンス(RI)は、オンデマンド料金と比較して大幅なコスト削減が可能です。しかし、RDS の RI には Amazon EC2 の RI とは異なり開始日時を指定した予約購入の機能がなく、購入 API を実行した時点で即座に課金が開始されます。そのため、大量の RI を短時間で正確に購入するには手動オペレーションでは負荷が高く、お客様にとって購入のハードルが高い状況となっています。 なお、第 7 世代以降のインスタンスで利用可能になった Database Savings Plans では予約購入が可能ですが、旧世代のインスタンスを利用されている場合は引き続き RI での購入が必要です。 本記事では、公共機関における RI 購入の制約を例に、RDS / Aurora の RI を効率的に一括購入するサンプルスクリプトをご紹介します。公共機関以外のお客様でも、大量の RI を正確に購入したいケースで参考にしていただけます。 背景 公共機関ではデータ保管場所や予算執行の都合により購入方法が限定されることが一般的です。例えば、次のような制約があります。 日本リージョン(東京・大阪)のみ 前払いなし(No Upfront)のみ利用可能 購入タイミングは 4/1 09:00:00〜09:59:59(JST) ※会計年度を1時間でもまたがないようにする場合 この制約のもとでは、限られた時間内に正確な購入を完了する必要があり、事後に間違いに気づいても購入を止めることが出来ません。そして、多数のアカウントが同じ時間帯に一斉に RI を購入する必要があることを考えると、手動オペレーションでは品質・工数の両面で課題があります。 この課題に対応するため、AWS CloudShell 上で動作する RDS RI 一括購入のサンプルスクリプトを用意しました。AWS CloudShell はブラウザからアクセスできるシェル環境で、AWS CLI や jq(JSON 処理ツール)などの主要ツールがプリインストールされているため、実行環境の準備を別途行う必要がありません。AWS マネジメントコンソールにログインできれば、すぐにスクリプトを実行できます。 前提条件 AWS CloudShell 環境(AWS CLI、jq がプリインストール済みのため、追加のセットアップは不要) IAM ロールに以下の権限が付与されていること rds:DescribeReservedDBInstancesOfferings rds:PurchaseReservedDBInstancesOffering rds:DescribeDBInstances sts:GetCallerIdentity 1アカウントあたり同一リージョンで保有可能なRIはデフォルト40件まで( 参考 )。超過する場合は事前にサービスクォータの引き上げを申請してください (参考)AWS CloudShell VPC environment を利用する場合 CloudShell VPC environment ではシェルが指定した VPC のサブネット内で動作するため、スクリプトが呼び出す AWS API(RDS、STS)へのネットワーク到達性を確保する必要があります。具体的には、以下のいずれかを構成してください。 AWS PrivateLink(VPC エンドポイント)による RDS API および STS API へのアクセス NAT Gateway を経由したインターネットアクセス(ご利用環境のシステム要件やセキュリティポリシーを確認のうえ実施してください) ネットワーク構成が不十分な場合、API 呼び出しがタイムアウトしスクリプトが正常に動作しません。 ※ 本記事執筆時点では、デジタル庁が提供するガバメントクラウド環境で AWS CloudShell を利用するには CloudShell VPC environment の構成が必要です。 (参考)CloudShellを使わずローカル端末から実行する場合 AWS CloudShell を使用せず、お手元のローカル端末から実行することも可能です。その場合、以下の点にご注意ください。 シェルスクリプト実行環境が必要です。Linux や macOS ではデフォルトシェルである bash や zsh が利用できます。Windows の場合は WSL(Windows Subsystem for Linux)上で実行してください。 AWS CLI(バージョン2推奨)および jq を事前にインストールしてください。 AWS 認証情報の設定が必要です。aws configure 、IAM Identity Center(aws sso login)、 aws login 等で、スクリプト実行前に認証情報を構成してください。 スクリプトはタイムゾーン Asia/Tokyo を使用して購入タイミングの判定を行います。OS のタイムゾーンデータが正しくインストールされていることを確認してください。 Linux:“cat /etc/timezone” 等 macOS:“sudo systemsetup -gettimezone” 等 準備 入力ファイル(input.csv)を準備してください(フォーマットは後述)。次に、rds-ri-launchpad.sh を CloudShell へアップロード、または GitHub から直接取得し、実行権限を付与してください。 GitHub から直接取得する場合の例 curl -O https://raw.githubusercontent.com/aws-samples/sample-rds-ri-bulk-purchase-script/main/rds-ri-launchpad.sh chmod +x rds-ri-launchpad.sh スクリプトの特徴 スクリプトは「ドライラン(検証)モード」と「購入モード」の 2 段階で動作します。ドライランモードでは実際の購入を行わず、入力内容の検証のみを実施します。 ドライランモード(デフォルト) ./rds-ri-launchpad.sh input.csv 前述のとおり、RDS の RI には開始日時を指定した予約購入の機能がありません。そのため、本スクリプトではドライランモードを用意し、購入直前までの検証を事前に実施できるようにしています。以下のチェックを実施できます。 日本リージョン(ap-northeast-1 / ap-northeast-3)のみであること 前払いなし(No Upfront)のみであること インスタンスクラス形式の妥当性(db.xxx.xxx 形式) エンジン名の許可リスト検証 DescribeReservedDBInstancesOfferings API による Offering(購入可能な RI の条件と価格の組み合わせ)の存在確認 実際に稼働中のインスタンスとの突合(購入数量が稼働数を超過していないかの警告) 購入は一切実行されないため、実際のRI購入日である 4/1 より前に安全に事前確認が可能です。 購入モード ./rds-ri-launchpad.sh --purchase input.csv --purchase オプションを指定することで、実際の購入を実行します。購入時にはドライラン時のチェック項目に加えて、以下の制御が行われます。 購入タイミングの確認 — 4/1 09:00:00〜09:59:59(JST)の範囲外の場合はアラートを発行し、続行するかの確認プロンプトを表示 最終確認プロンプト — 購入実行前に yes/no の確認を要求 入力ファイル CSV 形式で購入情報を定義します。1 行目のヘッダー行はスクリプトが自動的にスキップします。 こちらはサンプルファイルです。 region,db_type,instance_class,engine,multi_az,quantity,duration,payment_option ap-northeast-1,RDS,db.t3.medium,mysql,yes,2,1,No Upfront ap-northeast-1,Aurora,db.r5.large,aurora-mysql,no,3,1,No Upfront フィールド 説明 許容値 region リージョン ap-northeast-1, ap-northeast-3 db_type DB 種別 RDS, Aurora instance_class インスタンスクラス db.xxx.xxx 形式 engine エンジン aurora-mysql, aurora-postgresql, mysql, postgresql, mariadb, oracle-se2, sqlserver-ex multi_az Multi-AZ yes, no(Aurora の場合は無視) quantity 購入数量 1 以上の整数 duration 期間(年) 1, 3 payment_option 支払いオプション No Upfront(他の文字列の場合はエラー) セキュリティ上の考慮 基本的なパストラバーサル対策 — 入力ファイルパスに .. が含まれる場合は即座に終了 入力値の許可リスト検証 — エンジン名、リージョン、支払いオプションを許可リストで検証 ロックディレクトリ機構 — 同時実行を防止し、二重購入を回避 結果ファイルのパーミッション制限 — chmod 600 で所有者のみ読み取り可能 エラー出力のサニタイズ — アカウント ID や ARN をマスクして表示 set -euo pipefail — 未定義変数やパイプエラーを即座に検出 エラーハンドリング スクリプトは「行単位の継続処理」を採用しています。ある行でバリデーションエラーや Offering 取得失敗が発生した場合でも、該当行をスキップして次の行の処理を継続します。AWS 認証エラーなど回復不能なエラーの場合のみスクリプトを終了します。 実行結果は ri_purchase_result_YYYYMMDD_HHMMSS.txt に自動出力され、成功・失敗・スキップの件数と各行の詳細が記録されます。一部の処理が失敗した場合は、 失敗した行のみを記載した新しいCSVファイルを作成し、再実行することで手動リトライが可能です。 本スクリプトは1回の実行あたり10〜100行程度の入力を想定しています。大量の入力(100行超)を処理する場合、AWS APIのスロットリングが発生する可能性があります。その場合はCSVファイルを分割し、複数回に分けて実行してください 。 ダウンロード サンプルスクリプトは以下リンクよりダウンロード可能です。 sample-rds-ri-bulk-purchase-script まとめ 本スクリプトは、限られた購入時間枠の中で大量の RDS / Aurora の RI を安全かつ効率的に一括購入するためのサンプルです。ドライランモードで事前に購入内容を検証し、購入モードで実行するという 2 段階のアプローチにより、オペレーションミスのリスクを低減します。 免責事項 本記事で紹介するスクリプトはサンプルコードであり、AWS サポートの対象外です。本番環境での使用前に、必ずテスト環境で十分な動作確認を行ってください。 本ブログや添付資料の内容はできる限り正確な情報を提供するように努めておりますが、正確性や安全性を保証するものではありません。本ブログや添付資料はあくまで一例であり、すべての作業内容を充足するものではありません。本ブログや添付資料は AWS サービスの変更・追加などにより今後修正される場合があります。本ブログや添付資料の利用によって生じた損害等の責任は利用者が負うものとし、アマゾン ウェブ サービス ジャパン合同会社は一切の責任を負いません。上記ご了承の上、ご利用ください。 筆者について Hidenori Hiroki 廣木 秀典(Hidenori Hiroki)は、公共部門担当のTechnical Account Manager(TAM)。ポストフェーズにおけるお客様の課題解決をご支援しています。コスト最適化やセキュリティ設計など、お客様のクラウド活用を日々サポートしています。 Kazuki Fujimoto 藤本 一樹(Kazuki Fujimoto)は、パブリックセクターの自治体担当のソリューションアーキテクトです。自治体の基幹システムのクラウド移行やそれに伴うコスト最適化とモダナイゼーション、生成 AI の活用支援などをおこなっています。












