|
| 1 | +# Iceberg Table Migration to OCI |
1 | 2 |
|
| 3 | +This repository is a Terraform-based demo for migrating an Apache Iceberg table into OCI Object Storage and validating it with Spark. |
| 4 | + |
| 5 | +Current primary run mode: |
| 6 | + |
| 7 | +```text |
| 8 | +local terminal -> terraform apply -> SSH to OCI VM -> run /opt/iceberg scripts |
| 9 | +``` |
| 10 | + |
| 11 | +## What It Does |
| 12 | + |
| 13 | +Terraform provisions: |
| 14 | + |
| 15 | +- OCI network |
| 16 | +- OCI compute VM |
| 17 | +- OCI Object Storage bucket |
| 18 | +- cloud-init bootstrap on the VM |
| 19 | + |
| 20 | +Cloud-init installs and prepares: |
| 21 | + |
| 22 | +- Docker |
| 23 | +- Spark |
| 24 | +- Apache Iceberg runtime |
| 25 | +- PostgreSQL |
| 26 | +- Hive Metastore container |
| 27 | +- Iceberg JDBC catalog |
| 28 | +- OCI CLI and AWS CLI |
| 29 | +- helper scripts under `/opt/iceberg` |
| 30 | + |
| 31 | +The demo scripts then: |
| 32 | + |
| 33 | +1. Generate a real tiny Iceberg table using MinIO as fake AWS S3. |
| 34 | +2. Export real Iceberg `data/` and `metadata/` files. |
| 35 | +3. Copy those files into OCI Object Storage. |
| 36 | +4. Register the copied table in Iceberg JDBC catalog. |
| 37 | +5. Validate with Spark. |
| 38 | +6. Optionally validate with Trino. |
| 39 | + |
| 40 | +## Terraform Options |
| 41 | + |
| 42 | +Optional Trino validation is controlled by the Terraform variable `validation_engines`. |
| 43 | + |
| 44 | +Set it in `terraform.tfvars` before `terraform apply`: |
| 45 | + |
| 46 | +```hcl |
| 47 | +validation_engines = ["spark", "trino"] |
| 48 | +``` |
| 49 | + |
| 50 | +Default is Spark only: |
| 51 | + |
| 52 | +```hcl |
| 53 | +validation_engines = ["spark"] |
| 54 | +``` |
| 55 | + |
| 56 | +The included `terraform.tfvars` may already set `["spark", "trino"]`; change it to `["spark"]` if you want Spark-only validation. |
| 57 | + |
| 58 | +You can also override it for one VM run: |
| 59 | + |
| 60 | +```bash |
| 61 | +export VALIDATION_ENGINES="spark,trino" |
| 62 | +``` |
| 63 | + |
| 64 | +## Configure Terraform Inputs |
| 65 | + |
| 66 | +Populate these values before running Terraform. |
| 67 | + |
| 68 | +In `provider.auto.tfvars`: |
| 69 | + |
| 70 | +- `provider_oci.tenancy_ocid`: tenancy OCID from OCI. |
| 71 | +- `provider_oci.user_ocid`: user OCID for the API key owner. |
| 72 | +- `provider_oci.fingerprint`: fingerprint of the uploaded OCI API key. |
| 73 | +- `provider_oci.private_key_path`: local path to the matching API private key. |
| 74 | +- `provider_oci.private_key_password`: private key passphrase, or `""` if the key has none. |
| 75 | +- `provider_oci.region`: target OCI region, for example `eu-frankfurt-1`. |
| 76 | +- `compartment_ids.sandbox`: compartment OCID where Terraform creates the resources. |
| 77 | + |
| 78 | +In `terraform.tfvars`: |
| 79 | + |
| 80 | +- `linux_images`: Oracle Linux 9 image OCID for the selected `provider_oci.region`. |
| 81 | +- `instance_params`: VM availability domain, shape, subnet, image version, OCPUs, and memory. |
| 82 | +- `ssh_public_key`, which must point to the public key Terraform should inject into the VM. |
| 83 | +- `registry`: OCIR region key, for example `fra.ocir.io` or `iad.ocir.io`. |
| 84 | +- `bucket_params`: Object Storage bucket name, compartment name, storage tier, and optional `force_destroy`. |
| 85 | +- Network maps: adjust CIDRs, subnet privacy, route rules, and security list rules if the defaults do not fit your tenancy. |
| 86 | +- `validation_engines`: use `["spark"]` for Spark only or `["spark", "trino"]` to add Trino validation. |
| 87 | + |
| 88 | +## Quick Run |
| 89 | + |
| 90 | +From this repository: |
| 91 | + |
| 92 | +```bash |
| 93 | +terraform init |
| 94 | +terraform validate |
| 95 | +terraform plan |
| 96 | +terraform apply |
| 97 | +``` |
| 98 | + |
| 99 | +When Terraform asks for confirmation, enter `yes`. |
| 100 | + |
| 101 | +After Terraform finishes, note the public IP address from the `linux_instances` output. |
| 102 | + |
| 103 | +## Post-Deploy Actions |
| 104 | + |
| 105 | +SSH to the compute VM: |
| 106 | + |
| 107 | +```bash |
| 108 | +ssh opc@<vm_public_ip> |
| 109 | +``` |
| 110 | + |
| 111 | +Confirm cloud-init completed and the helper scripts exist: |
| 112 | + |
| 113 | +```bash |
| 114 | +cloud-init status --wait --long |
| 115 | +docker version |
| 116 | +/opt/spark/bin/spark-submit --version |
| 117 | +ls -l /opt/iceberg/ |
| 118 | +``` |
| 119 | + |
| 120 | +If the helper scripts are missing, inspect the cloud-init log: |
| 121 | + |
| 122 | +```bash |
| 123 | +sudo tail -n 100 /var/log/cloud-init-output.log |
| 124 | +``` |
| 125 | + |
| 126 | +Generate the simulated AWS Iceberg source table: |
| 127 | + |
| 128 | +```bash |
| 129 | +/opt/iceberg/generate-simulated-aws-iceberg-table.sh |
| 130 | +``` |
| 131 | + |
| 132 | +The default export location is: |
| 133 | + |
| 134 | +```text |
| 135 | +/opt/iceberg/generated_aws_source/iceberg-table-demo/lakehouse/sales/orders/ |
| 136 | +``` |
| 137 | + |
| 138 | +Confirm the export contains Iceberg `data/` and `metadata/` files: |
| 139 | + |
| 140 | +```bash |
| 141 | +find /opt/iceberg/generated_aws_source/iceberg-table-demo/lakehouse/sales/orders -type f | sort |
| 142 | +find /opt/iceberg/generated_aws_source/iceberg-table-demo/lakehouse/sales/orders/metadata -name "*.metadata.json" -type f | sort |
| 143 | +``` |
| 144 | + |
| 145 | +Copy the generated Iceberg files to OCI Object Storage: |
| 146 | + |
| 147 | +```bash |
| 148 | +/opt/iceberg/copy-simulated-source-to-oci.sh |
| 149 | +``` |
| 150 | + |
| 151 | +Verify the copied objects: |
| 152 | + |
| 153 | +```bash |
| 154 | +oci os object list \ |
| 155 | + --auth instance_principal \ |
| 156 | + --bucket-name iceberg-table-demo \ |
| 157 | + --prefix lakehouse/sales/orders/ \ |
| 158 | + --fields name \ |
| 159 | + --all |
| 160 | +``` |
| 161 | + |
| 162 | +If you changed the bucket or table prefix, use the same `BUCKET` and `TABLE_PREFIX` values for generation, copy, verification, and registration. |
| 163 | + |
| 164 | +Create an OCI Customer Secret Key for S3-compatible Object Storage access, then set it on the VM. Use the Customer Secret Key access key as `OCI_ACCESS_KEY_ID` and the generated secret value as `OCI_SECRET_ACCESS_KEY`. |
| 165 | + |
| 166 | +```bash |
| 167 | +export OCI_ACCESS_KEY_ID="<access-key>" |
| 168 | +export OCI_SECRET_ACCESS_KEY="<secret-key>" |
| 169 | +``` |
| 170 | + |
| 171 | +The VM scripts use the Terraform region by default. Set `OCI_REGION` or `OCI_S3_ENDPOINT` only if you need to override the generated endpoint: |
| 172 | + |
| 173 | +```bash |
| 174 | +export OCI_REGION="<oci_region>" |
| 175 | +export OCI_S3_ENDPOINT="https://<namespace>.compat.objectstorage.<oci_region>.oci.customer-oci.com" |
| 176 | +``` |
| 177 | + |
| 178 | +Register the copied table and validate it with Spark: |
| 179 | + |
| 180 | +```bash |
| 181 | +/opt/iceberg/register-simulated-oci-table.sh |
| 182 | +``` |
| 183 | + |
| 184 | +If `validation_engines` includes `trino`, the same registration script also runs Trino validation. You can enable it for one run with: |
| 185 | + |
| 186 | +```bash |
| 187 | +export VALIDATION_ENGINES="spark,trino" |
| 188 | +/opt/iceberg/register-simulated-oci-table.sh |
| 189 | +``` |
| 190 | + |
| 191 | +To run a manual Spark SQL check: |
| 192 | + |
| 193 | +```bash |
| 194 | +/opt/iceberg/spark-sql-oci.sh |
| 195 | +``` |
| 196 | + |
| 197 | +```sql |
| 198 | +SHOW TABLES IN oci.sales; |
| 199 | +DESCRIBE oci.sales.orders; |
| 200 | +SELECT * FROM oci.sales.orders; |
| 201 | +exit; |
| 202 | +``` |
| 203 | + |
| 204 | +## Input Modes |
| 205 | + |
| 206 | +The scripts support two source modes: |
| 207 | + |
| 208 | +| Mode | Status | How to use | |
| 209 | +| --- | --- | --- | |
| 210 | +| `simulated_aws` / MinIO | Proven default | Run `generate-simulated-aws-iceberg-table.sh`, then copy/register/validate. | |
| 211 | +| `local_export` | Supported as local copy input | Set `SOURCE_DIR` to an existing local Iceberg table export, then run the copy/register flow. | |
| 212 | + |
| 213 | +Inputs must be generated by Iceberg or come from a real exported Iceberg table folder. |
| 214 | + |
| 215 | +### Local Export |
| 216 | + |
| 217 | +The local export folder should contain real Iceberg files: |
| 218 | + |
| 219 | +```text |
| 220 | +/path/to/exported/iceberg/table/ |
| 221 | + data/ |
| 222 | + metadata/ |
| 223 | + *.metadata.json |
| 224 | + *.avro |
| 225 | +``` |
| 226 | + |
| 227 | +Copy it to OCI with: |
| 228 | + |
| 229 | +```bash |
| 230 | +SOURCE_DIR=/path/to/exported/iceberg/table \ |
| 231 | +BUCKET=iceberg-table-demo \ |
| 232 | +TABLE_PREFIX=lakehouse/sales/orders \ |
| 233 | +/opt/iceberg/copy-simulated-source-to-oci.sh |
| 234 | +``` |
0 commit comments