0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【第2編】HDFSのrawログとApache IcebergをローカルS3へ移行してみる

0
Last updated at Posted at 2026-09-18

はじめに

すでにHDFS RawをAmazon S3へバックアップし、IcebergもAmazon S3 + Glueへ同期している場合、本稿のOzone移行は実施しない。本seriesの後続記事では、その既存AWS backupを使用する。本稿はAWSへ接続せずS3互換性をローカル検証したい場合の任意手順として残す。

前回は、Kafka Connect HDFS Sinkが保存したrawログをSpark SQLで直接解析し、Hive curatedを経由せずApache Icebergへ投入するように変更した。

今回はこの続きとして、保存先をHDFSから自宅環境のS3互換Object Storageへ段階的に移行する。ローカルS3にはApache OzoneのS3 Gatewayを使用する。

対象は次の2系統である。

  • syslog
  • authlog

本記事では、既存HDFSをすぐに削除しない。HDFSとOzoneを並行運用し、件数と内容を比較した後に参照先を切り替える。

本記事のOzone単一ノード構成は移行手順を確認するための検証用である。HDFSの代替として可用性まで求める場合は、OM/SCMのHA、複数DataNode、複数S3 Gateway、TLS、Kerberos、監視、バックアップを別途設計する。

今回の方針

rawとIcebergで移行方法を分ける

rawログはJSON Textであり、HDFS上の通常ファイルとして保存されている。このため、Hadoop DistCpでhdfs://からs3a://へコピーできる。

一方、Iceberg warehouseをファイル単位でそのままコピーしてはいけない。Icebergのmetadataにはdata fileやmanifest fileの場所が記録されており、HDFS上のmetadataをOzoneへ置くだけでは参照先がHDFSのまま残る可能性がある。

今回は次の方法を使う。

対象 移行方法
raw JSON Text Hadoop DistCpでhdfs://からs3a://へコピー
Iceberg Ozone側に新規テーブルを作成し、Spark SQLのINSERT INTO ... SELECT ...で論理コピー

既存環境を正本として残す

切り替え前は次の状態にする。

Ozone側を作成しても、検証が終わるまではHDFS側の削除や既存テーブルのDROPを行わない。

前提環境

以下は構築済みとする。

項目 値
OS Ubuntu 24.04
Hadoop 3.3.6
HDFS nameservice cluster1
Spark 3.5.8
Apache Iceberg 1.10.1
Hive Metastore 構築済み
既存catalog hive_prod
既存warehouse hdfs://cluster1/warehouse/iceberg
操作ホスト ope1
Ozoneホスト ozone1
Ozone 2.2.1
Ozone S3 Gateway http://ozone1:9878

既存テーブルは次の通りである。

hive_prod.logs.syslog_iceberg
hive_prod.logs.authlog_iceberg

rawログは次の場所に保存されている。

hdfs://cluster1/data/kafka/syslog/YYYY/MM/DD/HH
hdfs://cluster1/data/kafka/authlog/YYYY/MM/DD/HH

Ozoneには次のS3 bucketを作る。

bucket 用途
log-raw syslog/authlogのraw JSON Text
lakehouse Iceberg warehouse

Apache Ozoneを構築する

Apache Ozoneは、Ozone Manager(OM)、Storage Container Manager(SCM)、DataNode、S3 Gateway(S3G)で構成される。S3GはstatelessなS3互換REST frontendであり、objectとmetadataはOzone cluster側へ保存される。

検証用clusterを用意する

本記事では、まず公式のDocker Compose構成で移行方法を確認する。production構成は公式Administrator Guideに従ってbare metalまたはKubernetesへ構築する。

ozone1で実行する。

wget https://downloads.apache.org/ozone/2.2.1/ozone-2.2.1.tar.gz
tar xzf ozone-2.2.1.tar.gz
cd ozone-2.2.1/compose/ozone
docker compose up -d --scale datanode=3
docker compose ps

S3 Gatewayは標準では9878で待ち受ける。操作ホストから疎通を確認する。

curl -I http://ozone1:9878

Docker Compose版は開発・評価用である。HDFSから完全移行する前に、少なくともOM/SCM/DataNodeの永続volume、replication、service再起動、host再起動、disk故障時の動作を確認する。

S3 credentialを用意する

Ozone securityを無効にした検証用clusterでは、任意のaccess keyとsecret keyを使用できる。次の値は例であり、実際には推測されにくい値を使用する。

export AWS_ACCESS_KEY_ID='<OZONE_S3_ACCESS_KEY>'
export AWS_SECRET_ACCESS_KEY='<OZONE_S3_SECRET_KEY>'
export AWS_DEFAULT_REGION='us-east-1'

Kerberosを有効にしたproduction clusterでは、対象ユーザーでkinitした後に次を実行し、Ozone ManagerからS3 credentialを取得する。

ozone s3 getsecret

Ozone 1.4.0以降、生成したsecretが表示されるのは初回だけである。紛失時はozone s3 revokesecretで失効させてから再生成する。credentialは記事、Git、shell historyへ残さない。

bucketを作成する

操作ホストope1にAWS CLIを用意し、Ozone S3 Gatewayをendpointに指定する。

aws --endpoint-url http://ozone1:9878 s3api create-bucket --bucket log-raw
aws --endpoint-url http://ozone1:9878 s3api create-bucket --bucket lakehouse
aws --endpoint-url http://ozone1:9878 s3api list-buckets

S3 Gatewayで作成したbucketは、Ozoneの/s3v volume配下へ保存される。Ozone Shellでも確認できる。

ozone sh bucket list /s3v

productionではApache RangerまたはOzone ACLを使い、Spark用principalへlog-rawとlakehouseに必要な権限だけを付与する。検証用の非secure clusterをそのまま外部公開しない。

Hadoop S3Aを設定する

対応するJARを確認する

HadoopからS3互換Object Storageへ接続するにはS3A connectorが必要である。Hadoop 3.3.6では、Hadoop本体と互換性のあるhadoop-awsおよびAWS SDK bundleを使用する。

hadoop version
hadoop classpath | tr ':' '\n' | grep -E 'hadoop-aws|aws-java-sdk'

何も表示されない場合は、利用中のHadoop distributionと同じversionのhadoop-aws、およびそのPOMが要求するAWS SDKを追加する。Hadoop本体と異なるversionのhadoop-awsを混在させない。

S3A classが読み込めるclasspathを、DistCpとSparkの両方へ反映する。

credential fileを作成する

検証用として、Spark実行ユーザーだけが読めるHadoop credential providerへaccess keyとsecret keyを保存する。

sudo install -d -o spark -g spark -m 700 /var/lib/spark/.hadoop
sudo -u spark hadoop credential create fs.s3a.access.key \
  -provider jceks://file/var/lib/spark/.hadoop/ozone-s3.jceks
sudo -u spark hadoop credential create fs.s3a.secret.key \
  -provider jceks://file/var/lib/spark/.hadoop/ozone-s3.jceks
sudo chmod 600 /var/lib/spark/.hadoop/ozone-s3.jceks

promptにはOzone S3用のaccess keyとsecret keyを入力する。平文のcredentialをcore-site.xmlや起動shellへ直接記載しない。

cluster modeでexecutorを別hostに起動する場合は、同じJCEKSを全実行nodeの同一pathへ安全に配布する。DistCpを別ユーザーで実行する場合も、そのユーザー専用のcredential providerを作成する。

core-site.xmlを設定する

Hadoop clientとSpark executorから参照されるcore-site.xmlへ追加する。

<property>
  <name>fs.s3a.endpoint</name>
  <value>http://ozone1:9878</value>
</property>

<property>
  <name>fs.s3a.endpoint.region</name>
  <value>us-east-1</value>
</property>

<property>
  <name>fs.s3a.path.style.access</name>
  <value>true</value>
</property>

<property>
  <name>fs.s3a.bucket.probe</name>
  <value>0</value>
</property>

<property>
  <name>fs.s3a.change.detection.mode</name>
  <value>none</value>
</property>

<property>
  <name>fs.s3a.connection.ssl.enabled</name>
  <value>false</value>
</property>

<property>
  <name>fs.s3a.aws.credentials.provider</name>
  <value>org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider</value>
</property>

<property>
  <name>hadoop.security.credential.provider.path</name>
  <value>jceks://file/var/lib/spark/.hadoop/ozone-s3.jceks</value>
</property>

Ozone S3 Gatewayは標準でpath-style URLを使用する。今回はhttp://ozone1:9878/log-raw/...形式で接続するためtrueにする。Ozoneの公式S3A手順に合わせ、bucket probeとchange detectionも無効にする。

接続を確認する。

sudo -u spark hadoop fs -mkdir -p s3a://log-raw/connection-test
sudo -u spark hadoop fs -touchz s3a://log-raw/connection-test/ok
sudo -u spark hadoop fs -ls s3a://log-raw/connection-test
sudo -u spark hadoop fs -rm -r s3a://log-raw/connection-test

rawログをHDFSからOzoneへコピーする

最初から全期間をコピーせず、1日分で動作を確認する。

sudo -u spark hadoop distcp \
  -update \
  -strategy dynamic \
  hdfs://cluster1/data/kafka/syslog/2026/09/15 \
  s3a://log-raw/syslog/2026/09/15

sudo -u spark hadoop distcp \
  -update \
  -strategy dynamic \
  hdfs://cluster1/data/kafka/authlog/2026/09/15 \
  s3a://log-raw/authlog/2026/09/15

sourceとtargetのファイル数、byte数を比較する。

sudo -u spark hadoop fs -count -h hdfs://cluster1/data/kafka/syslog/2026/09/15
sudo -u spark hadoop fs -count -h s3a://log-raw/syslog/2026/09/15

sudo -u spark hadoop fs -count -h hdfs://cluster1/data/kafka/authlog/2026/09/15
sudo -u spark hadoop fs -count -h s3a://log-raw/authlog/2026/09/15

S3はHDFSと異なりdirectoryそのものを持たない。hadoop fs -countに表示されるdirectory数は一致判定の中心にせず、ファイル数、byte数、Sparkで読んだrecord数を確認する。

全期間を移行するときは、まず過去分をコピーし、切り替え直前に同じ-updateを再実行して差分を追従する。

SparkからOzone rawを読む

前回作成したload_raw_to_iceberg.shの入力pathを変更する前に、SparkがOzoneのJSON Textを読めることを確認する。

sudo -u spark /usr/local/bin/spark-sql-iceberg <<'EOF'
SET spark.sql.session.timeZone=Asia/Tokyo;

CREATE OR REPLACE TEMPORARY VIEW syslog_s3_test
USING text
OPTIONS (
  path 's3a://log-raw/syslog/2026/09/15'
);

SELECT COUNT(*) AS cnt
FROM syslog_s3_test
WHERE value IS NOT NULL
  AND length(trim(value)) > 0;
EOF

HDFS側も同じ条件で数える。

sudo -u spark /usr/local/bin/spark-sql-iceberg <<'EOF'
CREATE OR REPLACE TEMPORARY VIEW syslog_hdfs_test
USING text
OPTIONS (
  path 'hdfs://cluster1/data/kafka/syslog/2026/09/15'
);

SELECT COUNT(*) AS cnt
FROM syslog_hdfs_test
WHERE value IS NOT NULL
  AND length(trim(value)) > 0;
EOF

authlogも同様に確認する。ここで一致しない場合は、Iceberg移行へ進まず、DistCpの失敗、書き込み途中のHDFS file、権限、S3Aのmultipart uploadを確認する。

Ozone側のIceberg catalogを追加する

既存のhive_prodを残し、移行確認用にozone_s3 catalogを追加する。

Spark起動optionへ次を設定する。

--conf spark.sql.catalog.ozone_s3=org.apache.iceberg.spark.SparkCatalog
--conf spark.sql.catalog.ozone_s3.type=hive
--conf spark.sql.catalog.ozone_s3.uri=thrift://hive1:9083
--conf spark.sql.catalog.ozone_s3.warehouse=s3a://lakehouse/warehouse

hive_prodとozone_s3が同じHive Metastoreを利用するため、同じnamespaceとtable名は使わない。移行確認用namespaceはlogs_s3とする。

移行元: hive_prod.logs.syslog_iceberg
移行先: ozone_s3.logs_s3.syslog_iceberg

移行元: hive_prod.logs.authlog_iceberg
移行先: ozone_s3.logs_s3.authlog_iceberg

catalog設定後、Spark SQLを起動して確認する。

SHOW CATALOGS;
CREATE NAMESPACE IF NOT EXISTS ozone_s3.logs_s3;

ozone_s3というcatalog名だけではHive Metastore内のtable identityは分離されない。同じMetastoreを使う場合は、移行元と異なるnamespaceまたはtable名が必要である。

Ozone側にIcebergテーブルを作成する

最初に移行元のschemaとpartition specを確認する。

sudo -u spark /usr/local/bin/spark-sql-iceberg <<'EOF'
DESCRIBE TABLE EXTENDED hive_prod.logs.syslog_iceberg;
DESCRIBE TABLE EXTENDED hive_prod.logs.authlog_iceberg;
EOF

以下は既存記事と同じschema、days(ts)とhostによるpartitionを使用する例である。実環境の結果が異なる場合は、移行元に合わせる。

sudo -u spark /usr/local/bin/spark-sql-iceberg <<'EOF'
CREATE NAMESPACE IF NOT EXISTS ozone_s3.logs_s3;

CREATE TABLE IF NOT EXISTS ozone_s3.logs_s3.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 's3a://lakehouse/warehouse/logs_s3/syslog_iceberg';

CREATE TABLE IF NOT EXISTS ozone_s3.logs_s3.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 's3a://lakehouse/warehouse/logs_s3/authlog_iceberg';
EOF

作成場所を確認する。

sudo -u spark hadoop fs -ls s3a://lakehouse/warehouse/logs_s3/syslog_iceberg
sudo -u spark hadoop fs -ls s3a://lakehouse/warehouse/logs_s3/authlog_iceberg

Icebergデータを論理コピーする

まず1日分をコピーする。再実行できるよう、targetの対象日を削除してから投入する。

sudo -u spark /usr/local/bin/spark-sql-iceberg <<'EOF'
SET spark.sql.session.timeZone=Asia/Tokyo;

DELETE FROM ozone_s3.logs_s3.syslog_iceberg
WHERE dt = DATE '2026-09-15';

INSERT INTO ozone_s3.logs_s3.syslog_iceberg
SELECT host, ts, severity, program, msg, dt, hr
FROM hive_prod.logs.syslog_iceberg
WHERE dt = DATE '2026-09-15';

DELETE FROM ozone_s3.logs_s3.authlog_iceberg
WHERE dt = DATE '2026-09-15';

INSERT INTO ozone_s3.logs_s3.authlog_iceberg
SELECT host, ts, severity, program, msg, dt, hr
FROM hive_prod.logs.authlog_iceberg
WHERE dt = DATE '2026-09-15';
EOF

この処理ではSparkがHDFS側Icebergを読み、Ozone側に新しいdata file、manifest、metadataを生成する。warehouse directoryの物理コピーは行わない。

全期間を一度に処理すると失敗時の再実行範囲が大きくなるため、dt単位で古い日から順に移行する。

HDFS側とOzone側を比較する

日別件数と時刻範囲

sudo -u spark /usr/local/bin/spark-sql-iceberg <<'EOF'
SET spark.sql.session.timeZone=Asia/Tokyo;

SELECT 'hdfs' AS src, dt, COUNT(*) AS cnt,
       MIN(ts) AS min_ts, MAX(ts) AS max_ts
FROM hive_prod.logs.syslog_iceberg
WHERE dt = DATE '2026-09-15'
GROUP BY dt
UNION ALL
SELECT 'ozone' AS src, dt, 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'
GROUP BY dt;
EOF

authlogも同じSQLで比較する。

集計差分

全行の順序には意味がないため、host、program、severity、dt、hr単位の件数を比較する。

WITH hdfs_data AS (
  SELECT host, program, severity, dt, hr, COUNT(*) AS cnt
  FROM hive_prod.logs.syslog_iceberg
  WHERE dt = DATE '2026-09-15'
  GROUP BY host, program, severity, dt, hr
), s3_data AS (
  SELECT host, program, severity, dt, hr, COUNT(*) AS cnt
  FROM ozone_s3.logs_s3.syslog_iceberg
  WHERE dt = DATE '2026-09-15'
  GROUP BY host, program, severity, dt, hr
), hdfs_only AS (
  SELECT * FROM hdfs_data
  EXCEPT
  SELECT * FROM s3_data
), s3_only AS (
  SELECT * FROM s3_data
  EXCEPT
  SELECT * FROM hdfs_data
)
SELECT 'hdfs_only' AS diff, * FROM hdfs_only
UNION ALL
SELECT 's3_only' AS diff, * FROM s3_only;

結果が0行であれば、この集計粒度では差分がない。複数日を比較し、特に00時台と23時台のdt、hr、tsを確認する。

日次ロードをOzoneへ切り替える

前回作成した直接ロードでは、入力pathと書き込み先を次のように変更する。

- path 'hdfs://cluster1/data/kafka/syslog/${load.day}'
+ path 's3a://log-raw/syslog/${load.day}'

- path 'hdfs://cluster1/data/kafka/authlog/${load.day}'
+ path 's3a://log-raw/authlog/${load.day}'

- DELETE FROM hive_prod.logs.syslog_iceberg
+ DELETE FROM ozone_s3.logs_s3.syslog_iceberg

- INSERT INTO hive_prod.logs.syslog_iceberg
+ INSERT INTO ozone_s3.logs_s3.syslog_iceberg

- DELETE FROM hive_prod.logs.authlog_iceberg
+ DELETE FROM ozone_s3.logs_s3.authlog_iceberg

- INSERT INTO hive_prod.logs.authlog_iceberg
+ INSERT INTO ozone_s3.logs_s3.authlog_iceberg

ただし、Kafka Connectの保存先をまだHDFSから変更していない場合、当日分のrawはOzoneに存在しない。移行期間中は次の順で日次処理を行う。

完全移行する場合はKafka Connect側もS3 Sinkへ変更する。S3 Sink connectorの導入方法と設定項目は使用するdistributionによって異なるため、本記事では既存HDFS Sinkからの履歴移行とSpark/Icebergの切り替えまでを対象とする。

切り替え手順

次の順で実施する。

  1. Ozone clusterとS3 bucketを構築する
  2. S3Aの接続を確認する
  3. 1日分のrawをDistCpでコピーする
  4. SparkでHDFS rawとOzone rawの件数を比較する
  5. Ozone側Icebergテーブルを別namespaceに作成する
  6. 1日分を論理コピーし、件数、時刻範囲、集計差分を比較する
  7. 過去分をdt単位で移行する
  8. 移行中に増えたrawをDistCpの-updateで追従する
  9. 日次ロードをOzone入力、Ozone Iceberg出力へ変更する
  10. Trino、Grafana、Zeppelinなどの参照先をOzone側へ変更する
  11. 一定期間、HDFS側をread-onlyのrollback元として保持する
  12. 保持期間と復旧手順を確認してからHDFS側を廃止する

切り替え直前には、HDFS側へのIceberg書き込みを止め、最後の対象日をOzoneへ同期して差分が0件であることを確認する。

TrinoをOzone S3 Icebergへ接続する

Trinoの設定変更は必要か

必要である。

Icebergのmetadataにdata fileのpathが記録されていても、Trinoには次の情報が必要になる。

  • Iceberg catalogとして使用するHive Metastoreの接続先
  • s3a://を読み込むS3 filesystem
  • Ozone S3 Gatewayのendpointとregion
  • パス形式のアクセス
  • S3 access keyとsecret key

既存のHDFS用iceberg catalogを直接置き換えず、Ozone確認用にiceberg_ozone catalogを追加する。

iceberg       = HDFS Iceberg、rollback用
iceberg_ozone = Ozone S3 Iceberg、移行先

以下はTrino 476、Coordinator兼Worker 1台とWorker 2台の構成を前提とする。catalog設定とcredentialはtrino1、trino2、trino3へ同じ内容を配置する。

TrinoはS3互換storageへ接続できるが、公式に検証される製品は限られる。Ozone S3 Gatewayは本番切り替え前に、SELECT、metadata table、partition pruning、複数Workerでの分散読み込みを実データで確認する。

既存catalogをバックアップする

各Trino nodeで実行する。

sudo cp -a \
  /opt/trino/etc/catalog/iceberg.properties \
  /opt/trino/etc/catalog/iceberg.properties.$(date +%Y%m%d%H%M%S).bak

既存HDFS catalogは削除しない。

Ozone credentialをsystemdへ渡す

catalog fileへsecretを平文で書かず、環境変数を使用する。

sudo tee /etc/trino/ozone-s3.env >/dev/null <<'EOF'
OZONE_S3_ACCESS_KEY=<OZONE_S3_ACCESS_KEY>
OZONE_S3_SECRET_KEY=<OZONE_S3_SECRET_KEY>
EOF

sudo chown root:trino /etc/trino/ozone-s3.env
sudo chmod 640 /etc/trino/ozone-s3.env

各nodeのTrino systemd unitへdrop-inを追加する。

sudo systemctl edit trino
[Service]
EnvironmentFile=/etc/trino/ozone-s3.env

実際のservice名が異なる場合は既存unit名へ置き換える。

Ozone用Iceberg catalogを追加する

各Trino nodeに/opt/trino/etc/catalog/iceberg_ozone.propertiesを作成する。

sudo tee /opt/trino/etc/catalog/iceberg_ozone.properties >/dev/null <<'EOF'
connector.name=iceberg
iceberg.catalog.type=hive_metastore
hive.metastore.uri=thrift://hive1:9083,thrift://hive2:9083

fs.native-s3.enabled=true
s3.endpoint=http://ozone1:9878
s3.region=us-east-1
s3.path-style-access=true
s3.aws-access-key=${ENV:OZONE_S3_ACCESS_KEY}
s3.aws-secret-key=${ENV:OZONE_S3_SECRET_KEY}

iceberg.file-format=PARQUET
iceberg.compression-codec=ZSTD
EOF

sudo chown trino:trino /opt/trino/etc/catalog/iceberg_ozone.properties
sudo chmod 640 /opt/trino/etc/catalog/iceberg_ozone.properties

Trino 476ではnative S3 filesystemをfs.native-s3.enabled=trueで有効にする。新しいTrinoではproperty名がfs.s3.enabledへ変更されているため、upgrade時は使用versionのdocumentationを確認する。

Ozone側tableはSparkでs3a://lakehouse/...として作成している。Trinoのnative S3 filesystemがOzone endpointへ接続し、Iceberg metadataに記録されたobjectを読み込む。

全Trino nodeへ配布する

trino1で設定を作成した場合の例である。

sudo rsync -av \
  /opt/trino/etc/catalog/iceberg_ozone.properties \
  trino2:/tmp/
sudo rsync -av \
  /opt/trino/etc/catalog/iceberg_ozone.properties \
  trino3:/tmp/

trino2とtrino3で所定の場所へ配置し、/etc/trino/ozone-s3.envとsystemd drop-inも同様に設定する。Coordinatorだけでなく、data fileを読む全Workerに設定とnetwork到達性が必要である。

Trinoを再起動する

sudo systemctl daemon-reload
sudo systemctl restart trino
sudo systemctl status trino --no-pager

3台構成ではWorker、Coordinatorの順に停止し、Coordinator、Workerの順に起動する既存のcluster運用手順に合わせる。

logを確認する。

sudo journalctl -u trino -n 200 --no-pager

iceberg_ozone catalogのload error、credential error、Ozone endpointへの接続失敗がないことを確認する。

Trinoから確認する

Coordinatorへ接続して実行する。

SHOW CATALOGS;
SHOW SCHEMAS FROM iceberg_ozone;
SHOW TABLES FROM iceberg_ozone.logs_s3;

SELECT
  dt,
  COUNT(*) AS cnt,
  MIN(ts) AS min_ts,
  MAX(ts) AS max_ts
FROM iceberg_ozone.logs_s3.syslog_iceberg
GROUP BY dt
ORDER BY dt DESC
LIMIT 7;

authlogも確認する。

SELECT dt, COUNT(*) AS cnt
FROM iceberg_ozone.logs_s3.authlog_iceberg
GROUP BY dt
ORDER BY dt DESC
LIMIT 7;

Iceberg metadata tableも確認する。

SELECT committed_at, snapshot_id, operation
FROM iceberg_ozone.logs_s3."syslog_iceberg$snapshots"
ORDER BY committed_at DESC
LIMIT 10;

HDFS側と同じ対象日を比較する。

SELECT 'hdfs' AS src, COUNT(*) AS cnt
FROM iceberg.logs.syslog_iceberg
WHERE dt = DATE '2026-09-15'
UNION ALL
SELECT 'ozone' AS src, COUNT(*) AS cnt
FROM iceberg_ozone.logs_s3.syslog_iceberg
WHERE dt = DATE '2026-09-15';

GrafanaとZeppelinを切り替える

Trinoの接続先は同じCoordinatorのままでよい。SQL内のcatalogとschemaを変更する。

- FROM iceberg.logs.syslog_iceberg
+ FROM iceberg_ozone.logs_s3.syslog_iceberg

- FROM iceberg.logs.authlog_iceberg
+ FROM iceberg_ozone.logs_s3.authlog_iceberg

Grafana dashboardを一度に上書きせず、複製したdashboardでHDFS版とOzone版の件数、時刻、応答時間を比較してから切り替える。

Trinoのrollback

問題があればqueryの参照先を既存のiceberg.logsへ戻す。iceberg.propertiesを残しているため、Ozone用catalogを削除しなくても即時に参照経路を戻せる。

通常時   : iceberg_ozone.logs_s3.*
rollback : iceberg.logs.*

HDFSからOzone移行時のrollback

問題が発生した場合は、日次ロードと参照先を既存のhive_prod.logsへ戻す。HDFS側を削除していなければ、metadataの書き換えやOzoneからの逆コピーをせずに切り戻せる。

通常時
ozone_s3.logs_s3.* を参照

rollback時
hive_prod.logs.* を参照

切り替え後にOzone側だけへ書き込まれた期間がある場合は、その期間をHDFS側へ論理コピーしてから完全に戻す。Iceberg warehouseのdirectoryを逆方向へ単純コピーしない。

運用上の注意

HDFSを減らしても保存先の冗長化は必要

OzoneがHadoop clusterと同じ電源、同じhost、同じdiskだけで動いている場合、保存先をS3 APIへ変えても障害分離にはならない。少なくとも次を検討する。

  • OM/SCMのHAと複数DataNode構成
  • 複数S3 Gatewayとload balancer
  • TLS
  • disk故障と容量の監視
  • 別clusterまたは別mediaへのbackup
  • credentialの定期rotation
  • OzoneとHadoopを同時に失わない配置

small file問題は残る

保存先をHDFSからS3へ変えても、細かいdata fileが大量に作られる問題は解消しない。Icebergのcompaction、snapshot expiration、orphan file削除は継続する。

rename前提の処理を持ち込まない

Object StorageはHDFSと同じfilesystemではない。renameやdirectoryの存在を前提にした独自shellは見直す。IcebergテーブルはIcebergのcommitとmaintenance procedureを通して操作する。

credentialをログへ出さない

access key、secret keyを次へ残さない。

  • Git管理対象の設定ファイル
  • Qiita記事
  • シェル履歴
  • systemd unitのcommand line
  • Sparkイベントログ

Ozone移行後の構成

今回の移行後もHive MetastoreはIceberg catalogとして使用する。廃止するのはHive curated処理とHDFS上の保存領域であり、Hive Metastoreまで同時に撤去するものではない。

まとめ

HDFSからローカルS3へ移行するときは、rawとIcebergを同じ方法で扱わないことが重要である。

Ozone側は別namespaceで作成し、HDFS側と日別件数、時刻範囲、集計結果を比較した。検証後に日次ロードと参照先を切り替えることで、問題があればHDFS側へ戻せる。

前回Hive curatedを廃止して変換処理をSparkへ集約したため、今回変更する中心は入力pathとIcebergの書き込み先になった。保存形式を単純コピーするのではなく、IcebergのmetadataをOzone上で新しく生成することが、安全に移行するための要点である。

TrinoではHive Metastore設定だけでなく、Ozone endpoint、path-style access、credentialを持つiceberg_ozone catalogを全nodeへ追加した。既存HDFS catalogを残すことで、query単位で比較とrollbackができる。

参考資料

関連記事

実機検証について

本記事の手順は、執筆時点では実機環境での一連の動作検証をまだ完了していない。掲載している設定値、command、service名、package構成は設計例を含むため、利用するHadoop、Apache Ozone、Spark、Iceberg、Trinoなどのversionと実環境の構成に合わせた確認が必要である。

実施する場合は、検証環境でbackupとrollback手順を用意し、HDFS側を保持したまま件数、時刻範囲、集計結果を比較してから切り替える。実機検証の結果に応じて本記事を更新する予定である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?