Metadata database
The metadata database is the relational database holding every metalake, catalog, schema, table, user, group, role, and grant the server knows. The chart deploys a PostgreSQL for it by default, on a persistent volume that survives upgrades and uninstalls, which suits a trial.
For production, use a database you operate and back up as the metadata database. Moving it out of the cluster puts the metadata on infrastructure with your own snapshots, retention, and failover, and takes the database out of the release's lifecycle.
On an external database you create the database and the user. The chart's schema Job creates the
tables on install and upgrades them on every helm upgrade, as it does for the database it deploys.
The metadata database does not hold the table pointers of an Iceberg catalog. Each Iceberg catalog keeps those in a store of its own, which can be a separate database on the same server; see JDBC backend.
Quick start
-
Create the database and user. The chart creates neither on an external server. Make the JDBC user the owner of the database. The schema Job and the server use the user's default schema, so do not set
currentSchemain the JDBC URL.Create the PostgreSQL database and userCREATE USER {db_user} WITH PASSWORD '{db_password}';
CREATE DATABASE {database} OWNER {db_user};
\c {database}
GRANT ALL ON SCHEMA public TO {db_user};As the owner of the database, the user can create tables in
publicon PostgreSQL 15 and later as well; theGRANTis needed only when someone else owns the database. The user creates and alters the tables during schema upgrades and reads and writes their rows at run time, so a read-write user that does not own the tables is not enough. -
Make it the metadata database. Turn off the built-in database and set the connection, including the password.
my-values.yamlgravitino:
postgresql:
enabled: false
mysql:
enabled: false
entity:
jdbcUrl: "jdbc:postgresql://{postgres_host}:5432/{database}"
jdbcDriver: "org.postgresql.Driver"
jdbcUser: "{db_user}"
jdbcPassword: "{db_password}"The
entitysettings are read only when bothpostgresql.enabledandmysql.enabledare false. While either is true the chart derives the URL, driver, and user from the database it deploys and ignores what you put here.The chart writes the password to a Secret named
{release}-gravitino-entity-storethat it owns. Do not create that Secret yourself: Helm refuses to install over a Secret it did not create, and the chart has no setting for an existing Secret. The chart keeps the Secret's password once it exists, so a later change toentity.jdbcPasswordhas no effect. To rotate the password, change it in the database and in thejdbc-passwordkey of the Secret, then restart the server pods.With
gravitino.networkPolicy.enabled, the default egress rules reach only the in-cluster database. Add the external database host togravitino.networkPolicy.egressRules. -
Apply the values. Add the blocks above to the values file you installed with, and run the install command from Install again, with the same release, namespace, and license.
Reinstall with the new valueshelm upgrade --install "$RELEASE" "./enterprise-helm-$VERSION.tgz" -n "$NAMESPACE" -f my-values.yaml \
--set-string gravitino.license.key="$(tr -d '\n' < "$LICENSE_FILE")" \
--timeout 15m 2>&1 | tee helm-install.logThe schema Job runs against the new database and the server waits for it before starting.
The new database starts empty. Moving a release that already holds metadata to an external database means restoring a dump of the old one into it first; see Backup and restore. The in-cluster database's volume is kept, so nothing is lost if you have not.
-
Confirm the server reached the database. See Verify the connection.
Supported databases
| Database | Supported versions | Shipped by the chart |
|---|---|---|
| PostgreSQL | 12 to 16 | 16.15, the default |
| MySQL | 8 | 8.4, from the Red Hat rhel10/mysql-84 image, which needs a Red Hat pull secret; the community 8.4.11 image is an option |
Use MySQL
MySQL works the same way, with a different URL and driver. MySQL Connector/J is not in the server
image, so the chart must supply it at runtime. By default the chart downloads it from Maven Central
when the pod starts. An air-gapped cluster sets gravitino.jdbcDrivers.download.mavenMirror to an
internal Maven mirror, or sets gravitino.jdbcDrivers.mode to image or volume to supply the jar
another way.
CREATE USER IF NOT EXISTS '{db_user}'@'%' IDENTIFIED BY '{db_password}';
CREATE DATABASE IF NOT EXISTS {database};
GRANT ALL PRIVILEGES ON {database}.* TO '{db_user}'@'%';
FLUSH PRIVILEGES;
gravitino:
postgresql:
enabled: false
mysql:
enabled: false
jdbcDrivers:
enabled: true
mysql:
enabled: true
entity:
jdbcUrl: "jdbc:mysql://{mysql_host}:3306/{database}"
jdbcDriver: "com.mysql.cj.jdbc.Driver"
jdbcUser: "{db_user}"
jdbcPassword: "{db_password}"
Setting gravitino.mysql.enabled: true is a different thing entirely: it deploys a MySQL inside the
cluster in place of the default PostgreSQL. For an external MySQL both flags stay false.
Lineage database
Turning off gravitino.postgresql.enabled removes the database the lineage service uses as well.
Lineage keeps its own database on that same in-cluster PostgreSQL by default, so moving the metadata
database out without providing for lineage leaves the lineage pods unable to start.
Two routes are open. Point lineage at an external PostgreSQL of its own, or have the chart deploy a PostgreSQL for lineage alone.
External PostgreSQL for lineage
-
Create the lineage database and user. Nothing provisions them on an external server.
Create the lineage database and userCREATE USER buendia WITH PASSWORD '{lineage_password}';
CREATE DATABASE marquez OWNER buendia;
\c marquez
GRANT ALL ON SCHEMA public TO buendia;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO buendia; -
Point lineage at it.
my-values.yamlmarquez:
postgresql:
enabled: false
marquez:
inClusterDB:
postgresql:
enabled: false
db:
host: "{postgres_host}"
port: 5432
name: marquez
user: buendia
password: "{lineage_password}"
The lineage service runs its own schema migrations at startup, so the tables are created for you once the database and user exist. The chart's default lineage password, used by both routes unless you set one, is a public default; set your own. The names above are the chart defaults and can be changed, as long as the values file and the database agree. See Lineage for what the service does with them.
Dedicated in-cluster PostgreSQL for lineage
marquez:
postgresql:
enabled: true
The chart then deploys a PostgreSQL for lineage only, with its own database and user.
Schema
The schema Job runs whenever the chart deploys the database, and whenever gravitino.entity.jdbcUrl
starts with jdbc:postgresql:// or jdbc:mysql://.
| Event | What the chart does |
|---|---|
helm install | The Job applies the base schema, then the enterprise schema, and records the version. |
helm upgrade | A pre-upgrade hook scales the server to zero, the Job applies the upgrade scripts, and the server returns. |
The scripts come from the server image, so the schema always matches the server version the release runs.
Schema settings
| Value | Default | Purpose |
|---|---|---|
gravitino.schema.enabled | unset | Unset runs the Job automatically as described above. false skips it. true requires a PostgreSQL or MySQL target. |
gravitino.schema.baselineVersion | empty | The Gravitino version that created an existing database with no recorded version. Set to the image version to record it without DDL. |
gravitino.schema.sslMode | empty | Client TLS mode for the Job. Empty takes it from the JDBC URL. |
gravitino.schema.dbHost | empty | Host, when the JDBC URL is not a plain host:port/database form. |
gravitino.schema.dbPort | empty | Port, for the same case. |
gravitino.schema.dbName | empty | Database name, for the same case. |
gravitino.schema.wait.timeoutSeconds | 600 | How long the server waits for the Job before its init container fails. |
The Job parses the JDBC URL for host, port, and database. It cannot parse IPv6 addresses, a
comma-separated host list, or a Cloud SQL socket; set dbHost, dbPort, and dbName for those.
The Job recognizes only URLs that start with jdbc:postgresql:// or jdbc:mysql://. A
jdbc:mysql:loadbalance:// URL skips the Job entirely, so the server starts against an empty
database; apply the schema yourself, as described next.
The Job does not mount certificate files, so sslMode values that verify the server certificate,
verify-ca and verify-full for PostgreSQL or VERIFY_CA and VERIFY_IDENTITY for MySQL, fail.
require works.
Apply the schema yourself
Where a DBA applies DDL, turn the Job off:
gravitino:
schema:
enabled: false
With the Job off the chart neither creates nor upgrades the tables. Run the scripts from
/opt/gravitino/scripts in the server image the release runs, against the database you created:
on a fresh database the base schema, then the enterprise schema; on an upgrade, the
upgrade-{old_version}-to-{new_version} script for each version step in order, with the server
stopped.
| Engine | Base schema | Enterprise schema |
|---|---|---|
| PostgreSQL | postgresql/schema-{version}-postgresql.sql | enterprise/postgresql/enterprise-schema-{version}-postgresql.sql |
| MySQL | mysql/schema-{version}-mysql.sql | enterprise/mysql/enterprise-schema-{version}-mysql.sql |
Apply only the upgrade scripts to an existing database, never the base schema again. On PostgreSQL, re-running the base schema against a populated database fails.
High availability
Kubernetes restarts a server that stops, and several replicas keep query traffic answering meanwhile. Everything the servers know is in the metadata database, so availability depends on that database. Use a database service with automatic failover to a standby, such as Amazon RDS Multi-AZ or Aurora, Azure Database for PostgreSQL or MySQL with zone-redundant high availability, or Cloud SQL with high availability.
Point jdbcUrl at the service's endpoint name, which moves to the new primary on failover, not at
the address of one instance. The server reads and writes through that one address and does not use
read replicas.
During a failover, requests that need the database fail with an error and the readiness check reports the database as down. The server tests each connection before using it, so once the endpoint answers again it replaces the broken connections and carries on without a restart.
The same applies to each Iceberg catalog's JDBC database, since every commit to an Iceberg table goes there, and to the lineage database if you use lineage.
Backups
Back up the metadata database, and the lineage database if you use lineage. What else to back up, and how to restore, is covered in Backup and restore.
Verify the connection
The Settings tab under Configure in the UI shows the Entity store group, read only, with the URL, driver, and user the server is running with. The password key shows as Write-only.
When the server cannot reach its database on a new install or upgrade, the server container never
starts. The pod waits in its wait-for-schema init container until the schema Job succeeds, and
fails after gravitino.schema.wait.timeoutSeconds. A missing database, the wrong user or password,
and an unreachable host all show up in the schema Job's log:
kubectl -n "$NAMESPACE" logs -l app.kubernetes.io/component=schema-migrate --tail 100
kubectl -n "$NAMESPACE" logs "deployment/$RELEASE-gravitino" -c wait-for-schema
With the schema Job turned off, the server itself fails at startup when it cannot reach the database, and the pod restarts; the server log names the failure.
Once the server is running, the readiness endpoint probes the metadata database. A server that lost its
database returns 503 with an entityStore check in the DOWN state, and the pod goes unready:
kubectl -n "$NAMESPACE" port-forward "svc/$RELEASE-gravitino" 8090:8090 &
curl -s http://localhost:8090/api/health/ready
See Health and readiness for the response format and the probe timeout.
Related
- Install, for the values file and the install command these steps reuse
- Server configuration, for the connection pool and version retention settings that apply whichever database holds the metadata