はじめに
前回は、HDFS上のrawログとApache Icebergを、Apache OzoneのS3 Gatewayを使ったローカルS3へ移行した。
今回はその続きとして、Ozone上のrawログとIcebergをAWSへ移行する。ただし、これまでの記事で作成済みのS3 Raw backupとS3 Icebergがあるため、本seriesの標準手順では再コピーせず、その既存データを引き継ぐ。
既存バックアップを引き継ぐ(標準手順)
source /etc/rawlog-s3-backup/common.env
export AWS_RAW_BUCKET="${S3_BUCKET_NAME}"
export AWS_RAW_PROFILE=home-rawlog-backup
source /etc/iceberg/aws.env
export AWS_ICEBERG_BUCKET="${ICEBERG_BUCKET}"
export AWS_ICEBERG_PROFILE=home-aws
aws sts get-caller-identity --profile "${AWS_RAW_PROFILE}"
aws s3api head-bucket --bucket "${AWS_RAW_BUCKET}" --profile "${AWS_RAW_PROFILE}"
aws s3 ls "s3://${AWS_RAW_BUCKET}/raw/" --profile "${AWS_RAW_PROFILE}"
aws sts get-caller-identity --profile "${AWS_ICEBERG_PROFILE}"
aws s3api head-bucket --bucket "${AWS_ICEBERG_BUCKET}" --profile "${AWS_ICEBERG_PROFILE}"
aws glue get-tables --database-name "${GLUE_DATABASE}" \
--region "${AWS_REGION}" --profile "${AWS_ICEBERG_PROFILE}"
この経路では、後述のbucket作成、rcloneによるRaw再コピー、AWS側Iceberg tableの再作成、過去データのINSERT INTO ... SELECTを省略する。既存Rawのobject数・sizeと、既存Icebergの件数・時刻範囲をOzone側と比較し、差がないことだけを確認する。
以降のコピー手順は、既存AWS backupを利用できず、Ozoneから別の検証用AWS環境へ移す場合だけ実施する代替手順である。
- raw JSON TextはOzoneからAmazon S3へobject単位でコピーする
- IcebergはAWS Glue Data CatalogとAmazon S3上に新規テーブルを作り、Spark SQLで論理コピーする
- Ozoneをすぐに削除せず、AWS側との比較とrollbackができる状態で切り替える
前提
以下は構築済みとする。
| 項目 | 値 |
|---|---|
| Ozone S3 Gateway | http://ozone1:9878 |
| Ozone raw bucket | log-raw |
| Ozone Iceberg bucket | lakehouse |
| Ozone Spark catalog | ozone_s3 |
| Ozone namespace | logs_s3 |
| Spark | 3.5.8 |
| Apache Iceberg | 1.10.1 |
| 移行対象 |
syslog_iceberg、authlog_iceberg
|
| AWS region | ap-northeast-1 |
| 操作ホスト | ope1 |
Ozone側の既存テーブルは次の通りである。
ozone_s3.logs_s3.syslog_iceberg
ozone_s3.logs_s3.authlog_iceberg
AWS側には次の構成を作る。
Amazon S3 raw bucket
Amazon S3 Iceberg warehouse
AWS Glue Data Catalog
Amazon Athena
移行方針
Apache Ozoneへの移行が完了したログ基盤を、AWS S3とAWS Glue Data Catalogへ移行する。rawとIcebergでは内部構造が異なるため、移行方法を分ける。
rawはobject単位でコピーできる。一方、Ozone上のIceberg warehouseをAWS S3へそのままコピーすると、metadata内にs3a://lakehouse/...が残る可能性がある。このため、AWS側に新しいIcebergテーブルを作成し、Sparkで論理コピーする。
代替手順: 別の検証用AWS環境へ移行する
ここから「AWSへ切り替える」までの節は、既存バックアップを使う標準経路では実施しない。
AWS側の変数を設定する
bucket名はAWS全体で一意になる値へ変更する。
export AWS_REGION='ap-northeast-1'
export AWS_PROFILE='home-aws-admin'
export AWS_RAW_BUCKET='<YOUR_UNIQUE_RAW_BUCKET>'
export AWS_ICEBERG_BUCKET='<YOUR_UNIQUE_ICEBERG_BUCKET>'
export GLUE_DATABASE='logs'
AWS CLIの認証を確認する。
aws sts get-caller-identity \
--profile "${AWS_PROFILE}" \
--region "${AWS_REGION}"
Spark実行ユーザーには、少なくとも対象S3 prefixへの読み書き、Glue Database/Tableの作成・取得・更新権限が必要である。長期access keyをshellへ直接記載せず、可能であればIAM Roleまたは短期credentialを使用する。
AWS S3 bucketを作成する
東京regionに2つのbucketを作成する。
aws s3api create-bucket \
--bucket "${AWS_RAW_BUCKET}" \
--region "${AWS_REGION}" \
--create-bucket-configuration LocationConstraint="${AWS_REGION}" \
--object-ownership BucketOwnerEnforced \
--profile "${AWS_PROFILE}"
aws s3api create-bucket \
--bucket "${AWS_ICEBERG_BUCKET}" \
--region "${AWS_REGION}" \
--create-bucket-configuration LocationConstraint="${AWS_REGION}" \
--object-ownership BucketOwnerEnforced \
--profile "${AWS_PROFILE}"
public accessをblockし、default encryptionとversioningを有効にする。
for bucket in "${AWS_RAW_BUCKET}" "${AWS_ICEBERG_BUCKET}"; do
aws s3api put-public-access-block \
--bucket "${bucket}" \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true \
--profile "${AWS_PROFILE}"
aws s3api put-bucket-encryption \
--bucket "${bucket}" \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}' \
--profile "${AWS_PROFILE}"
aws s3api put-bucket-versioning \
--bucket "${bucket}" \
--versioning-configuration Status=Enabled \
--profile "${AWS_PROFILE}"
done
KMS customer managed keyを使う場合は、bucket encryptionだけでなくSpark実行RoleへKMS権限も付与する。
rcloneへ2つのS3 remoteを登録する
AWS CLIの--endpoint-urlはcommand全体に1つだけ指定するため、OzoneとAWSという異なるendpoint間の直接コピーにはrcloneを使用する。
sudo apt update
sudo apt install -y rclone
rclone version
rclone config
対話画面で次の2つを作成する。
| remote名 | type / provider | endpoint | region | path style |
|---|---|---|---|---|
ozone |
S3 / Other | http://ozone1:9878 |
us-east-1 |
有効 |
aws |
S3 / AWS | 空欄 | ap-northeast-1 |
無効 |
ozoneにはOzone S3 credentialを設定する。awsはIAM Roleまたは既存のAWS profileを使う。rclone configにはsecretが含まれるため、権限を確認する。
rclone config file
chmod 600 "$(rclone config file | tail -n 1)"
rclone lsd ozone:
rclone lsd aws:
systemdやsparkユーザーから実行する場合は、それぞれが読める専用configを作る。個人ユーザーのconfigを全ユーザーから読める権限に変更しない。
rawを1日分コピーする
最初にsyslogとauthlogを1日分コピーする。syncではなくcopyを使い、AWS側の余分なobjectを自動削除しない。
rclone copy \
ozone:log-raw/syslog/2026/09/15 \
"aws:${AWS_RAW_BUCKET}/syslog/2026/09/15" \
--transfers 8 \
--checkers 16 \
--progress
rclone copy \
ozone:log-raw/authlog/2026/09/15 \
"aws:${AWS_RAW_BUCKET}/authlog/2026/09/15" \
--transfers 8 \
--checkers 16 \
--progress
Ozoneから一度操作ホストへdownloadし、そこからAWSへuploadされるため、操作ホストのnetwork帯域とAWS data transferを事前に確認する。
rawの移行結果を確認する
異なるS3実装間ではmultipart uploadのETagが一致しないことがあるため、ETagだけを同一性判定に使わない。まずobject数と合計sizeを確認する。
rclone size ozone:log-raw/syslog/2026/09/15
rclone size "aws:${AWS_RAW_BUCKET}/syslog/2026/09/15"
rclone size ozone:log-raw/authlog/2026/09/15
rclone size "aws:${AWS_RAW_BUCKET}/authlog/2026/09/15"
さらに、sourceに存在するobjectがtargetに同じsizeで存在することを確認する。
rclone check \
ozone:log-raw/syslog/2026/09/15 \
"aws:${AWS_RAW_BUCKET}/syslog/2026/09/15" \
--one-way \
--size-only
rclone check \
ozone:log-raw/authlog/2026/09/15 \
"aws:${AWS_RAW_BUCKET}/authlog/2026/09/15" \
--one-way \
--size-only
最終確認では、両方からsample objectを取得してSHA-256を比較するか、Sparkでraw record数とJSON parse後の集計値を比較する。
全期間はprefix単位でコピーする。
rclone copy \
ozone:log-raw/syslog \
"aws:${AWS_RAW_BUCKET}/syslog" \
--transfers 8 \
--checkers 16 \
--log-file "${HOME}/rclone-ozone-to-aws-syslog.log" \
--log-level INFO
rclone copy \
ozone:log-raw/authlog \
"aws:${AWS_RAW_BUCKET}/authlog" \
--transfers 8 \
--checkers 16 \
--log-file "${HOME}/rclone-ozone-to-aws-authlog.log" \
--log-level INFO
初回コピー後、cutover直前に同じcopyを再実行して増分を追従する。
AWS Glue CatalogをSparkへ追加する
Iceberg 1.10.1に合わせたiceberg-aws-bundleをSparkのclasspathへ追加する。
sudo install -d -m 755 /opt/spark/extra-jars
sudo curl -fLo /opt/spark/extra-jars/iceberg-aws-bundle-1.10.1.jar \
https://repo1.maven.org/maven2/org/apache/iceberg/iceberg-aws-bundle/1.10.1/iceberg-aws-bundle-1.10.1.jar
jar tf /opt/spark/extra-jars/iceberg-aws-bundle-1.10.1.jar \
| grep 'org/apache/iceberg/aws/glue/GlueCatalog.class'
Spark起動optionへglue_prodを追加する。既存のhive_prodとozone_s3は残す。
--jars /opt/spark/extra-jars/iceberg-aws-bundle-1.10.1.jar
--conf spark.sql.catalog.glue_prod=org.apache.iceberg.spark.SparkCatalog
--conf spark.sql.catalog.glue_prod.catalog-impl=org.apache.iceberg.aws.glue.GlueCatalog
--conf spark.sql.catalog.glue_prod.io-impl=org.apache.iceberg.aws.s3.S3FileIO
--conf spark.sql.catalog.glue_prod.warehouse=s3://<YOUR_UNIQUE_ICEBERG_BUCKET>/warehouse
--conf spark.sql.catalog.glue_prod.client.region=ap-northeast-1
実際の起動scriptでは<YOUR_UNIQUE_ICEBERG_BUCKET>を${AWS_ICEBERG_BUCKET}の値へ置き換える。AWS SDKが参照するcredentialをdriverとexecutorの両方へ渡す。local mode以外では、executor nodeからOzone S3 GatewayとAWS S3の両方へ接続できることも確認する。
AWS側Icebergテーブルを作成する
Glue namespaceと2つのテーブルを作成する。schema、partition spec、table propertyはOzone側のSHOW CREATE TABLEを確認して合わせる。
sudo -u spark /usr/local/bin/spark-sql-iceberg-aws <<EOF
CREATE NAMESPACE IF NOT EXISTS glue_prod.${GLUE_DATABASE};
CREATE TABLE IF NOT EXISTS glue_prod.${GLUE_DATABASE}.syslog_iceberg (
host string,
ts timestamp_ntz,
severity int,
program string,
msg string,
dt date,
hr int
)
USING iceberg
PARTITIONED BY (days(ts), host)
LOCATION 's3://${AWS_ICEBERG_BUCKET}/warehouse/${GLUE_DATABASE}/syslog_iceberg'
TBLPROPERTIES (
'format-version'='2',
'write.parquet.compression-codec'='zstd'
);
CREATE TABLE IF NOT EXISTS glue_prod.${GLUE_DATABASE}.authlog_iceberg (
host string,
ts timestamp_ntz,
severity int,
program string,
msg string,
dt date,
hr int
)
USING iceberg
PARTITIONED BY (days(ts), host)
LOCATION 's3://${AWS_ICEBERG_BUCKET}/warehouse/${GLUE_DATABASE}/authlog_iceberg'
TBLPROPERTIES (
'format-version'='2',
'write.parquet.compression-codec'='zstd'
);
EOF
Icebergを1日分移行する
Spark SQLからOzone側とAWS側の両方を参照し、1日分を論理コピーする。
sudo -u spark /usr/local/bin/spark-sql-iceberg-aws <<EOF
SET spark.sql.session.timeZone=Asia/Tokyo;
DELETE FROM glue_prod.${GLUE_DATABASE}.syslog_iceberg
WHERE dt = DATE '2026-09-15';
INSERT INTO glue_prod.${GLUE_DATABASE}.syslog_iceberg
SELECT host, ts, severity, program, msg, dt, hr
FROM ozone_s3.logs_s3.syslog_iceberg
WHERE dt = DATE '2026-09-15';
DELETE FROM glue_prod.${GLUE_DATABASE}.authlog_iceberg
WHERE dt = DATE '2026-09-15';
INSERT INTO glue_prod.${GLUE_DATABASE}.authlog_iceberg
SELECT host, ts, severity, program, msg, dt, hr
FROM ozone_s3.logs_s3.authlog_iceberg
WHERE dt = DATE '2026-09-15';
EOF
全期間はdt単位で古い日から実行する。一度に全件を投入せず、失敗した日だけ再実行できるようにする。
OzoneとAWSのIcebergを比較する
sudo -u spark /usr/local/bin/spark-sql-iceberg-aws <<EOF
SELECT 'ozone' AS src, COUNT(*) AS cnt, MIN(ts) AS min_ts, MAX(ts) AS max_ts
FROM ozone_s3.logs_s3.syslog_iceberg
WHERE dt = DATE '2026-09-15'
UNION ALL
SELECT 'aws' AS src, COUNT(*) AS cnt, MIN(ts) AS min_ts, MAX(ts) AS max_ts
FROM glue_prod.${GLUE_DATABASE}.syslog_iceberg
WHERE dt = DATE '2026-09-15';
EOF
authlogも同じ方法で確認する。さらにhost、program、severity、dt、hr単位でEXCEPTを双方向に実行し、差分が0行になることを確認する。
AWS S3上にdata fileとmetadataが生成されたことも確認する。
aws s3 ls \
"s3://${AWS_ICEBERG_BUCKET}/warehouse/${GLUE_DATABASE}/syslog_iceberg/" \
--recursive \
--profile "${AWS_PROFILE}"
AWSへ切り替える
移行期間中はOzoneを正本としてAWSへ追従させる。
導入は次の順で行う。
- Ozoneへの新規書き込みを停止する
- rawの
rclone copyを再実行する - 最終日までのIceberg論理コピーを実行する
- rawのobject数・sizeとIcebergの集計差分を確認する
- Trino、Athena、日次batchの参照先をAWS側へ変更する
- AWS側だけに新規データが入ることを確認する
- rollback期間中はOzoneをread-onlyで保持する
AWS移行後に問題が起きた場合は参照先をOzoneへ戻す。AWS側だけに追加された期間がある場合は、その期間をOzone側へ論理コピーしてから書き込み先を戻す。
TrinoをAWS S3 Icebergへ接続する
Trinoの設定変更は必要か
必要である。
Ozone側ではHive MetastoreとOzone S3 Gatewayを使用したが、AWS側では次へ変更する。
| 項目 | Ozone側 | AWS側 |
|---|---|---|
| Iceberg catalog | Hive Metastore | AWS Glue Data Catalog |
| data storage | Ozone S3 Gateway | Amazon S3 |
| endpoint | http://ozone1:9878 |
AWS S3 endpoint |
| 認証 | Ozone access key | AWS IAM credential |
| path style | 有効 | 通常は無効 |
既存のiceberg_ozoneを上書きせず、AWS確認用にiceberg_aws catalogを追加する。
iceberg_ozone = Ozone Iceberg、rollback用
iceberg_aws = AWS Glue + S3 Iceberg、移行先
以下はTrino 476の3 node clusterを前提とする。設定はCoordinatorと全Workerへ同じ内容を配置する。
AWS credentialをsystemdへ渡す
bare metalの検証環境でaccess keyを使用する例である。productionではIAM Roles Anywhere、EC2 instance profileなどの短期credentialを優先する。
sudo tee /etc/trino/aws.env >/dev/null <<'EOF'
AWS_REGION=ap-northeast-1
AWS_DEFAULT_REGION=ap-northeast-1
AWS_ACCESS_KEY_ID=<TRINO_AWS_ACCESS_KEY_ID>
AWS_SECRET_ACCESS_KEY=<TRINO_AWS_SECRET_ACCESS_KEY>
EOF
sudo chown root:trino /etc/trino/aws.env
sudo chmod 640 /etc/trino/aws.env
temporary credentialを使用する場合はAWS_SESSION_TOKENも追加する。各nodeのTrino systemd unitへ設定する。
sudo systemctl edit trino
[Service]
EnvironmentFile=/etc/trino/aws.env
catalog fileやGitへaccess keyとsecret keyを直接記載しない。
AWS用Iceberg catalogを追加する
各Trino nodeに/opt/trino/etc/catalog/iceberg_aws.propertiesを作成する。
sudo tee /opt/trino/etc/catalog/iceberg_aws.properties >/dev/null <<'EOF'
connector.name=iceberg
iceberg.catalog.type=glue
hive.metastore.glue.region=ap-northeast-1
fs.native-s3.enabled=true
s3.region=ap-northeast-1
iceberg.file-format=PARQUET
iceberg.compression-codec=ZSTD
EOF
sudo chown trino:trino /opt/trino/etc/catalog/iceberg_aws.properties
sudo chmod 640 /opt/trino/etc/catalog/iceberg_aws.properties
同一AWS accountのGlueを使う場合、iceberg.catalog.type=glueとregionを指定する。cross-accountの場合はGlue catalog ID、IAM role、trust policyも別途設定する。
Trino 476ではfs.native-s3.enabled=trueを使用する。新しいTrinoではfs.s3.enabled=trueへproperty名が変更されているため、upgrade時はcatalog fileも更新する。
IAM権限を確認する
Trino用IAM principalには、読み取りだけなら少なくとも次が必要である。
-
glue:GetDatabase、glue:GetDatabases -
glue:GetTable、glue:GetTables、glue:GetTableVersion、glue:GetTableVersions - 対象bucketの
s3:ListBucket - Iceberg warehouse prefixの
s3:GetObject
TrinoからINSERT、DELETE、OPTIMIZEなども実行する場合は、GlueのCreate/Update/Delete系とS3 Put/Deleteも追加する。まずread-onlyで接続確認し、必要な操作だけ権限を追加する。
全Trino nodeへ配布して再起動する
iceberg_aws.properties、/etc/trino/aws.env、systemd drop-inをtrino1、trino2、trino3へ配置する。
sudo systemctl daemon-reload
sudo systemctl restart trino
sudo systemctl status trino --no-pager
sudo journalctl -u trino -n 200 --no-pager
Coordinatorだけに設定すると、query planningは成功してもWorkerのS3 readで失敗する。全WorkerからGlue APIとS3のHTTPS endpointへ接続できることを確認する。
TrinoからAWS側を確認する
SHOW CATALOGS;
SHOW SCHEMAS FROM iceberg_aws;
SHOW TABLES FROM iceberg_aws.logs;
SELECT
dt,
COUNT(*) AS cnt,
MIN(ts) AS min_ts,
MAX(ts) AS max_ts
FROM iceberg_aws.logs.syslog_iceberg
GROUP BY dt
ORDER BY dt DESC
LIMIT 7;
authlogも確認する。
SELECT dt, COUNT(*) AS cnt
FROM iceberg_aws.logs.authlog_iceberg
GROUP BY dt
ORDER BY dt DESC
LIMIT 7;
Flinkから直接書き込むrealtime tableを作成した場合は、同じcatalogから確認できる。
SELECT dt, hr, COUNT(*) AS cnt
FROM iceberg_aws.logs.syslog_events
GROUP BY dt, hr
ORDER BY dt DESC, hr DESC
LIMIT 24;
snapshotも確認する。
SELECT committed_at, snapshot_id, operation
FROM iceberg_aws.logs."syslog_iceberg$snapshots"
ORDER BY committed_at DESC
LIMIT 10;
Ozone側とAWS側を比較する
SELECT 'ozone' AS src, COUNT(*) AS cnt
FROM iceberg_ozone.logs_s3.syslog_iceberg
WHERE dt = DATE '2026-09-15'
UNION ALL
SELECT 'aws' AS src, COUNT(*) AS cnt
FROM iceberg_aws.logs.syslog_iceberg
WHERE dt = DATE '2026-09-15';
件数だけでなくMIN(ts)、MAX(ts)、host/program/severity/hr別の分布も比較する。
GrafanaをAWS側へ切り替える
接続先のTrino Coordinatorは変更せず、SQL内のcatalogとschemaを変更する。
- FROM iceberg_ozone.logs_s3.syslog_iceberg
+ FROM iceberg_aws.logs.syslog_iceberg
- FROM iceberg_ozone.logs_s3.authlog_iceberg
+ FROM iceberg_aws.logs.authlog_iceberg
dashboardを複製し、Ozone版とAWS版を同じ期間で比較してから本番dashboardを切り替える。AWS S3を自宅Trinoから読む場合は、internet回線のlatency、data transfer、Athenaとの役割分担も確認する。
ZeppelinからAWS S3 Icebergを参照する
ZeppelinもS3移行後のIcebergを参照できる。ZeppelinからS3へ直接接続するのではなく、既存と同様にTrino JDBCを経由する。
この構成では、AWS credential、Glue接続設定、S3 filesystem設定を持つのはTrinoである。通常のJDBC通信では、Zeppelin側にAWS access keyやhadoop-awsを追加する必要はない。ZeppelinからはTrino Coordinatorへのnetwork到達性とTrino JDBC Driverが必要になる。
Trinoのspooling protocolを有効にしている場合、JDBC clientがspooled resultをobject storageから取得するため、Zeppelin hostからspooling用storageへのnetwork到達性と認証も必要になる。Iceberg data bucketへの直接アクセスとは分けて設計する。
AWS用Interpreterを追加する
既存のHDFS/Ozone用Interpreterを上書きせず、ZeppelinのWeb UIからAWS確認用Interpreterを追加する。
Interpreter → Create New
Interpreter Name : trino_aws
Interpreter Group : jdbc
propertiesは次のように設定する。
default.driver=io.trino.jdbc.TrinoDriver
default.url=jdbc:trino://trino1:8080/iceberg_aws/logs
default.user=zeppelin
default.validationQuery=SELECT 1
default.testOnBorrow=true
TrinoでTLSを有効にしている場合は、実際のHTTPS portと証明書検証方式に合わせる。
default.url=jdbc:trino://trino1:8443/iceberg_aws/logs?SSL=true
password認証、JWT、OAuth 2.0などを有効にしている環境では、Trinoの認証方式に対応するJDBC propertyを追加する。passwordやtokenをNotebook本文へ書かず、ZeppelinのCredential機能または保護されたInterpreter設定を使用する。
DependenciesのArtifactには、Trino clusterと同じversionのJDBC Driverを設定する。
io.trino:trino-jdbc:476
JARを手動配置する場合は、Zeppelin hostで次のように配置する。
cd /tmp
curl -fLO https://repo1.maven.org/maven2/io/trino/trino-jdbc/476/trino-jdbc-476.jar
sudo install -o zeppelin -g zeppelin -m 0644 \
trino-jdbc-476.jar \
/opt/zeppelin/lib/trino-jdbc-476.jar
sudo systemctl restart zeppelin
Artifact設定と手動配置を重複して行う必要はない。利用中のZeppelinのdependency解決方式に合わせて、どちらか一方を選ぶ。
Zeppelinから接続を確認する
Notebookでtrino_aws Interpreterを指定し、default catalogとschemaを確認する。
%trino_aws
SELECT current_catalog, current_schema;
想定結果は次である。
iceberg_aws | logs
table一覧と日別件数を確認する。
%trino_aws
SHOW TABLES;
SELECT dt, COUNT(*) AS cnt
FROM syslog_iceberg
WHERE dt >= current_date - INTERVAL '7' DAY
GROUP BY dt
ORDER BY dt;
JDBC URLでiceberg_aws/logsをdefaultにしているため、syslog_icebergのようにtable名だけでも参照できる。移行比較ではcatalogとschemaを明示する。
%trino_aws
SELECT 'ozone' AS src, COUNT(*) AS cnt
FROM iceberg_ozone.logs_s3.syslog_iceberg
WHERE dt = current_date - INTERVAL '1' DAY
UNION ALL
SELECT 'aws' AS src, COUNT(*) AS cnt
FROM iceberg_aws.logs.syslog_iceberg
WHERE dt = current_date - INTERVAL '1' DAY;
Flinkが作成したrealtime tableも同じInterpreterから参照できる。
%trino_aws
SELECT dt, hr, COUNT(*) AS cnt
FROM iceberg_aws.logs.syslog_events
GROUP BY dt, hr
ORDER BY dt DESC, hr DESC
LIMIT 48;
Zeppelin側でAWS S3を参照できない場合
次の順で切り分ける。
- Zeppelin hostからTrino Coordinatorへ接続できるか
- JDBC DriverとTrino clusterのversionが一致しているか
-
default.urlのcatalogがiceberg_aws、schemaがlogsになっているか - Trino CLIから同じSQLを実行できるか
- Trino全WorkerにGlue/S3設定とAWS credentialがあるか
- Trinoのaccess controlでZeppelin userに
iceberg_aws.logsのSELECT権限があるか
ZeppelinからTrinoへ接続できるのにS3 objectの読み取りで失敗する場合、問題はZeppelinではなくTrino Worker側のIAM、region、S3 filesystem設定である可能性が高い。
Zeppelinのrollback
AWS用Interpreterを別名で追加しているため、問題がある場合はNotebookの先頭を旧Interpreterへ戻す。
-%trino_aws
+%trino_ozone
旧InterpreterとOzone/HDFS catalogは、AWS側の比較と観察期間が終わるまで削除しない。
Trinoのrollback
AWS側で問題が発生した場合はSQLの参照先をiceberg_ozone.logs_s3へ戻す。catalogを別名で残しているため、Trino設定を再度書き換えなくても参照先を切り戻せる。
通常時 : iceberg_aws.logs.*
rollback : iceberg_ozone.logs_s3.*
まとめ
ローカルS3からAWS S3への移行では、rawとIcebergを同じ方法でコピーしない。
rawはobject数、合計size、sample checksumを確認する。Icebergは日付単位で論理コピーし、件数、時刻範囲、集計差分を確認する。cutover直前に増分を追従し、一定期間Ozoneをread-onlyで保持することでrollback経路も残せる。
Iceberg warehouseの物理コピーではなく、AWS側でdata file、manifest、metadataを再生成することが重要である。
TrinoではOzone用catalogを上書きせず、Glue Catalogとnative S3 filesystemを使用するiceberg_awsを追加した。GrafanaやZeppelinはCoordinatorを変更せず、SQLのcatalog名を切り替える。
参考資料
- rclone: S3 Storage Providers
- AWS CLI: create-bucket
- AWS CLI: s3 sync
- Apache Ozone S3 Gateway
- Trino: Iceberg connector
- Trino: Metastores
- Trino: S3 file system support
- Trino: JDBC driver
- Apache Zeppelin: Generic JDBC Interpreter
関連記事
- 【導入編】HDFSログ分析基盤をAWS S3へ段階移行する全8編の構成を紹介する
- 【第2編】HDFSのrawログとApache IcebergをローカルS3へ移行してみる
- 【第4編】Apache FlinkからAWS S3上のApache Icebergへリアルタイムに書き込んでみる
- 自宅HDFS上のApache IcebergをAWS S3へ複製してAthenaで分析する構成を考える【設計編】
実機検証について
本記事の手順は、執筆時点では実機環境での一連の動作検証をまだ完了していない。掲載している設定値、command、IAM権限、service名、package構成は設計例を含むため、利用するApache Ozone、AWS S3、AWS Glue、Spark、Iceberg、Trinoなどのversionと実環境の構成に合わせた確認が必要である。
実施する場合は、検証環境でbackupとrollback手順を用意し、Ozone側を保持したままobject数、size、checksum、Icebergの集計結果を比較してから切り替える。AWS利用料金にも注意し、実機検証の結果に応じて本記事を更新する予定である。
