# Deployment | OpenMetadata Installation & Setup Guide
Source: https://docs.open-metadata.org/v2.0.x/deployment
Deploy, configure, and manage OpenMetadata across environments with security and scaling guidance.
# From 0 to OpenMetadata in 5 minutes
We support different kinds of deployment:
## Quick Start
Choose how you want to try OpenMetadata
Try Sandbox
No setup · Hosted · Instant access
Explore OpenMetadata instantly with a hosted sandbox that mimics a real production setup no installation required.
Run locally with Docker
Local setup · Full control · Docker-based
Run OpenMetadata on your machine using Docker to get a hands-on experience with a real deployment.
- Prepare your environment with Docker and Docker Compose
- Launch OpenMetadata locally using the provided Docker Compose setup
- Operate and troubleshoot the deployment via UI and Docker commands
## Production
Deploy OpenMetadata securely and reliably for team and organization use.
Deploy OpenMetadata on AWS using Amazon EKS and AWS-managed services for production workloads.
Run OpenMetadata on Azure Kubernetes with Azure-native database and search services.
Deploy OpenMetadata on Google Cloud using GKE with managed infrastructure and services.
Deploy OpenMetadata on a self-managed, on-premises Kubernetes cluster.
Deploy OpenMetadata on Kubernetes using Helm charts for a scalable, production-ready setup.
Install and run OpenMetadata directly on physical or self-managed servers without Kubernetes.
## Production Configuration & Hardening
Production Ready Requirements
Understand hardware sizing, performance, and infrastructure requirements for production deployments.
Secure your OpenMetadata deployment with authentication, authorization, and secrets management.
## Upgrade
Before upgrading, review compatibility requirements and back up your metadata.
Safely upgrade your OpenMetadata deployment to the latest supported version with pre-checks and post-upgrade validation.
Understand supported upgrade paths, breaking changes, and version compatibility.
Back up your metadata before upgrading and restore it if needed.
# How to enable Azure Auth
Source: https://docs.open-metadata.org/v2.0.x/deployment/azure-auth
# AZURE resources on Postgres/MySQL Auth
[Azure Reference Doc](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-extensions#how-to-use-postgresql-extensions)
## Requirements
1. Azure Postgres or MySQL Cluster with auth enabled
2. User on DB Cluster with authentication enabled
## How to enable Azure Auth on postgresql
Set the environment variables
```Commandline theme={null}
DB_PARAMS="azure=true&allowPublicKeyRetrieval=true&sslmode=require&serverTimezone=UTC"
DB_USER_PASSWORD=none
```
Either through helm (if deployed in kubernetes) or as env vars.
The `DB_USER_PASSWORD` is still required and cannot be empty. Set it to a random/dummy string.
# Azure - Enable Passwordless Database Backend Connection
Source: https://docs.open-metadata.org/v2.0.x/deployment/azure-passwordless-auth
# Azure - Enable Passwordless Database Backend Connection
By Default, OpenMetadata supports basic authentication when connecting to MySQL/PostgreSQL as Database backend. With Azure, you can enhance the security for configuring Database configurations other the basic authentication mechanism.
This guide will help you setup the application to use passwordless approach for Azure PaaS Databases (preferrably [Azure Database for PostgreSQL - Flexible Server](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/service-overview) and [Azure Database for MySQL - Flexible Server](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/overview)).
## Prerequisites
This guide requires the following prerequisites -
* Azure Database Flexible Server enabled with Microsoft Entra authentication
* [Azure Managed Identities](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)
* Azure Kubernetes Service (Enabled with Workload Identity) or Azure VM
* OpenMetadata Application Version `1.5.9` and higher
If you are looking to enable Passwordless Database Backend Configuration on Existing OpenMetadata Application hosted using Azure Cloud, you need to create perform the following prerequisites -
* Create Managed Identity from Azure Portal
* Create a SQL User for Managed Identity in Azure Databases
* PostgreSQL Reference link [here](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/how-to-manage-azure-ad-users#create-a-userrole-using-microsoft-entra-principal-name)
* MySQL Reference link [here](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-azure-ad#create-microsoft-entra-users-in-azure-database-for-mysql)
* Assign Existing OpenMetadata Database Tables Ownership to Managed Identities created in above step
## Enabling Passwordless connections with OpenMetadata
Configure your Helm Values for Kubernetes Deployment like below -
```yaml theme={null}
# For PostgreSQL
commonLabels:
azure.workload.identity/use: "true"
serviceAccount:
create: true
annotations:
azure.workload.identity/client-id:
name: "openmetadata-sa"
automountServiceAccountToken: true
openmetadata:
config:
database:
host:
driverClass: org.postgresql.Driver
dbParams: "azure=true&allowPublicKeyRetrieval=true&serverTimezone=UTC&sslmode=require&authenticationPluginClassName=com.azure.identity.extensions.jdbc.postgresql.AzurePostgresqlAuthenticationPlugin"
dbScheme: postgresql
port: 5432
auth:
username:
password:
secretRef: database-secrets
secretKey: openmetadata-database-password
databaseName:
# For MySQL
commonLabels:
azure.workload.identity/use: "true"
serviceAccount:
create: true
annotations:
azure.workload.identity/client-id:
name: "openmetadata-sa"
automountServiceAccountToken: true
openmetadata:
config:
database:
host:
driverClass: com.mysql.cj.jdbc.Driver
dbParams: "azure=true&allowPublicKeyRetrieval=trueserverTimezone=UTC&sslMode=REQUIRED&defaultAuthenticationPlugin=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin"
dbScheme: mysql
port: 3306
auth:
username:
password:
secretRef: database-secrets
secretKey: openmetadata-database-password
databaseName:
```
In the above code snippet, the Database Credentials (Auth Password Kubernetes Secret) is still required and cannot be empty. Set it to dummy / random value.
Install / Upgrade your Helm Release with the following command -
```bash theme={null}
helm repo update open-metadata
helm upgrade --install openmetadata open-metadata/openmetadata --values
```
For further reference, checkout the official documentation available in the below links -
* [MySQL](https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/migrate-mysql-to-passwordless-connection?tabs=sign-in-azure-cli%2Cjava%2Capp-service)
* [PostgreSQL](https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/migrate-postgresql-to-passwordless-connection?tabs=sign-in-azure-cli%2Cjava%2Capp-service%2Cassign-role-service-connector)
# Bare Metal Deployment | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/bare-metal
Deploy the platform on bare-metal servers to maintain full control over infrastructure, authentication, and network configuration.
# Deploy on Bare Metal
Requirements This guide assumes you have access to a command-line environment or shell such as bash, zsh, etc. or Linux
or Mac OS X or PowerShell on Microsoft Windows. This guide also assumes that your command-line environment has access to
the tar utility. Please review additional requirements listed in the subsections below.
## Java (version 21.0.0)
OpenMetadata is built using Java, DropWizard, and Jetty.
Type the following command to verify that you have a supported version of the Java runtime installed.
```commandline theme={null}
java --version
```
To install Java or upgrade to Java 21, see the instructions for your operating system at [How do I install
Java?](https://java.com/en/download/help/download_options.html#mac).
## MySQL (version 8.0.42 or higher)
To install MySQL see the instructions for your operating system (OS) at [Installing and Upgrading MySQL](https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/installing.html)
or visit one of the following OS-specific guides.
* [Installing MySQL on Linux](https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/linux-installation.html)
* [Installing MySQL on Windows](https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/windows-installation.html)
* [Installing MySQL on MacOS](https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/macos-installation.html)
Make sure to configure required databases and users for OpenMetadata.
You can refer a sample script [here](https://github.com/open-metadata/OpenMetadata/blob/main/docker/mysql/mysql-script.sql).
## Postgres (version 15 or higher)
To install Postgres see the instructions for your operating system (OS) at [Postgres Download](https://www.postgresql.org/download/)
Make sure to configure required databases and users for OpenMetadata.
You can refer a sample script [here](https://github.com/open-metadata/OpenMetadata/blob/main/docker/postgresql/postgres-script.sql).
## Elasticsearch / OpenSearch
OpenMetadata supports ElasticSearch version 9.x (minimum 9.0.0, recommended 9.3.0) and OpenSearch version 3.x (minimum 3.0.0, recommended 3.3.0). The 9.x Elasticsearch client is not compatible with 8.x or older servers. To install or upgrade Elasticsearch to a supported version please see the instructions for your operating system at
[Installing ElasticSearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html).
Please follow the instructions here to [install ElasticSearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html).
If you are using AWS OpenSearch Service, OpenMetadata supports AWS OpenSearch Service engine version 3.x (minimum 3.0.0, recommended 3.3.0). Note that AWS OpenSearch Service currently supports up to OpenSearch 3.3. For more information on AWS OpenSearch Service, please visit the official docs [here](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html).
## Airflow or other workflow schedulers
OpenMetadata performs metadata ingestion using the Ingestion Framework. Learn more about how to deploy and manage
the ingestion workflows [here](/v2.0.x/deployment/ingestion).
OpenMetadata versions have specific Airflow compatibility requirements to ensure seamless metadata ingestion. OpenMetadata 1.5 supports Airflow 2.9, 1.6.4 supports Airflow 2.9.3, and 1.6.5 supports Airflow 2.10.5. Ensure that your Airflow version aligns with your OpenMetadata deployment to maintain stability and functionality.
## Minimum Sizing Requirements
* Our minimum specs recommendation for the OpenMetadata Deployment (one replica) is 2 vCPUs and 4 Gigs with 20 Gigs of volume size if using persistent volumes for logs.
* For Elasticsearch, 2 vCPUs and 2 Gigs RAM (per instance) with 30 Gigs of Storage volume attached.
* For the database, 2 vCPUs and 2 Gigs RAM (per instance) with 30 Gigs of Storage Volume Attached (dynamic expansion up to 100 Gigs).
These settings apply as well when using managed instances, such as RDS or AWS OpenSearch.
## Procedure
## 1. Download the distribution
Visit the [releases page](https://github.com/open-metadata/OpenMetadata/releases/latest) and download the latest binary release.
Release binaries follow the naming convention of `openmetadata-x.y.z.tar.gz`. Where `x`, `y`, and `z` represent the
major, minor, and patch release numbers.
## 2. Untar the release download
Once the tar file has downloaded, run the following command, updated if necessary for the version of OpenMetadata that you downloaded.
```commandline theme={null}
tar -zxvf openmetadata-*.tar.gz
```
## 3. Navigate to the directory created
```commandline theme={null}
cd openmetadata-*
```
Review and update the `openmetadata.yaml` configurations to match your environment. Specifically, consider aspects such
as the connection to the MySQL database or ElasticSearch. You can find more information about these configurations
[here](/v2.0.x/deployment/configuration).
## 4. Prepare the OpenMetadata Database and Indexes
The command below will generate all the necessary tables and indexes in ElasticSearch.
Note that if there's any data in that database, this command will drop it!
```commandline theme={null}
./bootstrap/openmetadata-ops.sh drop-create
```
## 5. Start OpenMetadata
```commandline theme={null}
./bin/openmetadata.sh start
```
We recommend configuring `serviced` to monitor the OpenMetadata command to restart in case of any failures.
## Run OpenMetadata with a load balancer
You may put one or more OpenMetadata instances behind a load balancer for reverse proxying.
To do this you will need to add one or more entries to the configuration file for your reverse proxy.
### Apache mod\_proxy
To use the Apache mod\_proxy module as a reverse proxy for load balancing, update the VirtualHost tag in your
Apache config file to resemble the following.
```xml theme={null}
BalancerMember http://127.0.0.1:8585
BalancerMember http://127.0.0.2:8686
ProxyPreserveHost On
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
```
### Nginx
To use OpenMetadata behind an Nginx reverse proxy, add an entry resembling the following the http context of your Nginx
configuration file for each OpenMetadata instance.
```commandline theme={null}
server {
access_log /var/log/nginx/stage-reverse-access.log;
error_log /var/log/nginx/stage-reverse-error.log;
server_name stage.open-metadata.org;
location / {
proxy_pass http://127.0.0.1:8585;
}
}
```
## Run OpenMetadata with AWS Services or your hosted DB/ElasticSearch
If you are running OpenMetadata in AWS, it is recommended to use [Amazon RDS](https://docs.aws.amazon.com/rds/index.html) and [Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/?id=docs_gateway).
We support
* Amazon RDS (MySQL) engine version 8 or higher
* Amazon OpenSearch (ElasticSearch) engine version 9.x (minimum 9.0.0, recommended 9.3.0) or Amazon OpenSearch engine version 3.x (minimum 3.0.0, recommended 3.3.0)
* Amazon RDS (PostgreSQL) engine version 15 or higher
For Production Systems, we recommend Amazon RDS to be in Multiple Availability Zones. For Amazon OpenSearch (or ElasticSearch) Service, we recommend Multiple Availability Zones with minimum 3 Master Nodes.
Once you have the RDS and OpenSearch Services Setup, you can update the environment variables below for OpenMetadata bare metal systems to connect with Database and ElasticSearch.
Below are the environment variables for OpenMetadata Server
### Configure MySQL connection
```
# MySQL Environment Variables
DB_DRIVER_CLASS='com.mysql.cj.jdbc.Driver'
DB_SCHEME='mysql'
DB_PARAMS='allowPublicKeyRetrieval=true&useSSL=true&serverTimezone=UTC'
DB_USER=''
DB_USER_PASSWORD=''
DB_HOST=''
DB_PORT=''
OM_DATABASE=''
```
### Configure Postgres Connection
```
# Postgres Environment Variables
DB_DRIVER_CLASS='org.postgresql.Driver'
DB_SCHEME='postgresql'
DB_PARAMS='allowPublicKeyRetrieval=true&useSSL=true&serverTimezone=UTC'
DB_USER=''
DB_USER_PASSWORD=''
DB_HOST=''
DB_PORT=''
OM_DATABASE=''
```
### Configure ElasticSearch Connection
```
ELASTICSEARCH_SOCKET_TIMEOUT_SECS='60'
ELASTICSEARCH_USER=''
ELASTICSEARCH_CONNECTION_TIMEOUT_SECS='5'
ELASTICSEARCH_PORT='443'
ELASTICSEARCH_SCHEME='https'
ELASTICSEARCH_BATCH_SIZE='10'
ELASTICSEARCH_HOST='vpc-..es.amazonaws.com'
ELASTICSEARCH_PASSWORD=''
ELASTICSEARCH_CLUSTER_ALIAS=''
```
### Configure OpenSearch
```
# ElasticSearch Configurations
SEARCH_TYPE="opensearch"
ELASTICSEARCH_HOST=""
ELASTICSEARCH_PORT=""
ELASTICSEARCH_SCHEME=""
ELASTICSEARCH_USER=""
ELASTICSEARCH_PASSWORD=""
ELASTICSEARCH_CLUSTER_ALIAS=""
```
If you want to separate indexes for production and non-production environments, you can set the `clusterAlias` in the configuration file.
### Configure Ingestion
```
PIPELINE_SERVICE_CLIENT_ENDPOINT=""
PIPELINE_SERVICE_CLIENT_HEALTH_CHECK_INTERVAL="300"
SERVER_HOST_API_URL="/api"
PIPELINE_SERVICE_CLIENT_VERIFY_SSL="no-ssl"
PIPELINE_SERVICE_CLIENT_SSL_CERT_PATH=""
PIPELINE_SERVICE_CLIENT_CLASS_NAME="org.openmetadata.service.clients.pipeline.airflow.AirflowRESTClient"
PIPELINE_SERVICE_IP_INFO_ENABLED="false"
PIPELINE_SERVICE_CLIENT_HOST_IP=""
PIPELINE_SERVICE_CLIENT_SECRETS_MANAGER_LOADER="noop"
AIRFLOW_USERNAME=""
AIRFLOW_PASSWORD=""
AIRFLOW_TIMEOUT="10"
AIRFLOW_TRUST_STORE_PATH=""
AIRFLOW_TRUST_STORE_PASSWORD=""
```
When setting up environment file if your custom password includes any special characters then make sure to follow the steps [here](https://github.com/open-metadata/OpenMetadata/issues/12110#issuecomment-1611341650).
## Troubleshooting
### Java Memory Heap Issue
If your openmetadata application logs speaks about the below issue -
```
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "AsyncAppender-Worker-async-file-appender"
Exception in thread "pool-5-thread-1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-file-appender" java.lang.OutOfMemoryError: Java heap space
Exception in thread "dw-46" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-console-appender" java.lang.OutOfMemoryError: Java heap space
```
This is due to the default JVM Heap Space configuration (1 GiB) being not enough for your workloads. In order to resolve this issue, head over to your openmetadata environment variables list and append the below environment variable
```
# environment variable file (either .bash_profile or .bashrc or add in conf/openmetadata-env.sh in release binaries)
export OPENMETADATA_HEAP_OPTS="-Xmx2G -Xms2G"
```
The flag `Xmx` specifies the maximum memory allocation pool for a Java virtual machine (JVM), while `Xms` specifies the initial memory allocation pool.
Restart the OpenMetadata Application using `./bin/openmetadata.sh start` which will start the service using a linux process.
## Enable Security
Please follow our [Enable Security Guide](/v2.0.x/deployment/bare-metal/security) to configure security for your OpenMetadata
installation.
# Enable Security | OpenMetadata Deployment Security Guide
Source: https://docs.open-metadata.org/v2.0.x/deployment/bare-metal/security
Secure bare-metal deployments with guidance on encryption, authentication, and secret management for non-cloud environments.
# Bare Metal Security
Follow the steps for setting up the SSO, and then check the specific `Bare Metal` section of your chosen SSO.
By default, Basic Authentication will be enabled as authentication mechanism.
Configure Basic Authentication to access the UI and APIs
Configure Ldap Authentication to access the UI and APIs
Configure Auth0 SSO to access the UI and APIs
Configure Azure SSO to access the UI and APIs
Configure a Custom OIDC SSO to access the UI and APIs
Configure Google SSO to access the UI and APIs
Configure Okta SSO to access the UI and APIs
Configure Amazon Cognito SSO to access the UI and APIs
Configure OneLogin SSO to access the UI and APIs
Configure Keycloak SSO to access the UI and APIs
# Configuring OpenMetadata to Run Under a Subpath
Source: https://docs.open-metadata.org/v2.0.x/deployment/bare-metal/subpath
## Subpath in OpenMetadata
To configure **OpenMetadata** to operate under a subpath (for example `/openmetadata`), useful when deploying behind a reverse proxy or load balancer, you need to adjust specific settings in the `openmetadata.yaml` configuration file.
**`BASE_PATH` must not have a trailing slash**, but `basePath` in `openmetadata.yaml` needs one. OpenMetadata builds static asset URLs by appending directly to `basePath` with no separator, so if it's missing the trailing slash, requests resolve to `/openmetadataassets/...` instead of `/openmetadata/assets/...` and every static asset 404s.
## Configuration Steps
### 1. Set the Base Path
Define the `basePath` parameter to configure the application's root context, and ensure that the `publicKeyUrl` is updated accordingly to reflect the new base path.
This sets the root context for the application. Note the trailing slash on `basePath`:
```yaml theme={null}
basePath: ${BASE_PATH:-/openmetadata}/
```
This configuration sets the base path to /openmetadata by default. You can override it by setting the BASE\_PATH environment variable: set `BASE_PATH` itself **without** a trailing slash (e.g., `BASE_PATH=/openmetadata`), since the `/` above is appended for you.
### 2. Configure Web Paths
Configure the web application and API endpoint paths to align with the specified base path. These settings live under the top-level `server:` block:
```yaml theme={null}
server:
applicationContextPath: ${BASE_PATH:-/openmetadata}
rootPath: ${BASE_PATH:-/openmetadata}api/*
```
* `applicationContextPath`: Defines the context path for the web application.
* `rootPath`: Specifies the root path for API endpoints. [GitHub](https://github.com/open-metadata/OpenMetadata/discussions/17954)
### 3. Set Asset Paths
Ensure that asset paths are correctly prefixed with the base path.
```yaml theme={null}
assets:
resourcePath: /openmetadata/assets/
uriPath: ${BASE_PATH:-/openmetadata}
```
* `resourcePath`: Path to static resources.
* `uriPath`: URI path prefix for assets.
## Example Configuration
Here's how the relevant section of your `openmetadata.yaml` might look:
```yaml theme={null}
basePath: ${BASE_PATH:-/openmetadata}/
publicKeyUrl: ${BASE_PATH:-/}api/v1/system/config/jwks
server:
applicationContextPath: ${BASE_PATH:-/openmetadata}
rootPath: ${BASE_PATH:-/openmetadata}api/*
assets:
resourcePath: /openmetadata/assets/
uriPath: ${BASE_PATH:-/openmetadata}
```
## Deployment Considerations
* **Reverse Proxy Configuration**: Ensure that your reverse proxy (e.g., NGINX, Apache) is configured to forward requests to the OpenMetadata application with the correct subpath.
* **Environment Variables**: You can override the default base path by setting the BASE\_PATH environment variable in your deployment environment. Ensure that related parameters such as basePath, applicationContextPath, rootPath, and publicKeyUrl are updated to reflect this change.
* **Static Assets**: Verify that static assets are accessible under the new subpath to prevent broken links or missing resources.
# Server Configuration Reference | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/configuration
Configure your platform deployment using environmental settings, secret values, service parameters, and performance tuning options.
# Server Configuration Reference
This document describes OpenMetadata Server Configuration
```yaml theme={null}
swagger:
resourcePackage: org.openmetadata.service.resources
server:
rootPath: '/api/*'
applicationConnectors:
- type: http
port: 8585
adminConnectors:
- type: http
port: 8586
# Logging settings.
# https://logback.qos.ch/manual/layouts.html#conversionWord
logging:
level: INFO
loggers:
org.openmetadata.service.common: DEBUG
io.swagger: ERROR
appenders:
- type: file
threshold: TRACE
logFormat: "%level [%d{HH:mm:ss.SSS}] [%t] %logger{5} - %msg %n"
currentLogFilename: ./logs/openmetadata.log
archivedLogFilenamePattern: ./logs/openmetadata-%d{yyyy-MM-dd}-%i.log.gz
archivedFileCount: 7
timeZone: UTC
maxFileSize: 50MB
database:
# the name of the JDBC driver, mysql in our case
driverClass: com.mysql.cj.jdbc.Driver
# the username and password
user: openmetadata_user
password: openmetadata_password
# the JDBC URL; the database is called openmetadata_db
url: jdbc:mysql://localhost/openmetadata_db?useSSL=false&serverTimezone=UTC
elasticsearch:
host: localhost
port: 9200
eventHandlerConfiguration:
eventHandlerClassNames:
- "org.openmetadata.service.events.AuditEventHandler"
health:
delayedShutdownHandlerEnabled: true
shutdownWaitPeriod: 1s
healthCheckUrlPaths: ["/api/v1/health-check"]
healthChecks:
- name: UserDatabaseCheck
critical: true
schedule:
checkInterval: 2500ms
downtimeInterval: 10s
failureAttempts: 2
successAttempts: 1
```
## Server Port
```yaml theme={null}
server:
rootPath: '/api/*'
applicationConnectors:
- type: http
port: 8585
adminConnectors:
- type: http
port: 8586
```
By default, the OpenMetadata server runs on port 8585. It uses Jetty Server. The above config can be changed to make it
run on a different port. Once you have updated the port details in config restart the server.
## Database
OpenMetadata supports MySQL or Postgres as the database. The database configurations and connection strings must be as
specified below.
### MySQL Configuration
We recommend you create a MySQL user with a strong password and update this section accordingly.
| Parameter | Description | Default Value |
| --------------- | --------------------------- | ------------------------------------------------------------------------ |
| **driverClass** | The name of the JDBC driver | `com.mysql.cj.jdbc.Driver` |
| **user** | MySQL database username | `openmetadata_user` |
| **password** | MySQL database password | `openmetadata_password` |
| **url** | JDBC URL connection string | `jdbc:mysql://localhost/openmetadata_db?useSSL=false&serverTimezone=UTC` |
```yaml theme={null}
database:
driverClass: com.mysql.cj.jdbc.Driver
user: openmetadata_user
password: openmetadata_password
url: jdbc:mysql://localhost/openmetadata_db?useSSL=false&serverTimezone=UTC
```
### PostgreSQL Configuration
OpenMetadata uses stored generated columns (supported since Postgres 12). We recommend running Postgres 15 or higher. Create a Postgres user with a strong password and update this section accordingly.
| Parameter | Description | Default Value |
| --------------- | ---------------------------- | ---------------------------------------------------------------------------------- |
| **driverClass** | The name of the JDBC driver | `org.postgresql.Driver` |
| **user** | PostgreSQL database username | `openmetadata_user` |
| **password** | PostgreSQL database password | `openmetadata_password` |
| **url** | JDBC URL connection string | `jdbc:postgresql://localhost:5432/openmetadata_db?useSSL=false&serverTimezone=UTC` |
```yaml theme={null}
database:
driverClass: org.postgresql.Driver
user: openmetadata_user
password: openmetadata_password
url: jdbc:postgresql://localhost:5432/openmetadata_db?useSSL=false&serverTimezone=UTC
```
## ElasticSearch
```yaml theme={null}
elasticsearch:
host: localhost
port: 9200
```
ElasticSearch is one of the pre-requisites to run OpenMetadata. Default configuration expects a single instance of
ElasticSearch running on the local machine. Please make sure you update it with your production elastic search.
## Event Handlers
```yaml theme={null}
eventHandlerConfiguration:
eventHandlerClassNames:
- "org.openmetadata.service.events.AuditEventHandler"
```
EventHandler configuration is optional. It will update the AuditLog in MySQL DB and also ElasticSearch indexes whenever
any entity is updated either through UI or API interactions. We recommend you leave it there as it enhances the user
experience.
## Healthcheck
```yaml theme={null}
health:
delayedShutdownHandlerEnabled: true
shutdownWaitPeriod: 1s
healthCheckUrlPaths: ["/api/v1/health-check"]
healthChecks:
- name: UserDatabaseCheck
critical: true
schedule:
checkInterval: 2500ms
downtimeInterval: 10s
failureAttempts: 2
successAttempts: 1
```
Healthcheck API provides an API endpoint to check the OpenMetadata server health. We recommend in production settings to
use this API to monitor the health of your OpenMetadata instance. Please tune the above configuration according to your
production needs.
## Security
Please follow our [Enable Security Guide](/v2.0.x/deployment/security) to configure security for your OpenMetadata installation.
# Database Connection Pooling
Source: https://docs.open-metadata.org/v2.0.x/deployment/database-connection-pooling
Optimize your OpenMetadata deployment with database connection pooling. Learn configuration best practices, performance tuning, and setup guides.
# Database Connection Pool
Dropwizard JDBI provides connection pooling by default. Enabling and properly configuring connection pooling ensures that each database query does not open a new connection but instead utilizes a pool of reusable connections. This enhances application performance, reduces latency, and efficiently manages database resources.
Database connection pooling is a technique used to maintain a cache of database connections that can be reused for future requests. This approach minimizes the overhead associated with establishing a new connection for each request, leading to improved performance and resource utilization.
In the context of Dropwizard JDBI, enabling and configuring connection pooling ensures that your application can handle multiple database operations efficiently, especially under high load conditions.
* [Why Use a Database Connection Pool?](#why-use-a-database-connection-pool)
* [Configuration Parameters](#configuration-parameters)
* [Best Practices](#best-practices-for-database-connection-pooling)
## Why Use a Database Connection Pool?
* **Performance Improvement**: Reusing existing connections reduces the time required to establish new connections, leading to faster query execution.
* **Resource Optimization**: Limits the number of open connections to the database, preventing resource exhaustion.
* **Scalability**: Efficiently handles increasing numbers of database requests by managing connections effectively.
* **Stability**: Reduces the risk of connection timeouts and failures by maintaining a pool of healthy connections.
## Configuration Parameters
The following configuration parameters control the behavior of the database connection pool in Dropwizard JDBI:
| Parameter Name | Description | Environment Variable | Default Value |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------------- |
| **maxSize** | Specifies the maximum number of connections that can be active in the pool at any given time. Determines the upper limit of concurrent database connections. Should be set based on expected workload and database server capabilities. | `DB_CONNECTION_POOL_MAX_SIZE` | `50` |
| **minSize** | Defines the minimum number of idle connections that the pool tries to maintain. Ensures connections are always ready to serve incoming requests and helps reduce latency for initial requests after periods of inactivity. | `DB_CONNECTION_POOL_MIN_SIZE` | `10` |
| **initialSize** | Sets the number of connections created when the pool is initialized. Determines how many connections are available immediately after startup. Typically aligned with `minSize` for consistency. | `DB_CONNECTION_POOL_INITIAL_SIZE` | `10` |
| **checkConnectionWhileIdle** | Indicates whether idle connections should be validated periodically to ensure they are still alive. Helps detect and remove stale or broken connections from the pool, maintaining pool health over time. | `DB_CONNECTION_CHECK_CONNECTION_WHILE_IDLE` | `true` |
| **checkConnectionOnBorrow** | Determines whether a connection should be validated before being handed over to a client. Ensures clients receive only valid and live connections, preventing runtime errors from broken connections. | `DB_CONNECTION_CHECK_CONNECTION_ON_BORROW` | `true` |
| **evictionInterval** | Specifies the interval at which idle connections are checked and evicted if necessary. Controls how frequently the pool checks for idle connections to remove. Works with `minIdleTime` to maintain optimal pool size. | `DB_CONNECTION_EVICTION_INTERVAL` | `5 minutes` |
| **minIdleTime** | Defines the minimum amount of time a connection can remain idle before it is eligible for eviction. Helps balance resource usage by removing unnecessary idle connections. | `DB_CONNECTION_MIN_IDLE_TIME` | `1 minute` |
```yaml theme={null}
maxSize: ${DB_CONNECTION_POOL_MAX_SIZE:-50}
minSize: ${DB_CONNECTION_POOL_MIN_SIZE:-10}
initialSize: ${DB_CONNECTION_POOL_INITIAL_SIZE:-10}
checkConnectionWhileIdle: ${DB_CONNECTION_CHECK_CONNECTION_WHILE_IDLE:-true}
checkConnectionOnBorrow: ${DB_CONNECTION_CHECK_CONNECTION_ON_BORROW:-true}
evictionInterval: ${DB_CONNECTION_EVICTION_INTERVAL:-5 minutes}
minIdleTime: ${DB_CONNECTION_MIN_IDLE_TIME:-1 minute}
```
## Best Practices for Database Connection Pooling
To ensure that your database connection pooling is optimized for performance, reliability, and resource management, consider the following best practices:
## 1. Monitor and Adjust
* **Regular Monitoring**: Continuously monitor your application’s performance and database load to understand how your connection pool is performing.
* **Adjust Pool Sizes**: Based on monitoring data, adjust the pool size parameters (`maxSize`, `minSize`, `initialSize`) to match your workload needs. This helps in avoiding resource bottlenecks or wastage.
## 2. Understand Workload Patterns
* **Peak vs. Off-Peak**: Identify peak and off-peak usage times in your application. Configure your pool to handle peak loads effectively while conserving resources during off-peak times.
* **Dynamic Scaling**: Consider implementing dynamic scaling of the connection pool size if your environment supports it. This allows the pool to grow and shrink in response to actual demand.
## 3. Ensure Database Capacity
* **Max Connections**: Verify that your database server can support the maximum number of connections specified by `maxSize`. Exceeding the database’s capacity can lead to connection failures and degraded performance.
* **Avoid Over-Configuration**: Setting `maxSize` too high can overwhelm your database, while setting it too low can result in connection shortages under high load. Balance is key.
## 4. Use Validation Checks
* **Enable `checkConnectionWhileIdle`**: This ensures that idle connections are periodically validated, preventing broken connections from remaining in the pool. It improves the reliability of the connection pool.
* **Enable `checkConnectionOnBorrow`**: Validate connections before handing them over to the application. This reduces the risk of runtime errors due to stale or broken connections.
## 5. Configure Timeouts Appropriately
* **Eviction Interval**: Set `evictionInterval` to an appropriate value that balances the frequency of idle connection checks with the overhead of performing these checks.
* **Idle Time**: Adjust `minIdleTime` based on typical usage patterns. This ensures that connections are retained for as long as they are likely to be needed, without wasting resources on keeping them idle for too long.
## 6. Consider Connection Pool Size Based on Application Behavior
* **Transactional Applications**: For applications with short, frequent transactions, a larger pool size might be necessary to handle the high concurrency.
* **Long-Lived Connections**: If your application tends to hold connections open for extended periods, ensure that your pool is large enough to accommodate other incoming requests without running out of connections.
## 7. Review and Test Configuration Changes
* **Staging Environment Testing**: Before deploying changes to production, test any configuration changes in a staging environment that closely mirrors production. This helps to catch potential issues early.
* **Review Logs and Metrics**: After making changes, review your application logs and database metrics to ensure that the new configuration is performing as expected.
## 8. Use Connection Pooling Libraries Effectively
* **Leverage Built-In Features**: Use features provided by your connection pooling library, such as connection leak detection, to ensure optimal usage of your pool.
* **Stay Updated**: Keep your connection pooling library up to date with the latest versions to benefit from performance improvements and security fixes.
By following these best practices, you can ensure that your database connection pool is configured for optimal performance, reliability, and resource efficiency, resulting in a more stable and responsive application.
# Docker Deployment | OpenMetadata Container Setup
Source: https://docs.open-metadata.org/v2.0.x/deployment/docker
Deploy the platform using Docker containers to simplify setup, scaling, and local testing without needing external dependencies.
# Docker Deployment
This guide will help you set up the OpenMetadata Application using Docker Deployment.
Before starting with the deployment make sure you follow all the below Prerequisites.
## Docker Deployment Architecture
## Prerequisites
### Configure OpenMetadata to use External Database and Search Engine
For Production Deployment using Docker, we recommend bringing your own Databases and ElasticSearch Engine and not rely on quickstart packages.
### Configure External Orchestrator Service (Ingestion Service)
OpenMetadata requires connectors to be scheduled to periodically fetch the metadata, or you can use the OpenMetadata APIs to push the metadata as well
1. OpenMetadata Ingestion Framework is flexible to run on any orchestrator. However, we built an ability to deploy and manage connectors as pipelines from the UI. This requires the Airflow container we ship.
2. If your team prefers to run on any other orchestrator such as prefect, dagster or even GitHub workflows. Please refer to our recent webinar on [How Ingestion Framework works](https://www.youtube.com/watch?v=i7DhG_gZMmE\&list=PLa1l-WDhLreslIS_96s_DT_KdcDyU_Itv\&index=10)
### Docker (version 20.10.0 or higher)
[Docker](https://docs.docker.com/get-started/overview/) is an open-source platform for developing, shipping, and running applications. It enables you to separate your applications from your infrastructure, so you can deliver software quickly using OS-level virtualization. It helps deliver software in packages called Containers.
To check what version of Docker you have, please use the following command.
```commandline theme={null}
docker --version
```
If you need to install Docker, please visit [Get Docker](https://docs.docker.com/get-docker/).
### Docker Compose (version v2.2.3 or greater)
The Docker compose package enables you to define and run multi-container Docker applications. The compose command integrates compose functions into the Docker platform, making them available from the Docker command-line interface ( CLI). The Python packages you will install in the procedure below use compose to deploy OpenMetadata.
* **MacOS X**: Docker on MacOS X ships with compose already available in the Docker CLI.
* **Linux**: To install compose on Linux systems, please visit the Docker CLI command documentation and follow the
instructions.
To verify that the docker compose command is installed and accessible on your system, run the following command.
```commandline theme={null}
docker compose version
```
Upon running this command you should see output similar to the following.
```commandline theme={null}
Docker Compose version v2.2.3
```
#### Install Docker Compose Version 2 on Linux
Follow the [Docker Compose installation instructions](https://docs.docker.com/compose/install/linux/) to install Docker Compose version 2.
1. Run the following command to download the current stable release of Docker Compose
```
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o
$DOCKER_CONFIG/cli-plugins/docker-compose
```
This command installs Compose V2 for the active user under \$HOME directory. To install Docker Compose for all users
on your system, replace` ~/.docker/cli-plugins` with `/usr/local/lib/docker/cli-plugins`.
2. Apply executable permissions to the binary
```
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
```
3. Test your installation
```
docker compose version
> Docker Compose version v2.2.3
```
## Steps for Deploying OpenMetadata using Docker
### 1. Create a directory for OpenMetadata
Create a new directory for OpenMetadata and navigate into that directory.
```commandline theme={null}
mkdir openmetadata-docker && cd openmetadata-docker
```
### 2. Download Docker Compose Files from GitHub Releases
Download the Docker Compose files from the [Latest GitHub Releases](https://github.com/open-metadata/OpenMetadata/releases/latest).
The Docker compose file name will be `docker-compose-openmetadata.yml`.
This docker compose file contains only the docker compose services for OpenMetadata Server. Bring up the dependencies as mentioned in the [prerequisites](#configure-openmetadata-to-use-external-database-and-search-engine) section.
You can also run the below command to fetch the docker compose file directly from the terminal -
```bash theme={null}
wget https://github.com/open-metadata/OpenMetadata/releases/download/2.0.1-release/docker-compose-openmetadata.yml
```
### 3. Update Environment Variables required for OpenMetadata Dependencies
In the previous [step](#2-download-docker-compose-files-from-github-releases), we download the `docker-compose` file.
Identify and update the environment variables in the file to prepare openmetadata configurations.
For MySQL Configurations, update the below environment variables -
```bash theme={null}
...
# Database configuration for MySQL
DB_DRIVER_CLASS="com.mysql.cj.jdbc.Driver"
DB_SCHEME="mysql"
DB_PARAMS="allowPublicKeyRetrieval=true&useSSL=true&serverTimezone=UTC"
DB_USER=""
DB_USER_PASSWORD=""
DB_HOST=""
DB_PORT=""
OM_DATABASE=""
```
For ElasticSearch Configurations, update the below environment variables -
```bash theme={null}
# ElasticSearch Configurations
SEARCH_TYPE="elasticsearch"
ELASTICSEARCH_HOST=""
ELASTICSEARCH_PORT=""
ELASTICSEARCH_SCHEME=""
ELASTICSEARCH_USER=""
ELASTICSEARCH_PASSWORD=""
ELASTICSEARCH_CLUSTER_ALIAS=""
```
For OpenSearch Configurations, update the below environment variables -
```bash theme={null}
# ElasticSearch Configurations
SEARCH_TYPE="opensearch"
ELASTICSEARCH_HOST=""
ELASTICSEARCH_PORT=""
ELASTICSEARCH_SCHEME=""
ELASTICSEARCH_USER=""
ELASTICSEARCH_PASSWORD=""
ELASTICSEARCH_CLUSTER_ALIAS=""
```
If you want to separate indexes for production and non-production environments, you can set the `clusterAlias` in the configuration file.
For Ingestion Configurations, update the below environment variables -
```bash theme={null}
PIPELINE_SERVICE_CLIENT_ENDPOINT=""
PIPELINE_SERVICE_CLIENT_HEALTH_CHECK_INTERVAL="300"
SERVER_HOST_API_URL="/api"
PIPELINE_SERVICE_CLIENT_VERIFY_SSL="no-ssl"
PIPELINE_SERVICE_CLIENT_SSL_CERT_PATH=""
PIPELINE_SERVICE_CLIENT_CLASS_NAME="org.openmetadata.service.clients.pipeline.airflow.AirflowRESTClient"
PIPELINE_SERVICE_IP_INFO_ENABLED="false"
PIPELINE_SERVICE_CLIENT_HOST_IP=""
PIPELINE_SERVICE_CLIENT_SECRETS_MANAGER_LOADER="noop"
AIRFLOW_USERNAME=""
AIRFLOW_PASSWORD=""
AIRFLOW_TIMEOUT="10"
AIRFLOW_TRUST_STORE_PATH=""
AIRFLOW_TRUST_STORE_PASSWORD=""
```
When setting up environment file if your custom password includes any special characters then make sure to follow the steps [here](https://github.com/open-metadata/OpenMetadata/issues/12110#issuecomment-1611341650).
### 4. Start the Docker Compose Services
Run the below command to deploy the OpenMetadata -
```bash theme={null}
docker compose --env-file ./env-mysql up --detach
```
You can validate that all containers are up by running with command `docker ps`.
```commandline theme={null}
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
470cc8149826 openmetadata/server:2.0.1 "./openmetadata-star…" 45 seconds ago Up 43 seconds 3306/tcp, 9200/tcp, 9300/tcp, 0.0.0.0:8585-8586->8585-8586/tcp openmetadata_server
```
In a few seconds, you should be able to access the OpenMetadata UI at `http://localhost:8585`.
## Next Steps
For port mapping, load balancer setup, AWS services configuration, Docker volumes, and troubleshooting, see
[Docker Advanced Configuration & Troubleshooting](/v2.0.x/deployment/docker/advanced).
# Docker Advanced Configuration & Troubleshooting
Source: https://docs.open-metadata.org/v2.0.x/deployment/docker/advanced
Configure port mapping, load balancing, AWS services, volumes, and troubleshoot common Docker deployment issues for OpenMetadata.
# Docker Advanced Configuration & Troubleshooting
## Port Mapping / Port Forwarding
We are shipping the OpenMetadata server and UI at container port and host port `8585`. You can change the host port number according to your requirement.
As an example, You could update the ports to serve OpenMetadata Server and UI at port `80`
To achieve this -
* You just have to update the ports mapping of the openmetadata-server in the `docker-compose.yml` file under `openmetadata-server` docker service section.
```yaml theme={null}
---
ports:
- "80:8585"
```
* Once the port is updated if there are any containers running remove them first using `docker compose down` command and then recreate the containers once again by below command
```commandline theme={null}
docker compose up --detach
```
## Run OpenMetadata with a load balancer
You may put one or more OpenMetadata instances behind a load balancer for reverse proxying. To do this you will need to add one or more entries to the configuration file for your reverse proxy.
### Nginx
To use OpenMetadata behind Nginx reverse proxy, add an entry resembling the following in the http context of your Nginx configuration file for each OpenMetadata instance.
```
server {
access_log /var/log/nginx/stage-reverse-access.log;
error_log /var/log/nginx/stage-reverse-error.log;
server_name stage.open-metadata.org;
location / {
proxy_pass http://127.0.0.1:8585;
}
}
```
## Run OpenMetadata with AWS Services
If you are running OpenMetadata in AWS, it is recommended to use [Amazon RDS](https://docs.aws.amazon.com/rds/index.html) and [Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/?id=docs_gateway).
We support
* Amazon RDS (MySQL) engine version 8 or higher
* Amazon OpenSearch (ElasticSearch) engine version 9.x (minimum 9.0.0, recommended 9.3.0) or Amazon OpenSearch engine version 3.x (minimum 3.0.0, recommended 3.3.0)
* Amazon RDS (PostgreSQL) engine version 15 or higher
Note:-
When using AWS Services the SearchType Configuration for elastic search should be `opensearch`, for both cases ElasticSearch and OpenSearch,
as you can see in the ElasticSearch configuration example.
For Production Systems, we recommend Amazon RDS to be in Multiple Availability Zones. For Amazon OpenSearch (or ElasticSearch) Service, we recommend Multiple Availability Zones with minimum 3 Master Nodes.
Once you have the RDS and OpenSearch Services Setup, you can update the environment variables below for OpenMetadata Docker Compose backed systems to connect with Database and ElasticSearch.
```
# MySQL Environment Variables
DB_DRIVER_CLASS='com.mysql.cj.jdbc.Driver'
DB_SCHEME='mysql'
DB_PARAMS='allowPublicKeyRetrieval=true&useSSL=true&serverTimezone=UTC'
DB_USER_PASSWORD=''
DB_HOST=''
DB_USER=''
OM_DATABASE=''
DB_PORT=''
# ElasticSearch Environment Variables
SEARCH_TYPE='opensearch'
ELASTICSEARCH_SOCKET_TIMEOUT_SECS='60'
ELASTICSEARCH_USER=''
ELASTICSEARCH_CONNECTION_TIMEOUT_SECS='5'
ELASTICSEARCH_PORT='443'
ELASTICSEARCH_SCHEME='https'
ELASTICSEARCH_BATCH_SIZE='10'
ELASTICSEARCH_HOST=''
ELASTICSEARCH_PASSWORD=''
ELASTICSEARCH_CLUSTER_ALIAS=''
```
Replace the environment variables values with the RDS and OpenSearch Service ones and then provide this environment variable file as part of docker compose command.
```bash theme={null}
docker compose --env-file ./env-mysql up --detach
```
## Advanced
### Add Docker Volumes for OpenMetadata Server Compose Service
There are many scenarios where you would want to provide additional files to the OpenMetadata Server and serve while running the application. In such scenarios, it is recommended to provision docker volumes for OpenMetadata Application.
If you are not familiar with Docker Volumes with Docker Compose Services, Please refer to [official documentation](https://docs.docker.com/storage/volumes/#use-a-volume-with-docker-compose) for more information.
For example, we would like to provide custom JWT Configuration Keys to be served to OpenMetadata Application. This requires the OpenMetadata Containers to have docker volumes sharing the private and public keys. Let's assume you have the keys available in `jwtkeys` directory in the same directory where your `docker-compose` file is available in the host machine.
In scenarios where you need to provide a custom `openmetadata.yaml` configuration file to the OpenMetadata application, you can do so by mounting the file as a volume in the Docker container. This is especially useful for configurations that cannot be controlled through environment variables.
We add the volumes section to mount the keys or `openmetadata.yaml` onto the docker containers create with docker compose as follows -
```yaml theme={null}
services:
openmetadata-server:
...
volumes:
- ./jwtkeys:/etc/openmetadata/jwtkeys
- ./openmetadata.yaml:/opt/openmetadata/conf/openmetadata.yaml
...
```
The above example uses [bind mounts](https://docs.docker.com/storage/bind-mounts/#use-a-bind-mount-with-compose) to share files and directories between host machine and openmetadata container.
Next, in your environment file, update the jwt configurations to use the right path from inside the container.
```bash theme={null}
...
# JWT Configuration
RSA_PUBLIC_KEY_FILE_PATH="/etc/openmetadata/jwtkeys/public_key.der"
RSA_PRIVATE_KEY_FILE_PATH="/etc/openmetadata/jwtkeys/private_key.der"
...
```
Ensure that the default environment variables are set appropriately to complement the settings in your `openmetadata.yaml`.
Once the changes are updated, if there are any containers running remove them first using `docker compose down` command and then recreate the containers once again by below command
```commandline theme={null}
docker compose up --detach
```
## Troubleshooting
### Java Memory Heap Issue
If your openmetadata Docker Compose logs speaks about the below issue -
```
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "AsyncAppender-Worker-async-file-appender"
Exception in thread "pool-5-thread-1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-file-appender" java.lang.OutOfMemoryError: Java heap space
Exception in thread "dw-46" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-console-appender" java.lang.OutOfMemoryError: Java heap space
```
This is due to the default JVM Heap Space configuration (1 GiB) being not enough for your workloads. In order to resolve this issue, head over to your custom openmetadata environment variable file and append the below environment variable
```
#environment variable file
OPENMETADATA_HEAP_OPTS="-Xmx2G -Xms2G"
```
The flag `Xmx` specifies the maximum memory allocation pool for a Java virtual machine (JVM), while `Xms` specifies the initial memory allocation pool.
Restart the OpenMetadata Docker Compose Application using `docker compose --env-file -f docker-compose.yml up --detach` which will recreate the containers with new environment variable values you have provided.
### PostgreSQL Issue permission denied to create extension "pgcrypto"
If you are facing the below issue with PostgreSQL as Database Backend for OpenMetadata Application,
```
Message: ERROR: permission denied to create extension "pgcrypto"
Hint: Must be superuser to create this extension.
```
It seems the Database User does not have sufficient privileges. In order to resolve the above issue, grant usage permissions to the PSQL User.
```sql theme={null}
GRANT USAGE ON SCHEMA schema_name TO ;
GRANT CREATE ON EXTENSION pgcrypto TO ;
```
In the above command, replace `` with the sql user used by OpenMetadata Application to connect to PostgreSQL Database.
In the above command, replace `` with the sql user used by OpenMetadata Application to connect to PostgreSQL Database.
## Security
Please follow our [Enable Security Guide](/v2.0.x/deployment/docker/security) to configure security for your OpenMetadata
installation.
## Next Steps
1. Refer the [How-to Guides](/v2.0.x/how-to-guides) for an overview of all the features in OpenMetadata.
2. Visit the [Connectors](/v2.0.x/connectors) documentation to see what services you can integrate with
OpenMetadata.
3. Visit the [API](https://docs.open-metadata.org/api-reference) documentation and explore the rich set of OpenMetadata APIs.
# Enable Security (Docker) | OpenMetadata Docker Security
Source: https://docs.open-metadata.org/v2.0.x/deployment/docker/security
Secure Docker-based deployments using best practices in network access, token handling, and identity provider integration.
# Docker Security
Follow the steps for setting up the SSO, and then check the specific `Docker` section of your chosen SSO.
By default Basic Authentication will be enabled as authentication mechanism.
Configure Basic Authentication to access the UI and APIs
Configure Ldap Authentication to access the UI and APIs
Configure Auth0 SSO to access the UI and APIs
Configure Azure SSO to access the UI and APIs
Configure a Custom OIDC SSO to access the UI and APIs
Configure Google SSO to access the UI and APIs
Configure Okta SSO to access the UI and APIs
# Configuring OpenMetadata to Run Under a Subpath
Source: https://docs.open-metadata.org/v2.0.x/deployment/docker/subpath
## Subpath in OpenMetadata
To configure **OpenMetadata** to operate under a subpath (for example `/openmetadata`), useful when deploying behind a reverse proxy or load balancer, you need to adjust specific settings in the `openmetadata.yaml` configuration file.
**`BASE_PATH` must not have a trailing slash**, but `basePath` in `openmetadata.yaml` needs one. OpenMetadata builds static asset URLs by appending directly to `basePath` with no separator, so if it's missing the trailing slash, requests resolve to `/openmetadataassets/...` instead of `/openmetadata/assets/...` and every static asset 404s.
## Configuration Steps
### 1. Set the Base Path
Define the `basePath` parameter to configure the application's root context, and ensure that the `publicKeyUrl` is updated accordingly to reflect the new base path.
This sets the root context for the application. Note the trailing slash on `basePath`:
```yaml theme={null}
basePath: ${BASE_PATH:-/openmetadata}/
```
This configuration sets the base path to /openmetadata by default. You can override it by setting the BASE\_PATH environment variable: set `BASE_PATH` itself **without** a trailing slash (e.g., `BASE_PATH=/openmetadata`), since the `/` above is appended for you.
### 2. Configure Web Paths
Configure the web application and API endpoint paths to align with the specified base path. These settings live under the top-level `server:` block:
```yaml theme={null}
server:
applicationContextPath: ${BASE_PATH:-/openmetadata}
rootPath: ${BASE_PATH:-/openmetadata}api/*
```
* `applicationContextPath`: Defines the context path for the web application.
* `rootPath`: Specifies the root path for API endpoints. [GitHub](https://github.com/open-metadata/OpenMetadata/discussions/17954)
### 3. Set Asset Paths
Ensure that asset paths are correctly prefixed with the base path.
```yaml theme={null}
assets:
resourcePath: /openmetadata/assets/
uriPath: ${BASE_PATH:-/openmetadata}
```
* `resourcePath`: Path to static resources.
* `uriPath`: URI path prefix for assets.
## Example Configuration
Here's how the relevant section of your `openmetadata.yaml` might look:
```yaml theme={null}
basePath: ${BASE_PATH:-/openmetadata}/
publicKeyUrl: ${BASE_PATH:-/}api/v1/system/config/jwks
server:
applicationContextPath: ${BASE_PATH:-/openmetadata}
rootPath: ${BASE_PATH:-/openmetadata}api/*
assets:
resourcePath: /openmetadata/assets/
uriPath: ${BASE_PATH:-/openmetadata}
```
## Deployment Considerations
* **Reverse Proxy Configuration**: Ensure that your reverse proxy (e.g., NGINX, Apache) is configured to forward requests to the OpenMetadata application with the correct subpath.
* **Environment Variables**: You can override the default base path by setting the BASE\_PATH environment variable in your deployment environment. Ensure that related parameters such as basePath, applicationContextPath, rootPath, and publicKeyUrl are updated to reflect this change.
* **Static Assets**: Verify that static assets are accessible under the new subpath to prevent broken links or missing resources.
# How to enable AWS RDS IAM Auth | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/iam-auth
Learn how to securely connect OpenMetadata to AWS RDS using IAM authentication with correct environment variables and configuration best practices.
# Aws resources on RDS IAM Auth
[AWS Reference Doc](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html)
## Requirements
1. AWS RDS Cluster with IAM auth enabled
2. User on DB Cluster with IAM enabled
3. IAM policy with permission on RDS connect
4. Role with IAM policy attached
5. IAM role attached to an EC2 instance on which openmetadata is deployed or ServiceAccount/Kube2Iam role attached to pod.
## How to enable ADS RDS IAM Auth on postgresql
Set the environment variables
```Commandline theme={null}
DB_USER_PASSWORD: "dummy"
DB_PARAMS: "awsRegion=eu-west-1&allowPublicKeyRetrieval=true&sslmode=require&serverTimezone=UTC"
```
Either through helm (if deployed in kubernetes) or as env vars.
The `DB_USER_PASSWORD` is still required and cannot be empty. Set it to a random/dummy string.
When using IAM authentication for AWS RDS, you must still provide a dummy value for the `DB_PASSWORD` environment variable. OpenMetadata automatically handles the IAM credentials internally. Ensure the following parameters are set for successful connection:
* `DB_PARAMS=awsRegion=us-east-1&allowPublicKeyRetrieval=true&serverTimezone=UTC`
* `DB_USE_SSL=true`
These settings ensure proper token generation and secure communication with the RDS instance.
# Ingestion Framework Deployment | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion
Configure ingestion from external, cloud-native, or hybrid environments using deployment guides.
# Ingestion Framework Deployment
The Ingestion Framework is the module that takes care of bringing metadata in to OpenMetadata. It is used
for any type of workflow that is supported in the platform: Metadata, Lineage, Usage, Profiler, Data Quality,...
## Manage & Schedule the Ingestion Framework
In this guide, we will present the different alternatives to run and manage your ingestion workflows. There are mainly
2 ways of running the ingestion:
1. Internally, by managing the workflows from OpenMetadata.
2. Externally, by using any other tool capable of running Python code.
Note that the end result is going to be the same. The only difference is that running the workflows internally,
OpenMetadata will dynamically generate the processes that will perform the metadata extraction. If configuring
the ingestion externally, you will be managing this processes directly on your platform of choice.
## Option 1 - From OpenMetadata
If you want to learn how to configure your setup to run them from OpenMetadata, follow these guides:
Deploy, configure and manage the ingestion workflows using Apache Airflow as the orchestrator.
Run ingestion pipelines using native Kubernetes Jobs and CronJobs - no Airflow required.
**New in 1.12**: The Kubernetes Native Orchestrator allows you to run ingestion pipelines directly as Kubernetes Jobs,
eliminating the need for Apache Airflow. This is ideal for organizations that want to reduce infrastructure complexity
while leveraging their existing Kubernetes cluster.
## Option 2 - Externally
Any tool capable of running Python code can be used to configure the metadata extraction from your sources.
In this section, we are going to give you some background on how the Ingestion Framework works, how to configure
the metadata extraction, and some examples on how to host the ingestion in different platforms.
Manage the Ingestion Framework from anywhere!
# Run the Ingestion Framework Externally
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external
Configure external ingestion endpoints to trigger metadata pipelines from orchestrators or custom scripts.
# Ingestion Framework External Deployment
Any tool capable of running Python code can be used to configure the metadata extraction from your sources.
## 1. How does the Ingestion Framework work?
The Ingestion Framework contains all the logic about how to connect to the sources, extract their metadata
and send it to the OpenMetadata server. We have built it from scratch with the main idea of making it an independent
component that can be run from - **literally** - anywhere.
In order to install it, you just need to get it from [PyPI](https://pypi.org/project/openmetadata-ingestion/).
```shell theme={null}
pip install openmetadata-ingestion
```
We will show further examples later, but a piece of code is the best showcase for its simplicity. In order to run
a full ingestion process, you just need to execute a single function. For example, if we wanted to run the metadata
ingestion from within a simple Python script:
```python theme={null}
from metadata.workflow.metadata import MetadataWorkflow
# Specify your YAML configuration
CONFIG = """
source:
...
workflowConfig:
openMetadataServerConfig:
hostPort: 'http://localhost:8585/api'
authProvider: openmetadata
securityConfig:
jwtToken: ...
"""
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
if __name__ == "__main__":
run()
```
Where this function runs is completely up to you, and you can adapt it to what makes the most sense within your
organization and engineering context. Below you'll see some examples of different orchestrators you can leverage
to execute the ingestion process.
## 2. Ingestion Configuration
In the example above, the `Workflow` class got created from a YAML configuration. Any Workflow that you execute (ingestion,
profiler, lineage,...) will have its own YAML representation.
You can think about this configuration as the recipe you want to execute: where is your source, which pieces do you
extract, how are they processed and where are they sent.
An example YAML config for extracting MySQL metadata looks like this:
```yaml theme={null}
source:
type: mysql
serviceName: mysql
serviceConnection:
config:
type: Mysql
username: openmetadata_user
authType:
password: openmetadata_password
hostPort: localhost:3306
databaseSchema: openmetadata_db
sourceConfig:
config:
type: DatabaseMetadata
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort: 'http://localhost:8585/api'
authProvider: openmetadata
securityConfig:
jwtToken: ...
```
You will find examples of all the workflow's YAML files at each Connector [page](/v2.0.x/connectors).
We will now show you examples on how to configure and run every workflow externally by using Snowflake as an example. But
first, let's digest some information that will be common everywhere, the `workflowConfig`.
### Workflow Config
Here you will define information such as where are you hosting the OpenMetadata server, and the JWT token to authenticate.
Review this section carefully to ensure you are properly managing service credentials and other security configurations.
**Logger Level**
You can specify the `loggerLevel` depending on your needs. If you are trying to troubleshoot an ingestion, running
with `DEBUG` will give you far more traces for identifying issues.
**JWT Token**
JWT tokens will allow your clients to authenticate against the OpenMetadata server.
To enable JWT Tokens, you will get more details [here](/v2.0.x/deployment/security/enable-jwt-tokens).
You can refer to the JWT Troubleshooting section [link](/v2.0.x/deployment/security/jwt-troubleshooting) for any issues in
your JWT configuration.
**Store Service Connection**
If set to `true` (default), we will store the sensitive information either encrypted via the Fernet Key in the database
or externally, if you have configured any [Secrets Manager](/v2.0.x/deployment/secrets-manager).
If set to `false`, the service will be created, but the service connection information will only be used by the Ingestion
Framework at runtime, and won't be sent to the OpenMetadata server.
**Secrets Manager Configuration**
If you have configured any [Secrets Manager](/v2.0.x/deployment/secrets-manager), you need to let the Ingestion Framework know
how to retrieve the credentials securely.
Follow the [docs](/v2.0.x/deployment/secrets-manager) to configure the secret retrieval based on your environment.
**SSL Configuration**
If you have added SSL to the [OpenMetadata server](/v2.0.x/deployment/security/enable-ssl), then you will need to handle
the certificates when running the ingestion too. You can either set `verifySSL` to `ignore`, or have it as `validate`,
which will require you to set the `sslConfig.caCertificate` with a local path where your ingestion runs that points
to the server certificate file.
Find more information on how to troubleshoot SSL issues [here](/v2.0.x/deployment/security/enable-ssl/ssl-troubleshooting).
```yaml theme={null}
workflowConfig:
loggerLevel: INFO # DEBUG, INFO, WARNING or ERROR
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "{bot_jwt_token}"
## Store the service Connection information
# storeServiceConnection: true or false
## Secrets Manager Configuration
# secretsManagerProvider: aws, azure or noop
# secretsManagerLoader: airflow or env
## If SSL, fill the following
# verifySSL: validate # or ignore
# sslConfig:
# caCertificate: /local/path/to/certificate
```
#### JWT Token with Secrets Manager
If you are using the [Secrets Manager](/v2.0.x/deployment/secrets-manager), you can let the Ingestion client to pick up
the JWT Token dynamically from the Secrets Manager at runtime. Let's show an example:
We have an OpenMetadata server running with the `managed-aws` Secrets Manager. Since we used the `OPENMETADATA_CLUSTER_NAME` env var
as `test`, our `ingestion-bot` JWT Token is safely stored under the secret ID `
/test/bot/ingestion-bot/config/jwttoken`.
Now, we can use the following workflow config to run the ingestion without having to pass the token, but just pointing to the secret itself:
```yaml theme={null}
workflowConfig:
loggerLevel: INFO # DEBUG, INFO, WARNING or ERROR
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "secret:/test/bot/ingestion-bot/config/jwttoken"
secretsManagerProvider: aws
secretsManagerLoader: env
```
Notice how:
1. We specify the `secretsManagerProvider` pointing to `aws`, since that's the manager we are using.
2. We set `secretsManagerLoader` as `env`. Since we're running this from our local, we'll let the AWS credentials to be
loaded from the local env vars. (When running this using the UI, note that the generated workflows will have this
value set as `airflow`!)
3. We set the `jwtToken` value as `secret:/test/bot/ingestion-bot/config/jwttoken`, which tells the client that
this value is a `secret` located under `/test/bot/ingestion-bot/config/jwttoken`.
Those are our env vars:
```
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=...
```
And we can run this normally with `metadata ingest -c `.
Note that **even if you are not using the Secrets Manager for the OpenMetadata Server**, you can still apply the same
approach by storing the JWT token manually to the secrets manager, and let the Ingestion client pick it up
from there automatically.
## 3. (Optional) Ingestion Pipeline
Additionally, if you want to see your runs logged in the `Ingestions` tab of the connectors page in the UI as you would
when running the connectors natively with OpenMetadata, you can add the following configuration on your YAMLs:
```yaml theme={null}
source:
type: mysql
serviceName: mysql
[...]
workflowConfig:
openMetadataServerConfig:
hostPort: 'http://localhost:8585/api'
authProvider: openmetadata
securityConfig:
jwtToken: ...
ingestionPipelineFQN: . # E.g., mysql.marketing_metadata`
```
Adding the `ingestionPipelineFQN` - the Ingestion Pipeline Fully Qualified Name - will tell the Ingestion Framework
to log the executions and update the ingestion status, which will appear on the UI. Note that the action buttons
will be disabled, since OpenMetadata won't be able to interact with external systems.
## 4. (Optional) Disable the Pipeline Service Client
If you want to run your workflows **ONLY externally** without relying on OpenMetadata for any workflow management
or scheduling, you can update the following server configuration:
```yaml theme={null}
pipelineServiceClientConfiguration:
enabled: ${PIPELINE_SERVICE_CLIENT_ENABLED:-true}
```
by setting `enabled: false` or setting the `PIPELINE_SERVICE_CLIENT_ENABLED=false` as an environment variable.
This will stop certain APIs and monitors related to the Pipeline Service Client (e.g., Airflow) from being operative.
## Examples
This is not an exhaustive list, and it will keep growing over time. Not because the orchestrators X or Y are not supported,
but just because we did not have the time yet to add it here. If you'd like to chip in and help us expand these guides and examples,
don't hesitate to reach to us in [Slack](https://slack.open-metadata.org/) or directly open a PR in
[GitHub](https://github.com/open-metadata/docs-v1/tree/main/content).
Run the ingestion process externally from Airflow
Run the ingestion process externally using AWS MWAA
Run the ingestion process externally from GCP Composer
Run the ingestion process externally from GitHub Actions
For code examples running Metadata, Lineage, Usage, Profiler, and Data Quality workflows externally, see [Workflow Examples](/v2.0.x/deployment/ingestion/external/examples).
# Run the ingestion from your Airflow
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/airflow
Deploy ingestion externally using Airflow for scalable orchestration of metadata pipelines across environments.
This page is about running the Ingestion Framework **externally**!
There are mainly 2 ways of running the ingestion:
1. Internally, by managing the workflows from OpenMetadata.
2. Externally, by using any other tool capable of running Python code.
If you are looking for how to manage the ingestion process from OpenMetadata, you can follow
this [doc](/deployment/ingestion/openmetadata).
# Run the ingestion from your Airflow
OpenMetadata integrates with Airflow to orchestrate ingestion workflows. You can use Airflow to [extract metadata](/v2.0.x/connectors/pipeline/airflow) and \[deploy workflows] (/deployment/ingestion/openmetadata) directly. This guide explains how to run ingestion workflows in Airflow using three different operators:
1. [Python Operator](#using-the-python-operator)
2. [Docker Operator](/v2.0.x/deployment/ingestion/external/airflow-docker-virtualenv#using-the-docker-operator)
3. [Python Virtualenv Operator](/v2.0.x/deployment/ingestion/external/airflow-docker-virtualenv#using-the-python-virtualenv-operator)
## Using the Python Operator
### Prerequisites
Install the `openmetadata-ingestion` package in your Airflow environment. This approach works best if you have access to the Airflow host and can manage dependencies.
#### Installation Command:
```
pip3 install openmetadata-ingestion[<plugin>]==x.y.z
```
-Replace [\](https://github.com/open-metadata/OpenMetadata/blob/main/ingestion/setup.py) with the sources to ingest, such as mysql, snowflake, or s3.
-Replace x.y.z with the OpenMetadata version matching your server (e.g., 2.0.1.0).
### Example
```
pip3 install openmetadata-ingestion[mysql,snowflake,s3]==2.0.1.0
```
### Example DAG
```python theme={null}
import yaml
from datetime import timedelta
from airflow import DAG
try:
from airflow.operators.python import PythonOperator
except ModuleNotFoundError:
from airflow.operators.python_operator import PythonOperator
from metadata.config.common import load_config_file
from metadata.workflow.metadata import MetadataWorkflow
from airflow.utils.dates import days_ago
default_args = {
"owner": "user_name",
"email": ["username@org.com"],
"email_on_failure": False,
"retries": 3,
"retry_delay": timedelta(minutes=5),
"execution_timeout": timedelta(minutes=60)
}
config = """
"""
def metadata_ingestion_workflow():
workflow_config = yaml.safe_load(config)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"sample_data",
default_args=default_args,
description="An example DAG which runs a OpenMetadata ingestion workflow",
start_date=days_ago(1),
is_paused_upon_creation=False,
schedule_interval='*/5 * * * *',
catchup=False,
) as dag:
ingest_task = PythonOperator(
task_id="ingest_using_recipe",
python_callable=metadata_ingestion_workflow,
)
```
### Key Notes
* **Function Setup**: The `python_callable` argument in the `PythonOperator` executes the `metadata_ingestion_workflow` function, which instantiates the workflow and runs the ingestion process.
* **Drawback**: This method requires pre-installed dependencies, which may not always be feasible. Consider using the **DockerOperator** or **PythonVirtualenvOperator** as alternatives.
## Next Steps
Run ingestion using the Docker Operator or Python Virtualenv Operator for isolated, dependency-free execution.
# Airflow Docker & Virtualenv Operators | OpenMetadata Ingestion
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/airflow-docker-virtualenv
Run OpenMetadata ingestion workflows in Airflow using the Docker Operator or Python Virtualenv Operator for isolated, dependency-free execution.
# Docker & Virtualenv Operators
These container-based operators let you run OpenMetadata ingestion without installing dependencies directly on the Airflow host.
## Using the Docker Operator
For this operator, we can use the `openmetadata/ingestion-base` image. This is useful to prepare DAGs without any installation required on the environment, although it needs for the host to have access to the Docker commands.
### Prerequisites
Ensure the Airflow host can run Docker commands. For Docker Compose setups, map the Docker socket as follows:
### Example
```yaml theme={null}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:z # Need 666 permissions to run DockerOperator
```
### Example DAG
```python theme={null}
from datetime import datetime
from airflow import models
from airflow.providers.docker.operators.docker import DockerOperator
config = """
"""
with models.DAG(
"ingestion-docker-operator",
schedule_interval='*/5 * * * *',
start_date=datetime(2021, 1, 1),
catchup=False,
tags=["OpenMetadata"],
) as dag:
DockerOperator(
command="python main.py",
image="openmetadata/ingestion-base:2.0.1",
environment={"config": config, "pipelineType": "metadata"},
docker_url="unix://var/run/docker.sock", # To allow to start Docker. Needs chmod 666 permissions
tty=True,
auto_remove="True",
network_mode="host", # To reach the OM server
task_id="ingest",
dag=dag,
)
```
Make sure to tune out the DAG configurations (`schedule_interval`, `start_date`, etc.) as your use case requires.
If you encounter issues such as missing task instances or Airflow failing to locate a deployed DAG (e.g., `Dag '' could not be found`), this may be due to a **timezone mismatch** in your Airflow configuration. To resolve this, set the following in your `airflow.cfg`:
```ini theme={null}
default_timezone = system
```
This ensures that Airflow uses the system timezone, which is particularly important when OpenMetadata and Airflow are running on the same server.
### Key Notes
* **Image Version**: Ensure the Docker image version matches your OpenMetadata server version (e.g., `openmetadata/ingestion-base:2.0.1`).
* **Pipeline Types**: Set the `pipelineType` to `metadata`, `usage`, `lineage`, `profiler`, or other supported values.
* **No Installation Required**: The `DockerOperator` eliminates the need to install dependencies directly on the Airflow host.
Another important point here is making sure that the Airflow will be able to run Docker commands to create the task.
As our example was done with Airflow in Docker Compose, that meant setting `docker_url="unix://var/run/docker.sock"`.
The final important elements here are:
* `command="python main.py"`: This does not need to be modified, as we are shipping the `main.py` script in the
image, used to trigger the workflow.
* `environment={"config": config, "pipelineType": "metadata"}`: Again, in most cases you will just need to update
the `config` string to point to the right connector.
Other supported values of `pipelineType` are `usage`, `lineage`, `profiler`, `dataInsight`, `elasticSearchReindex`, `dbt`, `application` or `TestSuite`. Pass the required flag
depending on the type of workflow you want to execute. Make sure that the YAML config reflects what ingredients
are required for your Workflow.
## Using the Python Virtualenv Operator
### Prerequisites
As stated in Airflow's [docs](https://airflow.apache.org/docs/apache-airflow/stable/howto/operator/python.html#pythonvirtualenvoperator), install the `virtualenv` package on the Airflow host.If using a different Python version in the virtual environment (e.g., Python 3.9 while Airflow uses 3.7), install additional packages such as:
```
gcc python3.9-dev python3.9-distutils
```
### Example DAG
```python theme={null}
from datetime import timedelta
from airflow import DAG
try:
from airflow.operators.python import PythonVirtualenvOperator
except ModuleNotFoundError:
from airflow.operators.python_operator import PythonVirtualenvOperator
from airflow.utils.dates import days_ago
default_args = {
"owner": "user_name",
"email": ["username@org.com"],
"email_on_failure": False,
"retries": 3,
"retry_delay": timedelta(seconds=10),
"execution_timeout": timedelta(minutes=60),
}
def metadata_ingestion_workflow():
from metadata.workflow.metadata import MetadataWorkflow
import yaml
config = """
source:
type: postgres
serviceName: local_postgres
serviceConnection:
config:
type: Postgres
username: openmetadata_user
authType:
password: openmetadata_password
hostPort: localhost:5432
database: pagila
sourceConfig:
config:
type: DatabaseMetadata
sink:
type: metadata-rest
config: {}
workflowConfig:
# loggerLevel: INFO # DEBUG, INFO, WARN or ERROR
openMetadataServerConfig:
hostPort: http://localhost:8585/api
authProvider: openmetadata
securityConfig:
jwtToken: "eyJraWQiOiJHYjM4OWEtOWY3Ni1nZGpzLWE5MmotMDI0MmJrOTQzNTYiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlzQm90IjpmYWxzZSwiaXNzIjoib3Blbi1tZXRhZGF0YS5vcmciLCJpYXQiOjE2NjM5Mzg0NjIsImVtYWlsIjoiYWRtaW5Ab3Blbm1ldGFkYXRhLm9yZyJ9.tS8um_5DKu7HgzGBzS1VTA5
"""
workflow_config = yaml.safe_load(config)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"ingestion_dag",
default_args=default_args,
description="An example DAG which runs a OpenMetadata ingestion workflow",
start_date=days_ago(1),
is_paused_upon_creation=True,
catchup=False,
) as dag:
ingest_task = PythonVirtualenvOperator(
task_id="ingest_using_recipe",
requirements=[
'openmetadata-ingestion[mysql]~=2.0.1.0', # Specify any additional Python package dependencies
],
system_site_packages=False, # Set to True if you want to include system site-packages in the virtual environment
python_version="3.9", # Remove if necessary
python_callable=metadata_ingestion_workflow
)
```
### Key Notes
**Function Rules**:
* Use a `def` function (not part of a class).
* All imports must occur inside the function.
* Avoid referencing variables outside the function's scope.
## Ingestion Workflow classes
We have different classes for different types of workflows. The logic is always the same, but you will need
to change your import path. The rest of the method calls will remain the same.
For example, for the `Metadata` workflow we'll use:
```python theme={null}
import yaml
from metadata.workflow.metadata import MetadataWorkflow
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
The classes for each workflow type are:
* `Metadata`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Lineage`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
* `Usage`: `from metadata.workflow.usage import UsageWorkflow`
* `dbt`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Profiler`: `from metadata.workflow.profiler import ProfilerWorkflow`
* `Data Quality`: `from metadata.workflow.data_quality import TestSuiteWorkflow`
* `Data Insights`: `from metadata.workflow.data_insight import DataInsightWorkflow`
* `Elasticsearch Reindex`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
# Managing Credentials
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/credentials
# Managing Credentials
On the release 0.12 we updated how services credentials are handled from an Ingestion Workflow. We are covering
now two scenarios:
**1.** If we are running a metadata workflow for the first time, pointing to a service that **does not yet exist**,
then the service will be created from the Metadata Ingestion pipeline. It does not matter if the workflow
is run from the CLI or any other scheduler.
**2.** If instead, there is an already existing service to which we are pointing with a Metadata Ingestion pipeline,
then we will be using the **stored credentials**, not the ones incoming from the YAML config.
## Existing Services
What this means is that once a service is created, the only way to update its connection credentials is via
the **UI** or directly running an API call. This prevents the scenario where a new YAML config is created, using a name
of a service that already exists, but pointing to a completely different source system.
One of the main benefits of this approach is that if an admin in our organisation creates the service from the UI,
then we can prepare any Ingestion Workflow without having to pass the connection details.
For example, for an Athena YAML, instead of requiring the full set of credentials as below:
```yaml theme={null}
source:
type: athena
serviceName: my_athena_service
serviceConnection:
config:
type: Athena
awsConfig:
awsAccessKeyId: KEY
awsSecretAccessKey: SECRET
awsRegion: us-east-2
s3StagingDir: s3 directory for datasource
workgroup: workgroup name
sourceConfig:
type: DatabaseMetadata
config:
markDeletedTables: true
includeTables: true
includeViews: true
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort:
authProvider:
```
We can use a simplified version:
```yaml theme={null}
source:
type: athena
serviceName: my_athena_service
sourceConfig:
config:
type: DatabaseMetadata
markDeletedTables: true
includeTables: true
includeViews: true
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort:
authProvider:
```
The workflow will then dynamically pick up the service connection details for `my_athena_service` and ingest
the metadata accordingly.
If instead, you want to have the full source of truth in your DAGs or processes, you can keep reading on different
ways to secure the credentials in your environment and not have them at plain sight.
## Securing Credentials
Note that these are just a few examples. Any secure and automated approach to retrieve a string would work here,
as our only requirement is to pass the string inside the YAML configuration.
When running Workflow with the CLI or your favourite scheduler, it's safer to not have the services' credentials
visible. For the CLI, the ingestion package can load sensitive information from environment variables.
For example, if you are using the [Glue](/v2.0.x/connectors/database/glue) connector you could specify the
AWS configurations as follows in the case of a JSON config file
```json theme={null}
[...]
"awsConfig": {
"awsAccessKeyId": "${AWS_ACCESS_KEY_ID}",
"awsSecretAccessKey": "${AWS_SECRET_ACCESS_KEY}",
"awsRegion": "${AWS_REGION}",
"awsSessionToken": "${AWS_SESSION_TOKEN}"
},
[...]
```
Or
```yaml theme={null}
[...]
awsConfig:
awsAccessKeyId: '${AWS_ACCESS_KEY_ID}'
awsSecretAccessKey: '${AWS_SECRET_ACCESS_KEY}'
awsRegion: '${AWS_REGION}'
awsSessionToken: '${AWS_SESSION_TOKEN}'
[...]
```
for a YAML configuration.
### AWS Credentials
The AWS Credentials are based on the following [JSON Schema](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-spec/src/main/resources/json/schema/security/credentials/awsCredentials.json).
Note that the only required field is the `awsRegion`. This configuration is rather flexible to allow installations under AWS
that directly use instance roles for permissions to authenticate to whatever service we are pointing to without having to
write the credentials down.
#### AWS Vault
If using [aws-vault](https://github.com/99designs/aws-vault), it gets a bit more involved to run the CLI ingestion as the credentials are not globally available in the terminal.
In that case, you could use the following command after setting up the ingestion configuration file:
```bash theme={null}
aws-vault exec -- $SHELL -c 'metadata ingest -c '
```
### GCP Credentials
The GCP Credentials are based on the following [JSON Schema](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-spec/src/main/resources/json/schema/security/credentials/gcpCredentials.json).
These are the fields that you can export when preparing a Service Account.
Once the account is created, you can see the fields in the exported JSON file from:
```
IAM & Admin > Service Accounts > Keys
```
You can validate the whole Google service account setup [here](/v2.0.x/deployment/security/google).
### Using GitHub Actions Secrets
If running the ingestion in a GitHub Action, you can create [encrypted secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
to store sensitive information such as users and passwords.
In the end, we'll map these secrets to environment variables in the process, that we can pick up with `os.getenv`, for example:
```python theme={null}
import os
import yaml
from metadata.workflow.metadata import MetadataWorkflow
CONFIG = f"""
source:
type: snowflake
serviceName: snowflake_from_github_actions
serviceConnection:
config:
type: Snowflake
username: {os.getenv('SNOWFLAKE_USERNAME')}
...
"""
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
if __name__ == "__main__":
run()
```
Make sure to update your step environment to pass the secrets as environment variables:
```yaml theme={null}
- name: Run Ingestion
run: |
source env/bin/activate
python ingestion-github-actions/snowflake_ingestion.py
# Add the env vars we need to load the snowflake credentials
env:
SNOWFLAKE_USERNAME: ${{ secrets.SNOWFLAKE_USERNAME }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
```
## Next Steps
For a step-by-step guide on using Airflow Connections to securely retrieve service credentials in your DAGs, see
[Using Airflow Connections](/v2.0.x/deployment/ingestion/external/credentials-airflow).
# Using Airflow Connections for Credentials
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/credentials-airflow
Retrieve and use Airflow Connections to securely pass service credentials in OpenMetadata ingestion pipelines.
# Using Airflow Connections
In any connector page, you might have seen an example on how to build a DAG to run the ingestion with Airflow
(e.g., [Athena](/v2.0.x/connectors/database/athena/yaml)).
A possible approach to retrieving sensitive information from Airflow would be using Airflow's
[Connections](https://airflow.apache.org/docs/apache-airflow/stable/howto/connection.html). Note that these
connections can be stored as environment variables, to Airflow's underlying DB or to multiple external services such as
Hashicorp Vault. Note that for external systems, you'll need to provide the necessary package and configure the
[Secrets Backend](https://airflow.apache.org/docs/apache-airflow/stable/security/secrets/secrets-backend/index.html).
The best way to choose how to store these credentials is to go through Airflow's [docs](https://airflow.apache.org/docs/apache-airflow/stable/concepts/connections.html).
## Example
Let's go over an example on how to create a connection to extract data from MySQL and how a DAG would look like
afterwards.
### Step 1 - Create the Connection
From our Airflow host, (e.g., `docker exec -it openmetadata_ingestion bash` if testing in Docker), you can run:
```bash theme={null}
airflow connections add 'my_mysql_db' \
--conn-uri 'mysql+pymysql://openmetadata_user:openmetadata_password@mysql:3306/openmetadata_db'
```
You will see an output like
```
Successfully added `conn_id`=my_mysql_db : mysql+pymysql://openmetadata_user:openmetadata_password@mysql:3306/openmetadata_db
```
Checking the credentials from the Airflow UI, we will see:
### Step 2 - Understanding the shape of a Connection
In the same host, we can open a Python shell to explore the Connection object with some more details. To do so, we first
need to pick up the connection from Airflow. We will use the `BaseHook` for that as the connection is not stored
in any external system.
```python theme={null}
from airflow.hooks.base import BaseHook
# Retrieve the connection
connection = BaseHook.get_connection("my_mysql_db")
# Access the connection details
connection.host # 'mysql'
connection.port # 3306
connection.login # 'openmetadata_user'
connection.password # 'openmetadata_password'
```
Based on this information, we now know how to prepare the DAG!
### Step 3 - Write the DAG
A full example on how to write a DAG to ingest data from our Connection can look like this:
```python theme={null}
import pathlib
import yaml
from datetime import timedelta
from airflow import DAG
from airflow.utils.dates import days_ago
try:
from airflow.operators.python import PythonOperator
except ModuleNotFoundError:
from airflow.operators.python_operator import PythonOperator
from metadata.config.common import load_config_file
from metadata.workflow.metadata import MetadataWorkflow
# Import the hook
from airflow.hooks.base import BaseHook
# Retrieve the connection
connection = BaseHook.get_connection("my_mysql_db")
# Use the connection details when setting the YAML
# Note how we escaped the braces as {{}} to not be parsed by the f-string
config = f"""
source:
type: mysql
serviceName: mysql_from_connection
serviceConnection:
config:
type: Mysql
username: {connection.login}
password: {connection.password}
hostPort: {connection.host}:{connection.port}
# databaseSchema: schema
sourceConfig:
config:
markDeletedTables: true
includeTables: true
includeViews: true
sink:
type: metadata-rest
config: {{}}
workflowConfig:
openMetadataServerConfig:
hostPort: ""
authProvider: ""
"""
def metadata_ingestion_workflow():
workflow_config = yaml.safe_load(config)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"mysql_connection_ingestion",
description="An example DAG which runs a OpenMetadata ingestion workflow",
start_date=days_ago(1),
is_paused_upon_creation=False,
schedule_interval='*/5 * * * *',
catchup=False,
) as dag:
ingest_task = PythonOperator(
task_id="ingest_using_recipe",
python_callable=metadata_ingestion_workflow,
)
```
### Option B - Reuse an existing Service
As explained in the [Managing Credentials](/v2.0.x/deployment/ingestion/external/credentials#existing-services) guide, once a service exists in OpenMetadata its connection details are stored and can be reused — just omit the `serviceConnection` YAML entries in your DAG:
```python theme={null}
import pathlib
import yaml
from datetime import timedelta
from airflow import DAG
from airflow.utils.dates import days_ago
try:
from airflow.operators.python import PythonOperator
except ModuleNotFoundError:
from airflow.operators.python_operator import PythonOperator
from metadata.config.common import load_config_file
from metadata.workflow.metadata import MetadataWorkflow
config = """
source:
type: mysql
serviceName: existing_mysql_service
sourceConfig:
config:
markDeletedTables: true
includeTables: true
includeViews: true
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort: ""
authProvider: ""
"""
def metadata_ingestion_workflow():
workflow_config = yaml.safe_load(config)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"mysql_connection_ingestion",
description="An example DAG which runs a OpenMetadata ingestion workflow",
start_date=days_ago(1),
is_paused_upon_creation=False,
schedule_interval='*/5 * * * *',
catchup=False,
) as dag:
ingest_task = PythonOperator(
task_id="ingest_using_recipe",
python_callable=metadata_ingestion_workflow,
)
```
# External Ingestion Workflow Examples
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/examples
Code examples for running OpenMetadata ingestion workflows externally — metadata, lineage, usage, profiler, and data quality.
# External Ingestion Workflow Examples
This page contains code examples for running each workflow type externally. For framework setup, configuration, and JWT token handling, see the [External Ingestion Overview](/v2.0.x/deployment/ingestion/external).
Let's jump now into some examples on how you could create the function to run the different workflows. Note that this code
can then be executed inside a DAG, a GitHub action, or a vanilla Python script. It will work for any environment.
### Testing
You can easily test every YAML configuration using the `metadata` CLI from the Ingestion Framework.
In order to install it, you just need to get it from [PyPI](https://pypi.org/project/openmetadata-ingestion/).
In each of the examples below, we'll showcase how to run the CLI, assuming you have a YAML file that contains
the workflow configuration.
### Metadata Workflow
This is the first workflow you have to configure and run. It will take care of fetching the metadata from your sources,
be it Database Services, Dashboard Services, Pipelines, etc.
The rest of the workflows (Lineage, Profiler,...) will be executed on top of the metadata already available in the platform.
**Adding the imports**
The first step is to import the `MetadataWorkflow` class, which will take care of the full ingestion logic. We'll
add the import for printing the results at the end.
**Defining the YAML**
Then, we need to pass the YAML configuration. For this simple example we are defining a variable, but you can
read from a file, parse secrets from your environment, or any other approach you'd need. In the end, it's just
Python code.
You can find complete YAMLs in each connector [docs](/v2.0.x/connectors) and find more information about the available
configurations.
**Preparing the Workflow**
Finally, we'll prepare a function that we can execute anywhere.
It will take care of instantiating the workflow, executing it and giving us the results.
```python theme={null}
import yaml
from metadata.workflow.metadata import MetadataWorkflow
CONFIG = """
source:
type: snowflake
serviceName:
serviceConnection:
config:
type: Snowflake
...
sourceConfig:
config:
type: DatabaseMetadata
markDeletedTables: true
includeTables: true
...
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "{bot_jwt_token}"
"""
def run():
workflow = MetadataWorkflow.create(yaml.safe_load(CONFIG))
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
You can test the workflow via `metadata ingest -c `.
### Lineage Workflow
This workflow will take care of scanning your query history and defining lineage relationships between your tables.
You can find more information about this workflow [here](/v2.0.x/connectors/ingestion/lineage).
**Adding the imports**
The first step is to import the `MetadataWorkflow` class, which will take care of the full ingestion logic. We'll
add the import for printing the results at the end.
Note that we are using the same class as in the Metadata Ingestion.
**Defining the YAML**
Then, we need to pass the YAML configuration. For this simple example we are defining a variable, but you can
read from a file, parse secrets from your environment, or any other approach you'd need.
Note how we have not added here the `serviceConnection`. Since the service would have been created during the
metadata ingestion, we can let the Ingestion Framework dynamically fetch the Service Connection information.
If, however, you are configuring the workflow with `storeServiceConnection: false`, you'll need to explicitly
define the `serviceConnection`.
You can find complete YAMLs in each connector [docs](/v2.0.x/connectors) and find more information about the available
configurations.
**Preparing the Workflow**
Finally, we'll prepare a function that we can execute anywhere.
It will take care of instantiating the workflow, executing it and giving us the results.
```python theme={null}
import yaml
from metadata.workflow.metadata import MetadataWorkflow
CONFIG = """
source:
type: snowflake-lineage
serviceName:
sourceConfig:
config:
type: DatabaseLineage
queryLogDuration: 1
parsingTimeoutLimit: 300
...
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "{bot_jwt_token}"
"""
def run():
workflow = MetadataWorkflow.create(yaml.safe_load(CONFIG))
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
You can test the workflow via `metadata ingest -c `.
### Usage Workflow
As with the lineage workflow, we'll scan the query history for any DML statements. The goal is to ingest queries
into the platform, figure out the relevancy of your assets and frequently joined tables.
**Adding the imports**
The first step is to import the `UsageWorkflow` class, which will take care of the full ingestion logic. We'll
add the import for printing the results at the end.
**Defining the YAML**
Then, we need to pass the YAML configuration. For this simple example we are defining a variable, but you can
read from a file, parse secrets from your environment, or any other approach you'd need.
Note how we have not added here the `serviceConnection`. Since the service would have been created during the
metadata ingestion, we can let the Ingestion Framework dynamically fetch the Service Connection information.
If, however, you are configuring the workflow with `storeServiceConnection: false`, you'll need to explicitly
define the `serviceConnection`.
You can find complete YAMLs in each connector [docs](/v2.0.x/connectors) and find more information about the available
configurations.
**Preparing the Workflow**
Finally, we'll prepare a function that we can execute anywhere.
It will take care of instantiating the workflow, executing it and giving us the results.
```python theme={null}
import yaml
from metadata.workflow.usage import UsageWorkflow
CONFIG = """
source:
type: snowflake-usage
serviceName:
sourceConfig:
config:
type: DatabaseUsage
queryLogDuration: 1
parsingTimeoutLimit: 300
...
processor:
type: query-parser
config: {}
stage:
type: table-usage
config:
filename: "/tmp/snowflake_usage"
bulkSink:
type: metadata-usage
config:
filename: "/tmp/snowflake_usage"
workflowConfig:
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "{bot_jwt_token}"
"""
def run():
workflow = UsageWorkflow.create(yaml.safe_load(CONFIG))
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
You can test the workflow via `metadata usage -c `.
### Profiler Workflow
This workflow will execute queries against your database and send the results into OpenMetadata. The goal is to compute
metrics about your data and give you a high-level view of its shape, together with the sample data.
This is an interesting previous step before creating Data Quality Workflows.
You can find more information about this workflow [here](/v2.0.x/how-to-guides/data-quality-observability/profiler/profiler-workflow).
**Adding the imports**
The first step is to import the `ProfilerWorkflow` class, which will take care of the full ingestion logic. We'll
add the import for printing the results at the end.
**Defining the YAML**
Then, we need to pass the YAML configuration. For this simple example we are defining a variable, but you can
read from a file, parse secrets from your environment, or any other approach you'd need.
Note how we have not added here the `serviceConnection`. Since the service would have been created during the
metadata ingestion, we can let the Ingestion Framework dynamically fetch the Service Connection information.
If, however, you are configuring the workflow with `storeServiceConnection: false`, you'll need to explicitly
define the `serviceConnection`.
You can find complete YAMLs in each connector [docs](/v2.0.x/connectors) and find more information about the available
configurations.
**Preparing the Workflow**
Finally, we'll prepare a function that we can execute anywhere.
It will take care of instantiating the workflow, executing it and giving us the results.
```python theme={null}
import yaml
from metadata.workflow.profiler import ProfilerWorkflow
CONFIG = """
source:
type: snowflake
serviceName:
sourceConfig:
config:
type: Profiler
generateSampleData: true
...
processor:
type: orm-profiler
config: {}
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "{bot_jwt_token}"
"""
def run():
workflow = ProfilerWorkflow.create(yaml.safe_load(CONFIG))
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
You can test the workflow via `metadata profile -c `.
### Data Quality Workflow
This workflow will execute queries against your database and send the results into OpenMetadata. The goal is to compute
metrics about your data and give you a high-level view of its shape, together with the sample data.
This is an interesting previous step before creating Data Quality Workflows.
You can find more information about this workflow [here](/v2.0.x/how-to-guides/data-quality-observability/quality/configure).
**Adding the imports**
The first step is to import the `TestSuiteWorkflow` class, which will take care of the full ingestion logic. We'll
add the import for printing the results at the end.
**Defining the YAML**
Then, we need to pass the YAML configuration. For this simple example we are defining a variable, but you can
read from a file, parse secrets from your environment, or any other approach you'd need.
Note how we have not added here the `serviceConnection`. Since the service would have been created during the
metadata ingestion, we can let the Ingestion Framework dynamically fetch the Service Connection information.
If, however, you are configuring the workflow with `storeServiceConnection: false`, you'll need to explicitly
define the `serviceConnection`.
Moreover, see how we are not configuring any tests in the `processor`. You can configure them in the [advanced YAML example](/v2.0.x/how-to-guides/data-quality-observability/quality/data-quality-as-code/advanced-usage#yaml-file-structure),
but even if nothing gets defined in the YAML, we will execute all the tests configured against the table.
You can find complete YAMLs in each connector [docs](/v2.0.x/connectors) and find more information about the available
configurations.
**Preparing the Workflow**
Finally, we'll prepare a function that we can execute anywhere.
It will take care of instantiating the workflow, executing it and giving us the results.
```python theme={null}
import yaml
from metadata.workflow.data_quality import TestSuiteWorkflow
CONFIG = """
source:
type: TestSuite
serviceName:
sourceConfig:
config:
type: TestSuite
entityFullyQualifiedName:
processor:
type: orm-test-runner
config: {}
sink:
type: metadata-rest
config: {}
workflowConfig:
openMetadataServerConfig:
hostPort: "http://localhost:8585/api"
authProvider: openmetadata
securityConfig:
jwtToken: "{bot_jwt_token}"
"""
def run():
workflow = TestSuiteWorkflow.create(yaml.safe_load(CONFIG))
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
You can test the workflow via `metadata test -c `.
# Run the ingestion from GCP Composer | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/gcp-composer
Deploy external ingestion using GCP Composer to automate metadata and quality pipelines on Google Cloud environments.
This page is about running the Ingestion Framework **externally**!
There are mainly 2 ways of running the ingestion:
1. Internally, by managing the workflows from OpenMetadata.
2. Externally, by using any other tool capable of running Python code.
If you are looking for how to manage the ingestion process from OpenMetadata, you can follow
this [doc](/deployment/ingestion/openmetadata).
# Run the ingestion from GCP Composer
## Requirements
This approach has been last tested against:
* Composer version 2.5.4
* Airflow version 2.6.3
It also requires the ingestion package to be at least `openmetadata-ingestion==2.0.1.0`.
## Using the Python Operator
The most comfortable way to run the metadata workflows from GCP Composer is directly via a `PythonOperator`. Note that
it will require you to install the packages and plugins directly on the host.
### Install the Requirements
In your environment you will need to install the following packages:
* `openmetadata-ingestion[]==x.y.z`.
* `sqlalchemy==1.4.27`: This is needed to align OpenMetadata version with the Composer internal requirements.
Where `x.y.z` is the version of the OpenMetadata ingestion package. Note that the version needs to match the server version. If we are using the server at 2.0.1, then the ingestion package needs to also be 2.0.1.0.
The plugin parameter is a list of the sources that we want to ingest. An example would look like this `openmetadata-ingestion[mysql,snowflake,s3]==2.0.1.0`.
### Prepare the DAG!
Note that this DAG is a usual connector DAG, just using the Airflow service with the `Backend` connection.
As an example of a DAG pushing data to OpenMetadata under Google SSO, we could have:
```python theme={null}
from datetime import timedelta
import yaml
from airflow import DAG
try:
from airflow.operators.python import PythonOperator
except ModuleNotFoundError:
from airflow.operators.python_operator import PythonOperator
from airflow.utils.dates import days_ago
from metadata.workflow.metadata import MetadataWorkflow
default_args = {
"owner": "user_name",
"email": ["username@org.com"],
"email_on_failure": False,
"retries": 3,
"retry_delay": timedelta(minutes=5),
"execution_timeout": timedelta(minutes=60),
}
CONFIG = """
...
"""
def metadata_ingestion_workflow():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"airflow_metadata_extraction",
default_args=default_args,
description="An example DAG which pushes Airflow data to OM",
start_date=days_ago(1),
is_paused_upon_creation=True,
schedule_interval="*/5 * * * *",
catchup=False,
) as dag:
ingest_task = PythonOperator(
task_id="ingest_using_recipe",
python_callable=metadata_ingestion_workflow,
)
```
## Ingestion Workflow classes
We have different classes for different types of workflows. The logic is always the same, but you will need
to change your import path. The rest of the method calls will remain the same.
For example, for the `Metadata` workflow we'll use:
```python theme={null}
import yaml
from metadata.workflow.metadata import MetadataWorkflow
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
The classes for each workflow type are:
* `Metadata`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Lineage`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
* `Usage`: `from metadata.workflow.usage import UsageWorkflow`
* `dbt`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Profiler`: `from metadata.workflow.profiler import ProfilerWorkflow`
* `Data Quality`: `from metadata.workflow.data_quality import TestSuiteWorkflow`
* `Data Insights`: `from metadata.workflow.data_insight import DataInsightWorkflow`
* `Elasticsearch Reindex`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
## Using the Kubernetes Pod Operator
In this second approach we won't need to install absolutely anything to the GCP Composer environment. Instead,
we will rely on the `KubernetesPodOperator` to use the underlying k8s cluster of Composer.
Then, the code won't directly run using the hosts' environment, but rather inside a container that we created
with only the `openmetadata-ingestion` package.
**Note:** This approach only has the `openmetadata/ingestion-base` ready from version 0.12.1 or higher!
### Prepare the DAG!
```python theme={null}
from datetime import datetime
from airflow import models
from airflow.providers.cncf.kubernetes.operators.kubernetes_pod import KubernetesPodOperator
CONFIG = """
...
"""
with models.DAG(
"ingestion-k8s-operator",
schedule_interval="@once",
start_date=datetime(2021, 1, 1),
catchup=False,
tags=["OpenMetadata"],
) as dag:
KubernetesPodOperator(
task_id="ingest",
name="ingest",
cmds=["python", "main.py"],
image="openmetadata/ingestion-base:2.0.1",
namespace='default',
env_vars={"config": CONFIG, "pipelineType": "metadata"},
dag=dag,
)
```
Some remarks on this example code:
#### Kubernetes Pod Operator
You can name the task as you want (`task_id` and `name`). The important points here are the `cmds`, this should not
be changed, and the `env_vars`. The `main.py` script that gets shipped within the image will load the env vars
as they are shown, so only modify the content of the config YAML, but not this dictionary.
Note that the example uses the image `openmetadata/ingestion-base:2.0.1`. The image version should be aligned with
your OpenMetadata server version to avoid incompatibilities.
```python theme={null}
KubernetesPodOperator(
task_id="ingest",
name="ingest",
cmds=["python", "main.py"],
image="openmetadata/ingestion-base:2.0.1",
namespace='default',
env_vars={"config": config, "pipelineType": "metadata"},
dag=dag,
)
```
You can find more information about the `KubernetesPodOperator` and how to tune its configurations
[here](https://cloud.google.com/composer/docs/how-to/using/using-kubernetes-pod-operator).
Note that depending on the kind of workflow you will be deploying, the YAML configuration will need to updated following
the official OpenMetadata docs, and the value of the `pipelineType` configuration will need to hold one of the following values:
* `metadata`
* `usage`
* `lineage`
* `profiler`
* `TestSuite`
Which are based on the `PipelineType` [JSON Schema definitions](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-spec/src/main/resources/json/schema/entity/services/ingestionPipelines/ingestionPipeline.json#L14)
# Run the ingestion from GitHub Actions
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/github-actions
Configure GitHub Actions for metadata ingestion to run workflows on commits, schedules, or triggers in your CI/CD pipelines.
This page is about running the Ingestion Framework **externally**!
There are mainly 2 ways of running the ingestion:
1. Internally, by managing the workflows from OpenMetadata.
2. Externally, by using any other tool capable of running Python code.
If you are looking for how to manage the ingestion process from OpenMetadata, you can follow
this [doc](/deployment/ingestion/openmetadata).
# Run the ingestion from GitHub Actions
The process to run the ingestion from GitHub Actions is the same as running it from anywhere else.
1. Get the YAML configuration,
2. Prepare the Python Script
3. Schedule the Ingestion
## 1. YAML Configuration
For any connector and workflow, you can pick it up from its doc [page](/v2.0.x/connectors).
## 2. Prepare the Python Script
In the GitHub Action we will just be triggering a custom Python script. This script will:
* Load the secrets from environment variables (we don't want any security risks!),
* Prepare the Workflow class from the Ingestion Framework that contains all the logic on how to run the metadata ingestion,
* Execute the workflow and log the results.
* A simplified version of such script looks like follows:
```python theme={null}
import os
import yaml
from metadata.workflow.metadata import MetadataWorkflow
CONFIG = f"""
source:
type: snowflake
serviceName: snowflake_from_github_actions
serviceConnection:
config:
type: Snowflake
username: {os.getenv('SNOWFLAKE_USERNAME')}
...
"""
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
if __name__ == "__main__":
run()
```
Note how we are securing the credentials using environment variables. You will need to create these env vars in your
GitHub repository. Follow the GitHub [docs](https://docs.github.com/en/actions/security-guides/encrypted-secrets) for
more information on how to create and use Secrets.
In the end, we'll map these secrets to environment variables in the process, that we can pick up with `os.getenv`.
## 3. Schedule the Ingestion
Now that we have all the ingredients, we just need to build a simple GitHub Actions with the following steps:
* Install Python
* Prepare virtual environment with the openmetadata-ingestion package
* Run the script!
* It is as simple as this. Internally the function run we created will be sending the results to the OpenMetadata server, so there's nothing else we need to do here.
A first version of the action could be:
```yaml theme={null}
name: ingest-snowflake
on:
# Any expression you'd like here
schedule:
- cron: '0 */2 * * *'
# If you also want to execute it manually
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
ingest:
runs-on: ubuntu-latest
steps:
# Pick up the repository code, where the script lives
- name: Checkout
uses: actions/checkout@v3
# Prepare Python in the GitHub Agent
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
# Install the dependencies. Make sure that the client version matches the server!
- name: Install Deps
run: |
python -m venv env
source env/bin/activate
pip install "openmetadata-ingestion[snowflake]==2.0.1.0"
- name: Run Ingestion
run: |
source env/bin/activate
python ingestion-github-actions/snowflake_ingestion.py
# Add the env vars we need to load the snowflake credentials
env:
SNOWFLAKE_USERNAME: ${{ secrets.SNOWFLAKE_USERNAME }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SBX_JWT: ${{ secrets.SBX_JWT }}
```
## \[Optional] - Getting Alerts in Slack
A very interesting option that GitHub Actions provide is the ability to get alerts in Slack after our action fails.
This can become specially useful if we want to be notified when our metadata ingestion is not working as expected.
We can use the same setup as above with a couple of slight changes:
```yaml theme={null}
- name: Run Ingestion
id: ingestion
continue-on-error: true
run: |
source env/bin/activate
python ingestion-github-actions/snowflake_ingestion.py
# Add the env vars we need to load the snowflake credentials
env:
SNOWFLAKE_USERNAME: ${{ secrets.SNOWFLAKE_USERNAME }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SBX_JWT: ${{ secrets.SBX_JWT }}
- name: Slack on Failure
if: steps.ingestion.outcome != 'success'
uses: slackapi/slack-github-action@v1.23.0
with:
payload: |
{
"text": "🔥 Metadata ingestion failed! 🔥"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- name: Force failure
if: steps.ingestion.outcome != 'success'
run: |
exit 1
```
We have:
* Marked the `Run Ingestion` step with a specific `id` and with `continue-on-error: true`. If anything happens, we don't want the action to stop.
* We added a step with `slackapi/slack-github-action@v1.23.0`. By passing a Slack Webhook link via a secret, we can send any payload to a
* specific Slack channel. You can find more info on how to set up a Slack Webhook [here](https://api.slack.com/messaging/webhooks).
* If our `ingestion` step fails, we still want to mark the action as failed, so we are forcing the failure we skipped before.
# Run the ingestion from AWS MWAA | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/mwaa
Integrate with MWAA to schedule and execute ingestion workflows using managed Airflow on AWS infrastructure.
This page is about running the Ingestion Framework **externally**!
There are mainly 2 ways of running the ingestion:
1. Internally, by managing the workflows from OpenMetadata.
2. Externally, by using any other tool capable of running Python code.
If you are looking for how to manage the ingestion process from OpenMetadata, you can follow
this [doc](/deployment/ingestion/openmetadata).
# Run the ingestion from AWS MWAA
This page covers using MWAA as the **orchestrator** that runs OpenMetadata ingestion workflows for your other sources, such as databases and dashboards. To extract **MWAA's own pipeline metadata** (its DAGs and runs) into OpenMetadata, see the [MWAA connector documentation](/v2.0.x/connectors/pipeline/airflow/mwaa). In most cases the [REST API connection](/v2.0.x/connectors/pipeline/airflow/rest-api-connection) is the recommended approach and requires nothing installed in MWAA for metadata extraction. Table-level lineage additionally requires the OpenLineage provider configured in MWAA.
When running ingestion workflows from MWAA we have three approaches:
1. Install the openmetadata-ingestion package as a requirement in the Airflow environment. We will then run the process using a `PythonOperator`
2. Configure an ECS cluster and run the ingestion as an `ECSOperator`.
3. Install a plugin and run the ingestion with the `PythonVirtualenvOperator`.
We will now discuss pros and cons of each aspect and how to configure them.
OpenMetadata does not support using Amazon MWAA (Managed Workflows for Apache Airflow) for internal ingestion. This limitation exists because MWAA does not allow the installation of the `openmetadata-ingestion-rest-apis` plugin, which is required to expose the necessary REST APIs for initiating workflows.
## Ingestion Workflows as a Python Operator
### PROs
* It is the simplest approach
* We don’t need to spin up any further infrastructure
### CONs
* We need to install the [openmetadata-ingestion](https://pypi.org/project/openmetadata-ingestion/) package in the MWAA environment
* The installation can clash with existing libraries
* Upgrading the OM version will require to repeat the installation process
To install the package, we need to update the `requirements.txt` file from the MWAA environment to add the following line:
```
openmetadata-ingestion[]==x.y.z
```
Where `x.y.z` is the version of the OpenMetadata ingestion package. Note that the version needs to match the server version. If we are using the server at 2.0.1, then the ingestion package needs to also be 2.0.1.0.
The plugin parameter is a list of the sources that we want to ingest. An example would look like this `openmetadata-ingestion[mysql,snowflake,s3]==2.0.1.0`.
A DAG deployed using a Python Operator would then look like follows
```python theme={null}
import json
from datetime import timedelta
from airflow import DAG
try:
from airflow.operators.python import PythonOperator
except ModuleNotFoundError:
from airflow.operators.python_operator import PythonOperator
from airflow.utils.dates import days_ago
from metadata.workflow.metadata import MetadataWorkflow
default_args = {
"retries": 3,
"retry_delay": timedelta(seconds=10),
"execution_timeout": timedelta(minutes=60),
}
config = """
YAML config
"""
def metadata_ingestion_workflow():
workflow_config = json.loads(config)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"redshift_ingestion",
default_args=default_args,
description="An example DAG which runs a OpenMetadata ingestion workflow",
start_date=days_ago(1),
is_paused_upon_creation=False,
catchup=False,
) as dag:
ingest_task = PythonOperator(
task_id="ingest_redshift",
python_callable=metadata_ingestion_workflow,
)
```
Where you can update the YAML configuration and workflow classes accordingly. accordingly. Further examples on how to
run the ingestion can be found on the documentation (e.g., [Snowflake](/v2.0.x/connectors/database/snowflake)).
## Ingestion Workflow classes
We have different classes for different types of workflows. The logic is always the same, but you will need
to change your import path. The rest of the method calls will remain the same.
For example, for the `Metadata` workflow we'll use:
```python theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}
import yaml
from metadata.workflow.metadata import MetadataWorkflow
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
The classes for each workflow type are:
* `Metadata`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Lineage`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
* `Usage`: `from metadata.workflow.usage import UsageWorkflow`
* `dbt`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Profiler`: `from metadata.workflow.profiler import ProfilerWorkflow`
* `Data Quality`: `from metadata.workflow.data_quality import TestSuiteWorkflow`
* `Data Insights`: `from metadata.workflow.data_insight import DataInsightWorkflow`
* `Elasticsearch Reindex`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
## Ingestion Workflows as an ECS Operator
### PROs
* Completely isolated environment
* Easy to update each version
### CONs
* We need to set up an ECS cluster and the required policies in MWAA to connect to ECS and handle Log Groups.
We will now describe the steps, following the official AWS documentation.
### 1. Create an ECS Cluster & Task Definition
* The cluster needs a task to run in `FARGATE` mode.
* The required image is `docker.open-metadata.org/openmetadata/ingestion-base:x.y.z`
* The same logic as above applies. The `x.y.z` version needs to match the server version. For example, `docker.open-metadata.org/openmetadata/ingestion-base:2.0.1`
We have tested this process with a Task Memory of 512MB and Task CPU (unit) of 256. This can be tuned depending on the amount of metadata that needs to be ingested.
When creating the Task Definition, take notes on the **log groups** assigned, as we will need them to prepare the MWAA Executor Role policies.
For example, if in the JSON from the Task Definition we see:
```json theme={null}
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-create-group": "true",
"awslogs-group": "/ecs/openmetadata",
"awslogs-region": "us-east-2",
"awslogs-stream-prefix": "ecs"
},
"secretOptions": []
}
```
We'll need to use the `/ecs/openmetadata` below when configuring the policies.
### 2. Task Definition ARN & Networking
1. From the AWS Console, copy your task definition ARN. It will look something like this `arn:aws:ecs:::task-definition/:`.
2. Get the network details on where the task should execute. We will be using a JSON like:
```json theme={null}
{
"awsvpcConfiguration": {
"subnets": [
"subnet-xxxyyyzzz",
"subnet-xxxyyyzzz"
],
"securityGroups": [
"sg-xxxyyyzzz"
],
"assignPublicIp": "ENABLED"
}
}
```
If you want to extract MWAA metadata, add the **VPC**, **subnets** and **security groups** used when setting up MWAA. We need to
be in the same network environment as MWAA to reach the underlying database.
### 3. Update MWAA Executor Role policies
* Identify your MWAA executor role. This can be obtained from the details view of your MWAA environment.
* Add the following two policies to the role, the first with ECS permissions:
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ecs:RunTask",
"ecs:DescribeTasks"
],
"Resource": "*"
},
{
"Action": "iam:PassRole",
"Effect": "Allow",
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"iam:PassedToService": "ecs-tasks.amazonaws.com"
}
}
}
]
}
```
And for the Log Group permissions
```json theme={null}
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:GetLogRecord",
"logs:GetLogGroupFields",
"logs:GetQueryResults"
],
"Resource": [
"arn:aws:logs:::log-group:*",
"arn:aws:logs:*:*:log-group::*"
]
}
```
Note how you need to replace the `region`, `account-id` and the `log group` names for your Airflow Environment and ECS.
### 4. Prepare the DAG
A DAG created using the ECS Operator will then look like this:
```python theme={null}
from airflow import DAG
# If using Airflow < 2.5
# from airflow.providers.amazon.aws.operators.ecs import ECSOperator
# If using Airflow > 2.5
from airflow.providers.amazon.aws.operators.ecs import EcsRunTaskOperator
from airflow.utils.dates import days_ago
CLUSTER_NAME="openmetadata-ingestion" # Replace value for CLUSTER_NAME with your information.
CONTAINER_NAME="openmetadata-ingestion" # Replace value for CONTAINER_NAME with your information.
LAUNCH_TYPE="FARGATE"
TASK_DEFINITION = "arn:aws:ecs:::task-definition/:"
NETWORK_CONFIG = {
"awsvpcConfiguration": {
"subnets": [
"subnet-xxxyyyzzz",
"subnet-xxxyyyzzz"
],
"securityGroups": [
"sg-xxxyyyzzz"
],
"assignPublicIp": "ENABLED"
}
}
config = """
YAML config
"""
with DAG(
dag_id="ecs_fargate_dag",
schedule_interval=None,
catchup=False,
start_date=days_ago(1),
is_paused_upon_creation=True,
) as dag:
ecs_operator_task = EcsRunTaskOperator(
task_id = "ecs_ingestion_task",
dag=dag,
cluster=CLUSTER_NAME,
task_definition=TASK_DEFINITION,
launch_type=LAUNCH_TYPE,
overrides={
"containerOverrides":[
{
"name":CONTAINER_NAME,
"command":["python", "main.py"],
"environment": [
{
"name": "config",
"value": config
},
{
"name": "pipelineType",
"value": "metadata"
},
],
},
],
},
network_configuration=NETWORK_CONFIG,
awslogs_group="/ecs/ingest",
awslogs_stream_prefix=f"ecs/{CONTAINER_NAME}",
)
```
Note that depending on the kind of workflow you will be deploying, the YAML configuration will need to updated following
the official OpenMetadata docs, and the value of the `pipelineType` configuration will need to hold one of the following values:
* `metadata`
* `usage`
* `lineage`
* `profiler`
* `TestSuite`
Which are based on the `PipelineType` [JSON Schema definitions](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-spec/src/main/resources/json/schema/entity/services/ingestionPipelines/ingestionPipeline.json#L14)
Moreover, one of the imports will depend on the MWAA Airflow version you are using:
* If using Airflow \< 2.5: `from airflow.providers.amazon.aws.operators.ecs import ECSOperator`
* If using Airflow > 2.5: `from airflow.providers.amazon.aws.operators.ecs import EcsRunTaskOperator`
Make sure to update the `ecs_operator_task` task call accordingly.
For the Python VirtualenvOperator approach, see [MWAA with Python VirtualenvOperator](/v2.0.x/deployment/ingestion/external/mwaa/virtualenv).
# MWAA Ingestion with Python VirtualenvOperator | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/external/mwaa/virtualenv
Configure the PythonVirtualenvOperator in AWS MWAA for isolated ingestion workflow execution without library conflicts.
# Ingestion Workflows as a Python Virtualenv Operator
## PROs
* Installation does not clash with existing libraries
* Simpler than ECS
## CONs
* We need to install an additional plugin in MWAA
* DAGs take longer to run due to needing to set up the virtualenv from scratch for each run.
We need to update the `requirements.txt` file from the MWAA environment to add the following line:
```
virtualenv
```
Then, we need to set up a custom plugin in MWAA. Create a file named virtual\_python\_plugin.py. Note that you may need to update the python version (eg, python3.7 -> python3.10) depending on what your MWAA environment is running.
```python theme={null}
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
from airflow.plugins_manager import AirflowPlugin
import airflow.utils.python_virtualenv
from typing import List
import os
def _generate_virtualenv_cmd(tmp_dir: str, python_bin: str, system_site_packages: bool) -> List[str]:
cmd = ['python3', '/usr/local/airflow/.local/lib/python3.7/site-packages/virtualenv', tmp_dir]
if system_site_packages:
cmd.append('--system-site-packages')
if python_bin is not None:
cmd.append(f'--python={python_bin}')
return cmd
airflow.utils.python_virtualenv._generate_virtualenv_cmd = _generate_virtualenv_cmd
os.environ["PATH"] = f"/usr/local/airflow/.local/bin:{os.environ['PATH']}"
class VirtualPythonPlugin(AirflowPlugin):
name = 'virtual_python_plugin'
```
This is modified from the [AWS sample](https://docs.aws.amazon.com/mwaa/latest/userguide/samples-virtualenv.html).
Next, create the plugins.zip file and upload it according to [AWS docs](https://docs.aws.amazon.com/mwaa/latest/userguide/configuring-dag-import-plugins.html). You will also need to [disable lazy plugin loading in MWAA](https://docs.aws.amazon.com/mwaa/latest/userguide/samples-virtualenv.html#samples-virtualenv-airflow-config).
A DAG deployed using the PythonVirtualenvOperator would then look like:
```python theme={null}
from datetime import timedelta
from airflow import DAG
from airflow.operators.python import PythonVirtualenvOperator
from airflow.utils.dates import days_ago
default_args = {
"retries": 3,
"retry_delay": timedelta(seconds=10),
"execution_timeout": timedelta(minutes=60),
}
def metadata_ingestion_workflow():
from metadata.workflow.metadata import MetadataWorkflow
import yaml
config = """
YAML config
"""
workflow_config = yaml.safe_load(config)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
with DAG(
"redshift_ingestion",
default_args=default_args,
description="An example DAG which runs a OpenMetadata ingestion workflow",
start_date=days_ago(1),
is_paused_upon_creation=False,
catchup=False,
) as dag:
ingest_task = PythonVirtualenvOperator(
task_id="ingest_redshift",
python_callable=metadata_ingestion_workflow,
requirements=['openmetadata-ingestion[redshift]~=2.0.1.0',
'apache-airflow==2.4.3', # note, v2.4.3 is the first version that does not conflict with OpenMetadata's 'tabulate' requirements
'apache-airflow-providers-amazon==6.0.0', # Amazon Airflow provider is necessary for MWAA
'watchtower',],
system_site_packages=False,
dag=dag,
)
```
Where you can update the YAML configuration and workflow classes accordingly. Further examples on how to
run the ingestion can be found on the documentation (e.g., [Snowflake](/v2.0.x/connectors/database/snowflake)).
You will also need to determine the OpenMetadata ingestion extras and Airflow providers you need. The OpenMetadata ingestion package should match your server minor version. For example, use a `2.0.x` ingestion package with a `2.0.x` server, and include the connector extras required by your YAML, such as `openmetadata-ingestion[mysql,snowflake,s3]~=2.0.1.0`.
For Airflow providers, you will want to pull the provider versions from [the matching constraints file](https://raw.githubusercontent.com/apache/airflow/constraints-2.4.3/constraints-3.7.txt). Since this example installs Airflow Providers v2.4.3 on Python 3.7, we use that constraints file.
Also note that the ingestion workflow function must be entirely self-contained as it will run by itself in the virtualenv. Any imports it needs, including the configuration, must exist within the function itself.
## Ingestion Workflow classes
We have different classes for different types of workflows. The logic is always the same, but you will need
to change your import path. The rest of the method calls will remain the same.
For example, for the `Metadata` workflow we'll use:
```python theme={null}
import yaml
from metadata.workflow.metadata import MetadataWorkflow
def run():
workflow_config = yaml.safe_load(CONFIG)
workflow = MetadataWorkflow.create(workflow_config)
workflow.execute()
workflow.raise_from_status()
workflow.print_status()
workflow.stop()
```
The classes for each workflow type are:
* `Metadata`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Lineage`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
* `Usage`: `from metadata.workflow.usage import UsageWorkflow`
* `dbt`: `from metadata.workflow.metadata import MetadataWorkflow`
* `Profiler`: `from metadata.workflow.profiler import ProfilerWorkflow`
* `Data Quality`: `from metadata.workflow.data_quality import TestSuiteWorkflow`
* `Data Insights`: `from metadata.workflow.data_insight import DataInsightWorkflow`
* `Elasticsearch Reindex`: `from metadata.workflow.metadata import MetadataWorkflow` (same as metadata)
# Kubernetes Native Orchestrator
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/kubernetes
Run ingestion pipelines using native Kubernetes Jobs and CronJobs without requiring Apache Airflow.
# Kubernetes Native Orchestrator
Starting with OpenMetadata 1.12, you can run ingestion pipelines directly using **native Kubernetes**,
eliminating the need for Apache Airflow. This is ideal for organizations that:
* Already run workloads on Kubernetes and prefer native solutions
* Don't need the full feature set of Apache Airflow
## Orchestration Modes
The Kubernetes orchestrator supports two modes for running ingestion pipelines:
### Option 1: OMJob Operator (Recommended)
Uses custom Kubernetes CRDs (`OMJob` and `CronOMJob`) managed by the OpenMetadata operator.
| Resource | Description |
| ------------- | ------------------------------------------------------ |
| **CronOMJob** | Scheduled pipelines - runs on a cron schedule |
| **OMJob** | On-demand pipelines - one-off execution when triggered |
**Recommended for production.** The OMJob Operator provides guaranteed exit handler execution and failure diagnostics.
**Advantages:**
* **Exit Handler Guarantee**: Even if the ingestion pod crashes (OOMKilled, node failure, etc.), the operator ensures pipeline status is always reported back to OpenMetadata
* **Failure Diagnostics**: Automatically collects detailed error context from pod logs and events when pipelines fail
* **Pod Lifecycle Monitoring**: The operator watches pod events and updates pipeline status in real-time
**Requirements:**
* Elevated permissions to install Custom Resource Definitions (CRDs)
* The OMJob Operator deployment running in your cluster
### Option 2: Native Kubernetes Jobs
Uses standard Kubernetes resources (`Job` and `CronJob`) without any custom CRDs.
| Resource | Description |
| ----------- | ------------------------------------------------------ |
| **CronJob** | Scheduled pipelines - runs on a cron schedule |
| **Job** | On-demand pipelines - one-off execution when triggered |
**Advantages:**
* No CRD installation required - uses only built-in Kubernetes resources
* Works in environments with restricted permissions
* Simpler setup
**Limitations:**
* No guaranteed exit handler - if a pod is killed unexpectedly, status updates may not reach OpenMetadata
* No automatic failure diagnostics
## Features
Pipelines run as standard Kubernetes Jobs, making them easy to monitor with existing K8s tooling.
Pipeline status is automatically reported back to OpenMetadata, including success/failure details.
When pipelines fail, detailed diagnostics are collected from pod logs and events. **(OMJob Operator only)**
Configure CPU, memory, node selectors, and security contexts for ingestion pods.
***
## Setup Option 1: OMJob Operator (Recommended)
This setup uses custom CRDs for guaranteed exit handler execution and failure diagnostics.
### Prerequisites
1. **OpenMetadata deployed on Kubernetes** (Helm chart recommended)
2. **Permissions to install CRDs** in your cluster
3. **Ingestion image** accessible from your cluster (`docker.getcollate.io/openmetadata/ingestion-base`)
### Helm Values Configuration
Always pin `ingestionImage` to the same version as your OpenMetadata server — never use `:latest`. The `:latest` tag moves whenever a new release is published; any Kubernetes node without the previous image cached will silently pull the newer version on the next scheduled job, causing a server/client version mismatch. The ingestion major version must match the server major version (for example, a 2.0.x server requires a 2.0.x ingestion image).
```yaml theme={null}
# Enable the OMJob Operator
omjobOperator:
enabled: true
image:
repository: docker.getcollate.io/openmetadata/omjob-operator
tag: "2.0.1"
pullPolicy: IfNotPresent
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
openmetadata:
config:
pipelineServiceClientConfig:
enabled: true
type: "k8s"
metadataApiEndpoint: http://openmetadata:8585/api
k8s:
# Use the OMJob Operator
useOMJobOperator: true
# Container image for ingestion jobs
ingestionImage: "docker.getcollate.io/openmetadata/ingestion-base:2.0.1"
imagePullPolicy: "IfNotPresent"
imagePullSecrets: ""
# Service account for ingestion jobs
serviceAccountName: "openmetadata-ingestion"
# Job lifecycle settings
ttlSecondsAfterFinished: 86400 # Keep completed jobs for 24 hours
activeDeadlineSeconds: 7200 # Max 2 hour runtime
backoffLimit: 3 # Retry up to 3 times
# Job history
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
# Pod security context
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
runAsNonRoot: true
# Resource limits
resources:
limits:
cpu: "2"
memory: "4Gi"
requests:
cpu: "500m"
memory: "1Gi"
# Enable failure diagnostics (only works with OMJob Operator)
enableFailureDiagnostics: true
# RBAC - set to false if managed externally
rbac:
enabled: true
```
### Required RBAC Permissions
When using the OMJob Operator, additional permissions are needed for the custom resources:
```yaml theme={null}
rules:
# Pod management for pipeline jobs and diagnostics
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# ConfigMaps for pipeline configuration
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# Secrets for pipeline credentials
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# Events for diagnostics
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list"]
# Jobs and CronJobs management
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# OMJob CRDs
- apiGroups: ["pipelines.openmetadata.org"]
resources: ["omjobs"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: ["pipelines.openmetadata.org"]
resources: ["omjobs/status"]
verbs: ["get", "patch"]
- apiGroups: ["pipelines.openmetadata.org"]
resources: ["cronomjobs"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: ["pipelines.openmetadata.org"]
resources: ["cronomjobs/status"]
verbs: ["get", "patch"]
```
***
## Setup Option 2: Native Kubernetes Jobs
This setup uses standard Kubernetes Jobs and CronJobs without any custom CRDs.
### Prerequisites
1. **OpenMetadata deployed on Kubernetes** (Helm chart recommended)
2. **RBAC permissions** for the OpenMetadata service account to manage Jobs, CronJobs, ConfigMaps, and Secrets
3. **Ingestion image** accessible from your cluster (`docker.getcollate.io/openmetadata/ingestion-base`)
### Helm Values Configuration
Always pin `ingestionImage` to the same version as your OpenMetadata server — never use `:latest`. The `:latest` tag moves whenever a new release is published; any Kubernetes node without the previous image cached will silently pull the newer version on the next scheduled job, causing a server/client version mismatch. The ingestion major version must match the server major version (for example, a 2.0.x server requires a 2.0.x ingestion image).
```yaml theme={null}
openmetadata:
config:
pipelineServiceClientConfig:
enabled: true
type: "k8s"
metadataApiEndpoint: http://openmetadata:8585/api
k8s:
# Do NOT use the OMJob Operator (default)
useOMJobOperator: false
# Container image for ingestion jobs
ingestionImage: "docker.getcollate.io/openmetadata/ingestion-base:2.0.1"
imagePullPolicy: "IfNotPresent"
imagePullSecrets: ""
# Service account for ingestion jobs
serviceAccountName: "openmetadata-ingestion"
# Job lifecycle settings
ttlSecondsAfterFinished: 86400
activeDeadlineSeconds: 7200
backoffLimit: 3
# Job history
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
# Pod security context
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
runAsNonRoot: true
# Resource limits
resources:
limits:
cpu: "2"
memory: "4Gi"
requests:
cpu: "500m"
memory: "1Gi"
# RBAC - set to false if managed externally
rbac:
enabled: true
```
### Required RBAC Permissions
```yaml theme={null}
rules:
# Pod management for pipeline jobs
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# ConfigMaps for pipeline configuration
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# Secrets for pipeline credentials
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
# Events for diagnostics
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list"]
# Jobs and CronJobs management
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
```
***
For validating your setup, viewing pipeline logs, troubleshooting, and migrating from Airflow, see the [Operations & Troubleshooting](/v2.0.x/deployment/ingestion/kubernetes/troubleshooting) guide.
# Kubernetes Orchestrator Operations & Troubleshooting
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/kubernetes/troubleshooting
Validate, monitor, and troubleshoot the Kubernetes native orchestrator for OpenMetadata ingestion pipelines.
# Kubernetes Orchestrator Operations & Troubleshooting
This guide covers validating your Kubernetes orchestrator setup, viewing pipeline logs, troubleshooting common issues, and migrating from Airflow. For initial setup, see the [Kubernetes Native Orchestrator](/v2.0.x/deployment/ingestion/kubernetes) guide.
## Validating the Setup
### 1. Check Service Health
Navigate to **Settings → Preferences → Health** in the OpenMetadata UI to verify the Kubernetes pipeline client is properly configured and can connect to the Kubernetes API.
### 2. Deploy a Test Pipeline
Create a simple metadata ingestion pipeline from the OpenMetadata UI. The pipeline should:
* Show "Deployed" status
* Display the Kubernetes Job/CronJob name
### 3. Check Kubernetes Resources
```bash theme={null}
# List ingestion ConfigMaps
kubectl get configmaps -l app.kubernetes.io/managed-by=openmetadata
# List ingestion Jobs
kubectl get jobs -l app.kubernetes.io/managed-by=openmetadata
# List ingestion CronJobs (native mode)
kubectl get cronjobs -l app.kubernetes.io/managed-by=openmetadata
# List CronOMJobs (operator mode)
kubectl get cronomjobs -l app.kubernetes.io/managed-by=openmetadata
# View pod logs
kubectl logs -l app.kubernetes.io/component=ingestion -f
```
## Pipeline Logs
Pipeline logs are retrieved directly from Kubernetes pod logs. OpenMetadata implements log pagination for large log files, splitting them into \~1MB chunks for efficient retrieval.
To view logs:
1. Navigate to **Settings → Services → Agents**
2. Select your pipeline
3. Click on Logs to view them directly on OpenMetadata UI
Alternatively, view logs directly with kubectl:
```bash theme={null}
kubectl logs job/ -c main
```
## Troubleshooting
### Server/Client Version Mismatch
If you see an error like `server version X does not match client version Y`, the ingestion job is running a different version of the ingestion library than the OpenMetadata server expects.
**Most common cause:** `ingestionImage` is set to `:latest` in your Helm values. When a new OpenMetadata release is published, `:latest` moves to that version. Any Kubernetes node without the old image cached will pull the new one on the next job run, silently upgrading the ingestion client while your server stays on the previous version.
**Fix:** Pin `ingestionImage` to the exact version of your server:
```yaml theme={null}
k8s:
ingestionImage: "docker.getcollate.io/openmetadata/ingestion-base:2.0.1"
```
Replace `2.0.1` with your actual server version. The ingestion major version must match the server major version — for example, a 2.0.x server requires a 2.0.x ingestion image.
After updating, redeploy OpenMetadata:
```bash theme={null}
helm upgrade openmetadata open-metadata/openmetadata -f values.yaml -n openmetadata
```
### Pipeline stuck in "Queued" state
If the pipeline cannot start and remains in "Queued" state, check if the pod can be scheduled:
```bash theme={null}
kubectl get pods -l app.kubernetes.io/pipeline=
kubectl describe pod
```
Common causes:
* Image pull errors (check `imagePullSecrets`)
* Insufficient cluster resources (increase CPU/memory limits or add nodes)
* Node selector constraints
### Permission Denied Errors
If you see RBAC-related errors:
```bash theme={null}
kubectl auth can-i create jobs --as=system:serviceaccount::openmetadata
```
Ensure the service account has the required permissions.
### Ingestion Pod Crashes (OOMKilled)
Increase memory limits in the Helm values:
```yaml theme={null}
k8s:
resources:
limits:
memory: "8Gi"
requests:
memory: "2Gi"
```
### CronJob Not Triggering
Check CronJob status and events:
```bash theme={null}
kubectl get cronjob -o yaml
kubectl describe cronjob
```
Common issues:
* Invalid cron expression
* `startingDeadlineSeconds` too short
* Concurrency policy blocking execution
## Migrating from Airflow
If you're migrating from Airflow to the Kubernetes orchestrator:
1. **Stop existing Airflow-managed pipelines** - Disable or delete pipelines managed by Airflow
2. **Update Helm values** - Switch `type: "airflow"` to `type: "k8s"`
3. **Redeploy OpenMetadata** - Apply the new Helm configuration
4. **Re-deploy pipelines** - Navigate to each pipeline and click "Deploy" to create the Kubernetes resources
The migration does not automatically transfer pipeline schedules. You'll need to re-configure and deploy each pipeline after switching to the Kubernetes orchestrator.
## Comparison: Airflow vs Kubernetes Orchestrator
| Feature | Airflow | K8s Native | K8s with OMJob Operator |
| -------------------------- | --------------------------- | ------------------------- | ------------------------- |
| **Infrastructure** | Requires Airflow deployment | Uses existing K8s cluster | Uses existing K8s cluster |
| **CRD Installation** | N/A | Not required | Required |
| **Exit Handler Guarantee** | ✅ Airflow handles | ❌ Best effort | ✅ Guaranteed |
| **Failure Diagnostics** | ❌ | ❌ | ✅ |
| **UI for DAGs** | ✅ Airflow UI | OpenMetadata UI | OpenMetadata UI |
| **Resource efficiency** | Always running | Jobs on-demand | Jobs on-demand |
| **K8s-native monitoring** | Extra setup | ✅ Native | ✅ Native |
# Run the ingestion from the OpenMetadata UI
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/openmetadata
Learn how to deploy and configure OpenMetadata Ingestion pipelines. Complete setup guide with connectors, scheduling, and best practices.
# Run the ingestion from the OpenMetadata UI
When you create and manage ingestion workflows from OpenMetadata, under the hood we need to communicate
with an orchestration system. It does not matter which one, but we need it to have a set of APIs to create,
run, fetch the logs, etc. of our workflows.
OpenMetadata supports two orchestration backends:
| Orchestrator | Description |
| --------------------- | ----------------------------------------------------------------------------- |
| **Apache Airflow** | The traditional approach - uses Airflow DAGs to manage pipelines |
| **Kubernetes Native** | **New in 1.12** - Uses native K8s Jobs and CronJobs without requiring Airflow |
Continue below for Airflow configuration
Use native K8s Jobs (no Airflow required)
***
## Airflow as Orchestrator
Out of the box, OpenMetadata comes with integration for Airflow. In this guide, we will show you how to manage
ingestions from OpenMetadata by linking it to an Airflow service.
Advanced note for developers: We have an [interface](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-spec/src/main/java/org/openmetadata/service/clients/pipeline/PipelineServiceClient.java)
that can be extended to bring support to any other orchestrator. You can follow the implementation we have for [Airflow](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/clients/pipeline/airflow/AirflowRESTClient.java)
or [Kubernetes](https://github.com/open-metadata/OpenMetadata/blob/main/openmetadata-service/src/main/java/org/openmetadata/service/clients/pipeline/k8s/K8sPipelineClient.java) as starting points.
1. **If you do not have an Airflow service** up and running on your platform, we provide a custom
[Docker](https://hub.docker.com/r/openmetadata/ingestion) image, which already contains the OpenMetadata ingestion
packages and custom [Airflow APIs](https://github.com/open-metadata/openmetadata-airflow-apis) to
deploy Workflows from the UI as well. **This is the simplest approach**.
2. If you already have Airflow up and running and want to use it for the metadata ingestion, you will
need to install the ingestion modules to the host. You can find more information on how to do this
in the Custom Airflow Installation section.
## Airflow permissions
These are the permissions required by the user that will manage the communication between the OpenMetadata Server
and Airflow's Webserver:
```
[
(permissions.ACTION_CAN_DELETE, permissions.RESOURCE_DAG),
(permissions.ACTION_CAN_CREATE, permissions.RESOURCE_DAG),
(permissions.ACTION_CAN_EDIT, permissions.RESOURCE_DAG),
(permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG),
]
```
`User` permissions is enough for these requirements.
You can find more information on Airflow's Access Control [here](https://airflow.apache.org/docs/apache-airflow/stable/security/access-control.html).
## Shared Volumes
The Airflow Webserver, Scheduler and Workers - if using a distributed setup - need to have access to the same shared volumes
with RWX permissions.
We have specific instructions on how to set up the shared volumes in Kubernetes depending on your cloud deployment [here](/v2.0.x/deployment/kubernetes).
## Using the OpenMetadata Ingestion Image
If you are using our `openmetadata/ingestion` Docker image, there is just one thing to do: Configure the OpenMetadata server.
The OpenMetadata server takes all its configurations from a YAML file. You can find them in our [repo](https://github.com/open-metadata/OpenMetadata/tree/main/conf). In
`openmetadata.yaml`, update the `pipelineServiceClientConfiguration` section accordingly.
```yaml theme={null}
# For Bare Metal Installations
[...]
pipelineServiceClientConfiguration:
className: ${PIPELINE_SERVICE_CLIENT_CLASS_NAME:-"org.openmetadata.service.clients.pipeline.airflow.AirflowRESTClient"}
apiEndpoint: ${PIPELINE_SERVICE_CLIENT_ENDPOINT:-http://localhost:8080}
metadataApiEndpoint: ${SERVER_HOST_API_URL:-http://localhost:8585/api}
hostIp: ${PIPELINE_SERVICE_CLIENT_HOST_IP:-""}
verifySSL: ${PIPELINE_SERVICE_CLIENT_VERIFY_SSL:-"no-ssl"} # Possible values are "no-ssl", "ignore", "validate"
sslConfig:
certificatePath: ${PIPELINE_SERVICE_CLIENT_SSL_CERT_PATH:-""} # Local path for the Pipeline Service Client
# Default required parameters for Airflow as Pipeline Service Client
parameters:
username: ${AIRFLOW_USERNAME:-admin}
password: ${AIRFLOW_PASSWORD:-admin}
timeout: ${AIRFLOW_TIMEOUT:-10}
[...]
```
If using Docker, make sure that you are passing the correct environment variables:
```env theme={null}
PIPELINE_SERVICE_CLIENT_ENDPOINT: ${PIPELINE_SERVICE_CLIENT_ENDPOINT:-http://ingestion:8080}
SERVER_HOST_API_URL: ${SERVER_HOST_API_URL:-http://openmetadata-server:8585/api}
```
If using Kubernetes, make sure that you are passing the correct values to Helm Chart:
```yaml theme={null}
# Custom OpenMetadata Values.yaml
openmetadata:
config:
pipelineServiceClientConfig:
enabled: true
# endpoint url for airflow
apiEndpoint: http://openmetadata-dependencies-web.default.svc.cluster.local:8080
auth:
username: admin
password:
secretRef: airflow-secrets
secretKey: openmetadata-airflow-password
```
## Custom Airflow Installation
* The `openmetadata-ingestion` package requires Python 3.10 or later.
* The `airflow` extra installs Apache Airflow 3.3.1 for the 2.0 release line. Use this extra when creating a new Airflow environment.
* If you already run Airflow, do not install the `airflow` extra. Install only the connector extras you need. The `all` extra intentionally excludes Airflow, so it does not install or upgrade Airflow on an existing host.
You will need to follow three steps:
1. Install the `openmetadata-ingestion` package with the connector plugins that you need.
2. Install the `openmetadata-managed-apis` to deploy our custom APIs on top of Airflow.
3. Configure the Airflow environment.
4. Configure the OpenMetadata server.
### 1. Install the Connector Modules
The current approach we are following here is preparing the metadata ingestion DAGs as `PythonOperators`. This means that
the packages need to be present in the Airflow instances.
You will need to install:
```python theme={null}
pip3 install "openmetadata-ingestion[]==x.y.z"
```
And then run the DAG as explained in each [Connector](/v2.0.x/connectors), where `x.y.z` is the same version of your
OpenMetadata server. For example, if you are on version 1.0.0, then you can install the `openmetadata-ingestion`
with versions `1.0.0.*`, e.g., `1.0.0.0`, `1.0.0.1`, etc., but not `1.0.1.x`.
You can also install `openmetadata-ingestion[all]==x.y.z`, which will bring the requirements to run any connector. This extra intentionally excludes the separate `airflow` extra; install `openmetadata-ingestion[airflow]==x.y.z` only when you want the packaged Airflow environment.
You can check the [Connector Modules](/v2.0.x/connectors) guide above to learn how to install the `openmetadata-ingestion` package with the
necessary plugins. They are necessary because even if we install the APIs, the Airflow instance needs to have the
required libraries to connect to each source.
### 2. Install the Airflow APIs
The `openmetadata-ingestion-apis` has a dependency on `apache-airflow>=2.2.2`. Please make sure that
your host satisfies such requirement. Only installing the `openmetadata-ingestion-apis` won't result
in a proper full Airflow installation. For that, please follow the Airflow [docs](https://airflow.apache.org/docs/apache-airflow/stable/installation/index.html).
The goal of this module is to add some HTTP endpoints that the UI calls for deploying the Airflow DAGs.
The first step can be achieved by running:
```python theme={null}
pip3 install "openmetadata-managed-apis==x.y.z"
```
Here, the same versioning logic applies: `x.y.z` is the same version of your
OpenMetadata server. For example, if you are on version 1.0.0, then you can install the `openmetadata-managed-apis`
with versions `1.0.0.*`, e.g., `1.0.0.0`, `1.0.0.1`, etc., but not `1.0.1.x`.
### 3. Configure the Airflow environment
The ingestion image is built on Airflow's base image, ensuring it includes all necessary requirements to run Airflow. For Kubernetes deployments, the setup uses community Airflow charts with a modified base image, enabling it to function seamlessly as a **scheduler**, **webserver**, and **worker**.
We need a couple of settings:
#### AIRFLOW\_HOME
The APIs will look for the `AIRFLOW_HOME` environment variable to place the dynamically generated DAGs. Make
sure that the variable is set and reachable from Airflow.
#### Airflow APIs Basic Auth
Note that the integration of OpenMetadata with Airflow requires Basic Auth in the APIs. Make sure that your
Airflow configuration supports that. You can read more about it [here](https://airflow.apache.org/docs/apache-airflow/stable/security/api.html).
A possible approach here is to update your `airflow.cfg` entries for Airflow 3.x:
```
[api]
auth_backends = airflow.api_fastapi.auth.backend.basic_auth
```
#### DAG Generated Configs
Every time a DAG is created from OpenMetadata, it will also create a JSON file with some information about the
workflow that needs to be executed. By default, these files live under `${AIRFLOW_HOME}/dag_generated_configs`, which
in most environments translates to `/opt/airflow/dag_generated_configs`.
You can change this directory by specifying the environment variable `AIRFLOW__OPENMETADATA_AIRFLOW_APIS__DAG_GENERATED_CONFIGS`
or updating the `airflow.cfg` with:
```cfg theme={null}
[openmetadata_airflow_apis]
dag_generated_configs=/opt/airflow/dag_generated_configs
```
A safe way to validate if the configuration is properly set in Airflow is to run:
```bash theme={null}
airflow config get-value openmetadata_airflow_apis dag_generated_configs
```
### 4. Configure in the OpenMetadata Server
After installing the Airflow APIs, you will need to update your OpenMetadata Server.
The OpenMetadata server takes all its configurations from a YAML file. You can find them in our [repo](https://github.com/open-metadata/OpenMetadata/tree/main/conf). In
`openmetadata.yaml`, update the `pipelineServiceClientConfiguration` section accordingly.
```yaml theme={null}
# For Bare Metal Installations
[...]
pipelineServiceClientConfiguration:
className: ${PIPELINE_SERVICE_CLIENT_CLASS_NAME:-"org.openmetadata.service.clients.pipeline.airflow.AirflowRESTClient"}
apiEndpoint: ${PIPELINE_SERVICE_CLIENT_ENDPOINT:-http://localhost:8080}
metadataApiEndpoint: ${SERVER_HOST_API_URL:-http://localhost:8585/api}
hostIp: ${PIPELINE_SERVICE_CLIENT_HOST_IP:-""}
verifySSL: ${PIPELINE_SERVICE_CLIENT_VERIFY_SSL:-"no-ssl"} # Possible values are "no-ssl", "ignore", "validate"
sslConfig:
certificatePath: ${PIPELINE_SERVICE_CLIENT_SSL_CERT_PATH:-""} # Local path for the Pipeline Service Client
# Default required parameters for Airflow as Pipeline Service Client
parameters:
username: ${AIRFLOW_USERNAME:-admin}
password: ${AIRFLOW_PASSWORD:-admin}
timeout: ${AIRFLOW_TIMEOUT:-10}
[...]
```
If using Docker, make sure that you are passing the correct environment variables:
```env theme={null}
PIPELINE_SERVICE_CLIENT_ENDPOINT: ${PIPELINE_SERVICE_CLIENT_ENDPOINT:-http://ingestion:8080}
SERVER_HOST_API_URL: ${SERVER_HOST_API_URL:-http://openmetadata-server:8585/api}
```
If using Kubernetes, make sure that you are passing the correct values to Helm Chart:
```yaml theme={null}
# Custom OpenMetadata Values.yaml
openmetadata:
config:
pipelineServiceClientConfig:
enabled: true
# endpoint url for airflow
apiEndpoint: http://openmetadata-dependencies-web.default.svc.cluster.local:8080
auth:
username: admin
password:
secretRef: airflow-secrets
secretKey: openmetadata-airflow-password
```
***
For installation validation, Git Sync guidance, SSL configuration, and troubleshooting Airflow pipeline issues, see the [Airflow Troubleshooting & Advanced](/v2.0.x/deployment/ingestion/openmetadata/troubleshooting) guide.
# Airflow Troubleshooting & Advanced | OpenMetadata Ingestion
Source: https://docs.open-metadata.org/v2.0.x/deployment/ingestion/openmetadata/troubleshooting
Validate your Airflow installation, configure Git Sync and SSL, and troubleshoot common ingestion pipeline deployment issues.
# Airflow Troubleshooting & Advanced
This page covers installation validation, Git Sync guidance, SSL configuration, and troubleshooting for Airflow-based ingestion pipelines. For setup and configuration, see the [OpenMetadata Ingestion Overview](/v2.0.x/deployment/ingestion/openmetadata).
## Validating the installation
What we need to verify here is that the OpenMetadata server can reach the Airflow APIs endpoints
(wherever they live: bare metal, containers, k8s pods...). One way to ensure that is to connect to the deployment
hosting your OpenMetadata server and running a query against the `/health` endpoint. For example:
```bash theme={null}
$ curl -XGET ${PIPELINE_SERVICE_CLIENT_ENDPOINT}/api/v1/openmetadata/health
{"status": "healthy", "version": "x.y.z"}
```
It is important to do this validation passing the command as is (i.e., `curl -XGET ${PIPELINE_SERVICE_CLIENT_ENDPOINT}/api/v1/openmetadata/health`)
and allowing the environment to do the substitution for you. That's the only way we can be sure that the setup is
correct.
#### More validations in the installation
If you have an existing DAG in Airflow, you can further test your setup by running the following:
```bash theme={null}
curl -XPOST http://localhost:8080/api/v1/openmetadata/enable --data-raw '{"dag_id": "example_bash_operator"}' -u "admin:admin" --header 'Content-Type: application/json'
```
Note that in this example we are assuming:
* There is an Airflow instance running at `localhost:8080`,
* There is a user `admin` with password `admin`
* There is a DAG named `example_bash_operator`.
A generic call would look like:
```bash theme={null}
curl -XPOST /api/v1/openmetadata/enable --data-raw '{"dag_id": ""}' -u ":" --header 'Content-Type: application/json'
```
Please update it accordingly.
## Git Sync?
One recurrent question when setting up Airflow is the possibility of using [git-sync](https://airflow.apache.org/docs/helm-chart/1.7.0/manage-dags-files.html#mounting-dags-from-a-private-github-repo-using-git-sync-sidecar)
to manage the ingestion DAGs.
Let's remark the differences between `git-sync` and what we want to achieve by installing our custom API plugins:
1. `git-sync` will use Git as the source of truth for your DAGs. Meaning, any DAG you have on Git will eventually be used and scheduled in Airflow.
2. With the `openmetadata-managed-apis` we are using the OpenMetadata server as the source of truth. We are enabling dynamic DAG
creation from the OpenMetadata into your Airflow instance every time that you create a new Ingestion Workflow.
Then, should you use `git-sync`?
* If you have an existing Airflow instance, and you want to build and maintain your own ingestion DAGs then you can go for it. Check a DAG example [here](/v2.0.x/deployment/ingestion/external/airflow#example).
* If instead, you want to use the full deployment process from OpenMetadata, `git-sync` would not be the right tool, since the DAGs won't be backed up by Git, but rather created from OpenMetadata. Note that if anything
would to happen where you might lose the Airflow volumes, etc. You can just redeploy the DAGs from OpenMetadata.
## SSL
If you want to learn how to set up Airflow using SSL, you can learn more here:
Learn how to configure Airflow with SSL.
## Troubleshooting
## Ingestion Pipeline deployment issues
### Airflow APIs Not Found
Validate the installation, making sure that from the OpenMetadata server you can reach the Airflow host, and the
call to `/health` gives us the proper response:
```bash theme={null}
$ curl -XGET ${PIPELINE_SERVICE_CLIENT_ENDPOINT}/api/v1/openmetadata/health
{"status": "healthy", "version": "x.y.z"}
```
Also, make sure that the version of your OpenMetadata server matches the `openmetadata-ingestion` client version installed in Airflow.
### GetServiceException: Could not get service from type XYZ
In this case, the OpenMetadata client running in the Airflow host had issues getting the service you are trying to
deploy from the API. Note that once pipelines are deployed, the auth happens via the `ingestion-bot`. Here there are
a couple of points to validate:
1. The JWT of the ingestion bot is valid. You can check services such as [https://jwt.io/](https://jwt.io/) to help you
review if the token is expired or if there are any configuration issues.
2. The `ingestion-bot` does not have the proper role. If you go to `/bots/ingestion-bot`, the bot
should present the `Ingestion bot role`. You can validate the role policies as well to make sure they were not
updated and the bot can indeed view and access services from the API.
3. Run an API call for your service to verify the issue. An example trying to get a database service would look like follows:
```
curl -XGET 'http://:8585/api/v1/services/databaseServices/name/' \
-H 'Accept: application/json' -H 'Authorization: Bearer '
```
If, for example, you have an issue with the roles you would be getting a message similar to:
```
{"code":403,"message":"Principal: CatalogPrincipal{name='ingestion-bot'} operations [ViewAll] not allowed"}
```
### AirflowException: Dag 'XYZ' could not be found
If you're seeing a similar error to
```
[...]
task_run
_dag = get_dag(args.subdir, args.dag_id)
File "/home/airflow/.local/lib/python3.9/site-packages/airflow/utils/cli.py", line 235, in get_dag
raise AirflowException(
airflow.exceptions.AirflowException: Dag '...' could not be found; either it does not exist or it failed to parse.
```
This is a common situation where you have not properly enabled the shared volumes between Webserver \<> Scheduler \<> Worker
in your distributed environment.
We have specific instructions on how to set up the shared volumes in Kubernetes depending on your cloud deployment [here](/v2.0.x/deployment/kubernetes).
### ClientInitializationError
The main root cause here is a version mismatch between the server and the client. Make sure that the `openmetadata-ingestion`
python package you installed on the Airflow host has the same version as the OpenMetadata server. For example, to set up
OpenMetadata server 2.0.1 you will need to install `openmetadata-ingestion~=2.0.1.0`. Note that we are validating
the version as in `x.y.z`. Any differences after the PATCH versioning are not taken into account, as they are usually
small bugfixes on existing functionalities.
### 401 Unauthorized
If you get this response during a `Test Connection` or `Deploy`:
```
airflow API returned Unauthorized and response
{ "detail": null, "status": 401, "title": "Unauthorized", "type": "https://airflow.apache.org/docs/apache-airflow/2.3.3/stable-rest-api-ref.html#section/Errors/Unauthenticated" }
```
This is a communication issue between the OpenMetadata Server and the Airflow instance. You are able to reach the
Airflow host, but your provided user and password are not correct. Note the following section of the server configuration:
```yaml theme={null}
pipelineServiceClientConfiguration:
[...]
parameters:
username: ${AIRFLOW_USERNAME:-admin}
password: ${AIRFLOW_PASSWORD:-admin}
```
You should validate if the content of the environment variables `AIRFLOW_USERNAME` and `AIRFLOW_PASSWORD` allow you to
authenticate to the instance.
### CentOS / Debian - The name 'template\_blueprint' is already registered
If you are using a CentOS / Debian system to install the `openmetadata-managed-apis` you might encounter the following issue
when starting Airflow:
```bash theme={null}
airflow standalone
standalone | Starting Airflow Standalone
standalone | Checking database is initialized
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
WARNI [airflow.models.crypto] empty cryptography key - values will not be stored encrypted.
standalone | Database ready
[2023-08-11 05:39:28,851] {manager.py:508} INFO - Created Permission View: can create on DAGs
[2023-08-11 05:39:28,910] {manager.py:508} INFO - Created Permission View: menu access on REST API Plugin
[2023-08-11 05:39:28,916] {manager.py:568} INFO - Added Permission menu access on REST API Plugin to role Admin
Traceback (most recent call last):
File "/home/pmcevoy/airflow233/bin/airflow", line 8, in
sys.exit(main())
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/__main__.py", line 38, in main
args.func(args)
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/cli/cli_parser.py", line 51, in command
return func(*args, **kwargs)
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/cli/commands/standalone_command.py", line 48, in entrypoint
StandaloneCommand().run()
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/cli/commands/standalone_command.py", line 64, in run
self.initialize_database()
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/cli/commands/standalone_command.py", line 180, in initialize_database
appbuilder = cached_app().appbuilder
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/www/app.py", line 158, in cached_app
app = create_app(config=config, testing=testing)
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/www/app.py", line 140, in create_app
init_plugins(flask_app)
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/airflow/www/extensions/init_views.py", line 141, in init_plugins
app.register_blueprint(blue_print["blueprint"])
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/flask/scaffold.py", line 56, in wrapper_func
return f(self, *args, **kwargs)
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/flask/app.py", line 1028, in register_blueprint
blueprint.register(self, options)
File "/home/pmcevoy/airflow233/lib64/python3.9/site-packages/flask/blueprints.py", line 305, in register
raise ValueError(
ValueError: The name 'template_blueprint' is already registered for this blueprint. Use 'name=' to provide a unique name.
```
The issue occurs because a symlink exists inside the `venv`
```bash theme={null}
(airflow233) [pmcevoy@lab1 airflow233]$ ls -la
total 28
drwxr-xr-x 6 pmcevoy pmcevoy 4096 Aug 14 00:34 .
drwx------ 6 pmcevoy pmcevoy 4096 Aug 14 00:32 ..
drwxr-xr-x 3 pmcevoy pmcevoy 4096 Aug 14 00:34 bin
drwxr-xr-x 3 pmcevoy pmcevoy 4096 Aug 14 00:33 include
drwxr-xr-x 3 pmcevoy pmcevoy 4096 Aug 14 00:32 lib
lrwxrwxrwx 1 pmcevoy pmcevoy 3 Aug 14 00:32 lib64 -> lib
-rw-r--r-- 1 pmcevoy pmcevoy 70 Aug 14 00:32 pyvenv.cfg
drwxr-xr-x 3 pmcevoy pmcevoy 4096 Aug 14 00:34 share
```
```bash theme={null}
(airflow233) [pmcevoy@lab1 airflow233]$ grep -r template_blueprint *
lib/python3.9/site-packages/openmetadata_managed_apis/plugin.py:template_blueprint = Blueprint(
lib/python3.9/site-packages/openmetadata_managed_apis/plugin.py: "template_blueprint",
lib/python3.9/site-packages/openmetadata_managed_apis/plugin.py: flask_blueprints = [template_blueprint, api_blueprint]
grep: lib/python3.9/site-packages/openmetadata_managed_apis/__pycache__/plugin.cpython-39.pyc: binary file matches
lib64/python3.9/site-packages/openmetadata_managed_apis/plugin.py:template_blueprint = Blueprint(
lib64/python3.9/site-packages/openmetadata_managed_apis/plugin.py: "template_blueprint",
lib64/python3.9/site-packages/openmetadata_managed_apis/plugin.py: flask_blueprints = [template_blueprint, api_blueprint]
grep: lib64/python3.9/site-packages/openmetadata_managed_apis/__pycache__/plugin.cpython-39.pyc: binary file matches
```
A workaround is to remove the `lib64` symlink: `rm lib64`.
# Kubernetes Deployment | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes
Deploy the OpenMetadata on Kubernetes using Helm, custom values, and supported cloud configurations for scalable containerized environments.
# Kubernetes Deployment
OpenMetadata supports the Installation and Running of Application on kubernetes through Helm Charts.
## Kubernetes Deployment Architecture
Below is the expected Kubernetes Deployment Architecture for OpenMetadata Application in **Production**.
In the above architecture diagram, OpenMetadata Application is deployed using Helm Charts. The various kubernetes manifests that supports the installation. With the above architecture, OpenMetadata Application Connects with external dependencies which is Database, ElasticSearch and Orchestration tools like airflow.
The OpenMetadata Helm Charts Exposes the Application from Kubernetes Service at Port `8585` and `8586`. The Health Checks and Metrics endpoints are available on port `8586`.
Network Policies and Ingresses are optional manifests and disabled by default. These can be installed / enabled using the [Helm Values](/v2.0.x/deployment/kubernetes/on-prem).
## Links
For customizing OpenMetadata Helm Deployments
Run ingestion pipelines as native K8s Jobs (no Airflow required)
Deploy OpenMetadata in AWS Kubernetes
Deploy OpenMetadata in GCP Kubernetes
Deploy OpenMetadata in Azure Kubernetes
Deploy OpenMetadata in On Premises Kubernetes
# AKS Deployment: Prerequisites & Kubernetes Orchestrator
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/aks
Configure OpenMetadata on Azure Kubernetes Service using the recommended Kubernetes-native orchestrator with Helm charts and scalable configuration templates.
# OpenMetadata Deployment on Azure Kubernetes Service Cluster
OpenMetadata can be deployed on Azure Kubernetes Service. This guide covers both the recommended Kubernetes orchestrator (new in 1.12) and the alternative Airflow-based orchestrator.
## Prerequisites
### Azure Services for Database and Search Engine as Elastic Cloud
It is recommended to use [Azure SQL](https://azure.microsoft.com/en-in/products/azure-sql/database) and [Elastic Cloud on Azure](https://www.elastic.co/partners/microsoft-azure) for Production Deployments.
We support:
* Azure SQL (MySQL) engine version 8 or higher
* Azure SQL (PostgreSQL) engine version 15 or higher
* Elastic Cloud (ElasticSearch version 9.x, minimum 9.0.0, recommended 9.3.0)
We recommend:
* Azure SQL to be Multi Zone Available and Production Workload Environment
* Elastic Cloud Environment with multiple zones and minimum 2 nodes
### Step 1 - Create an AKS Cluster
If you are deploying on a new cluster set the `EnableAzureDiskFileCSIDriver=true` to enable container storage interface storage drivers.
```azure-cli theme={null}
az aks create --resource-group MyResourceGroup \
--name MyAKSClusterName \
--nodepool-name agentpool \
--outbound-type loadbalancer \
--location YourPreferredLocation \
--generate-ssh-keys \
--enable-addons monitoring \
EnableAzureDiskFileCSIDriver=true
```
For existing cluster it is important to enable the CSI storage drivers:
```azure-cli theme={null}
az aks update -n MyAKSCluster -g MyResourceGroup --enable-disk-driver --enable-file-driver
```
### Step 2 - Create a Namespace (optional)
```azure-cli theme={null}
kubectl create namespace openmetadata
```
### Step 3 - Add the Helm OpenMetadata Repo
```azure-cli theme={null}
helm repo add open-metadata https://helm.open-metadata.org/
helm repo update
```
***
## Kubernetes Orchestrator Configuration (Recommended)
Starting with OpenMetadata 1.12, we recommend using the **Kubernetes native orchestrator** for running ingestion pipelines. This eliminates the need for Apache Airflow and simplifies your deployment.
The Kubernetes orchestrator runs ingestion pipelines as native K8s Jobs and CronJobs. For full documentation on features, configuration options, and troubleshooting, see the [Kubernetes Orchestrator Guide](/v2.0.x/deployment/ingestion/kubernetes).
The recommended OMJob Operator approach requires installing Custom Resource Definitions (CRDs), which needs elevated cluster permissions. If your cluster policies don't allow CRDs, you can disable the operator by setting `useOMJobOperator: false` and `omjobOperator.enabled: false` in your values file to use native K8s Jobs instead.
### Create Kubernetes Secrets
Create the required secrets for your database and search engine:
```azure-cli theme={null}
# Database secret (for MySQL)
kubectl create secret generic mysql-secrets \
--namespace openmetadata \
--from-literal=openmetadata-mysql-password=
# ElasticSearch secret
kubectl create secret generic elasticsearch-secrets \
--namespace openmetadata \
--from-literal=openmetadata-elasticsearch-password=
```
### OpenMetadata Values Configuration
Create your `openmetadata-values.yaml` with the following configuration:
```yaml theme={null}
# openmetadata-values.yaml
openmetadata:
config:
# Database configuration
elasticsearch:
host:
searchType: elasticsearch
port: 443
scheme: https
connectionTimeoutSecs: 5
socketTimeoutSecs: 60
keepAliveTimeoutSecs: 600
batchSize: 10
auth:
enabled: true
username:
password:
secretRef: elasticsearch-secrets
secretKey: openmetadata-elasticsearch-password
database:
host:
port: 3306
driverClass: com.mysql.cj.jdbc.Driver
dbScheme: mysql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: mysql-secrets
secretKey: openmetadata-mysql-password
# Kubernetes Orchestrator configuration
pipelineServiceClientConfig:
enabled: true
type: "k8s"
metadataApiEndpoint: http://openmetadata.openmetadata.svc.cluster.local:8585/api
k8s:
ingestionImage: "docker.getcollate.io/openmetadata/ingestion-base:2.0.1"
useOMJobOperator: true
# Enable the OMJob Operator (recommended for production)
omjobOperator:
enabled: true
image:
repository: docker.getcollate.io/openmetadata/omjob-operator
tag: "2.0.1"
image:
tag: "2.0.1"
```
For advanced configuration options such as resource limits, job lifecycle settings, failure diagnostics, RBAC, and security contexts, see the [Kubernetes Orchestrator Guide](/v2.0.x/deployment/ingestion/kubernetes).
For Database as PostgreSQL, use the below config for database values:
```yaml theme={null}
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: postgresql-secret
secretKey: postgresql-password
```
### Deploy OpenMetadata
```bash theme={null}
# Install OpenMetadata (no dependencies chart needed with K8s orchestrator)
helm install openmetadata open-metadata/openmetadata \
--namespace openmetadata \
--values openmetadata-values.yaml
```
With the Kubernetes orchestrator, you don't need to deploy the `openmetadata-dependencies` chart that includes Airflow. This significantly simplifies your deployment.
### Verify the Deployment
```bash theme={null}
# Check pods are running
kubectl get pods -n openmetadata
# Check the K8s orchestrator health in OpenMetadata UI
# Navigate to Settings → Preferences → Health
```
### Access OpenMetadata
```azure-cli theme={null}
kubectl port-forward service/openmetadata 8585:8585 -n openmetadata
```
# AKS Deployment: Airflow Orchestrator & Troubleshooting
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/aks-airflow
Configure OpenMetadata on Azure Kubernetes Service using Apache Airflow as the ingestion orchestrator, including persistent volume setup and troubleshooting.
# AKS Deployment: Airflow Orchestrator & Troubleshooting
This page covers the Airflow-based orchestrator setup for AKS. For the simpler recommended Kubernetes-native orchestrator, see [AKS Deployment: Prerequisites & Kubernetes Orchestrator](/v2.0.x/deployment/kubernetes/aks).
## Using Airflow Orchestrator (Alternative)
If you prefer to use Apache Airflow as the orchestrator (e.g., for existing Airflow investments or complex DAG requirements), follow the configuration below.
Using Airflow requires additional infrastructure: persistent volumes with ReadWriteMany access, the openmetadata-dependencies Helm chart, and more complex configuration.
### Create Persistent Volumes
OpenMetadata helm chart depends on Airflow and Airflow expects a persistent disk that support ReadWriteMany (the volume can be mounted as read-write by many nodes). The Azure CSI storage drivers we enabled earlier support the provisioning of the disks in ReadWriteMany mode.
```yaml theme={null}
# logs_dags_pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: openmetadata-dependencies-dags-pvc
namespace: openmetadata
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: azurefile-csi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: openmetadata-dependencies-logs-pvc
namespace: openmetadata
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: azurefile-csi
```
Create the volume claims by applying the manifest:
```azure-cli theme={null}
kubectl apply -f logs_dags_pvc.yaml
```
### Change Owner and Update Permission for Persistent Volumes
Airflow pods run as non-root user and lack write access to our persistent volumes. To fix this we create a job permissions\_pod.yaml that runs a pod that mounts volumes into the persistent volume claim and updates the owner of the mounted folders /airflow-dags and /airflow-logs to user id 50000, which is the default linux user id of Airflow pods.
```yaml theme={null}
# permissions_pod.yaml
apiVersion: batch/v1
kind: Job
metadata:
labels:
run: my-permission-pod
name: my-permission-pod
namespace: openmetadata
spec:
template:
spec:
containers:
- image: busybox
name: my-permission-pod
volumeMounts:
- name: airflow-dags
mountPath: /airflow-dags
- name: airflow-logs
mountPath: /airflow-logs
command: ["/bin/sh", "-c", "chown -R 50000 /airflow-dags /airflow-logs", "chmod -R a+rwx /airflow-dags"]
restartPolicy: Never
volumes:
- name: airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs-pvc
- name: airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags-pvc
```
Start the job by applying the manifest:
```azure-cli theme={null}
kubectl apply -f permissions_pod.yaml
```
### Create Airflow Secrets
```azure-cli theme={null}
kubectl create secret generic airflow-secrets \
--namespace openmetadata \
--from-literal=openmetadata-airflow-password=
```
For production deployments connecting external postgresql database:
```azure-cli theme={null}
kubectl create secret generic postgresql-secret \
--namespace openmetadata \
--from-literal=postgresql-password=
```
### Install OpenMetadata Dependencies
Create `values-dependencies.yaml` to configure Airflow with persistent volumes:
```yaml theme={null}
# values-dependencies.yaml
airflow:
airflow:
extraVolumeMounts:
- mountPath: /airflow-logs
name: aks-airflow-logs
- mountPath: /airflow-dags/dags
name: aks-airflow-dags
extraVolumes:
- name: aks-airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs-pvc
- name: aks-airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags-pvc
config:
AIRFLOW__OPENMETADATA_AIRFLOW_APIS__DAG_GENERATED_CONFIGS: "/airflow-dags/dags"
dags:
path: /airflow-dags/dags
persistence:
enabled: false
logs:
path: /airflow-logs
persistence:
enabled: false
externalDatabase:
type: postgres # default mysql
host: Host_db_address
database: Airflow_metastore_dbname
user: db_userName
port: 5432
dbUseSSL: true
passwordSecret: postgresql-secret
passwordSecretKey: postgresql-password
```
Install the dependencies:
```azure-cli theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies \
--values values-dependencies.yaml \
--namespace openmetadata \
--set mysql.enabled=false
```
It takes a few minutes for all the pods to be correctly set-up and running:
```azure-cli theme={null}
kubectl get pods -n openmetadata
```
### Install OpenMetadata with Airflow
Create `openmetadata-values.yaml` for Airflow-based deployment:
```yaml theme={null}
# openmetadata-values.yaml
global:
pipelineServiceClientConfig:
apiEndpoint: http://openmetadata-dependencies-web.openmetadata.svc.cluster.local:8080
metadataApiEndpoint: http://openmetadata.openmetadata.svc.cluster.local:8585/api
openmetadata:
config:
elasticsearch:
host:
searchType: elasticsearch
port: 443
scheme: https
auth:
enabled: true
username:
password:
secretRef: elasticsearch-secrets
secretKey: openmetadata-elasticsearch-password
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
databaseName: openmetadata_db
auth:
username:
password:
secretRef: postgresql-secret
secretKey: postgresql-password
image:
tag: "2.0.1"
```
```azure-cli theme={null}
helm install openmetadata open-metadata/openmetadata \
--values openmetadata-values.yaml \
--namespace openmetadata
```
## Troubleshooting
### Troubleshooting Airflow
### JSONDecodeError: Unterminated string starting
If you are using Airflow with Azure Blob Storage as `PersistentVolume` as explained in [Storage class using blobfuse](https://learn.microsoft.com/en-us/azure/aks/azure-csi-blob-storage-provision?tabs=mount-nfs%2Csecret),
you may encounter the following error after a few days:
```bash theme={null}
{dagbag.py:346} ERROR - Failed to import: /airflow-dags/dags/...py
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 3552
```
Moreover, the Executor pods would actually be using old files. This behaviour is caused by the recommended config by the
mentioned documentation:
```yaml theme={null}
- -o allow_other
- --file-cache-timeout-in-seconds=120
- --use-attr-cache=true
- --cancel-list-on-mount-seconds=10 # prevent billing charges on mounting
- -o attr_timeout=120
- -o entry_timeout=120
- -o negative_timeout=120
- --log-level=LOG_WARNING # LOG_WARNING, LOG_INFO, LOG_DEBUG
- --cache-size-mb=1000 # Default will be 80% of available memory, eviction will happen beyond that.
```
**Disabling the cache** will help here. In this case it won't have any negative impact, since the `.py` and `.json`
files are small enough and not heavily used.
The same configuration without cache:
```yaml theme={null}
- --o direct_io
- --file-cache-timeout-in-seconds=0
- --use-attr-cache=false
- --cancel-list-on-mount-seconds=10
- --o attr_timeout=0
- --o entry_timeout=0
- --o negative_timeout=0
- --log-level=LOG_WARNING
- --cache-size-mb=0
```
You can find more information about this error [here](https://github.com/open-metadata/OpenMetadata/issues/15321), and similar
discussions [here](https://github.com/Azure/azure-storage-fuse/issues/1171) and [here](https://github.com/Azure/azure-storage-fuse/issues/1139).
## FAQs
## Java Memory Heap Issue
If your openmetadata pods are not in ready state at any point in time and the openmetadata pod logs speaks about the below issue -
```
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "AsyncAppender-Worker-async-file-appender"
Exception in thread "pool-5-thread-1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-file-appender" java.lang.OutOfMemoryError: Java heap space
Exception in thread "dw-46" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-console-appender" java.lang.OutOfMemoryError: Java heap space
```
This is due to the default JVM Heap Space configuration (1 GiB) being not enough for your workloads. In order to resolve this issue, head over to your custom openmetadata helm values and append the below environment variable
```yaml theme={null}
extraEnvs:
- name: OPENMETADATA_HEAP_OPTS
value: "-Xmx2G -Xms2G"
```
The flag `Xmx` specifies the maximum memory allocation pool for a Java virtual machine (JVM), while `Xms` specifies the initial memory allocation pool.
Upgrade the helm charts with the above changes using the following command `helm upgrade --install openmetadata open-metadata/openmetadata --values --namespace `. Update this command your `values.yml` filename and `namespaceName` where you have deployed OpenMetadata in Kubernetes.
## PostgreSQL Issue permission denied to create extension "pgcrypto"
If you are facing the below issue with PostgreSQL as Database Backend for OpenMetadata Application,
```
Message: ERROR: permission denied to create extension "pgcrypto"
Hint: Must be superuser to create this extension.
```
It seems the Database User does not have sufficient privileges. In order to resolve the above issue, grant usage permissions to the PSQL User.
```sql theme={null}
GRANT USAGE ON SCHEMA schema_name TO ;
GRANT CREATE ON EXTENSION pgcrypto TO ;
```
In the above command, replace `` with the sql user used by OpenMetadata Application to connect to PostgreSQL Database.
## How to extend and use custom docker images with OpenMetadata Helm Charts ?
## Extending OpenMetadata Server Docker Image
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/server`
OpenMetadata helm charts uses official published docker images from [DockerHub](https://hub.docker.com/u/openmetadata).
A typical scenario will be to install organization certificates for connecting with inhouse systems.
For Example -
```
FROM docker.open-metadata.org/openmetadata/server:x.y.z
WORKDIR /home/
COPY .
RUN update-ca-certificates
```
where `docker.open-metadata.org/openmetadata/server:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/server:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update your openmetadata helm values yaml
The OpenMetadata Application gets installed as part of `openmetadata` helm chart. In this step, update the custom helm values using YAML file to point the image created in the previous step. For example, create a helm values file named `values.yaml` with the following contents -
```yaml theme={null}
...
image:
repository:
# Overrides the image tag whose default is the chart appVersion.
tag:
...
```
### 3. Install / Upgrade your helm release
Upgrade/Install your openmetadata helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata open-metadata/openmetadata--values values.yaml
```
## Extending OpenMetadata Ingestion Docker Image
One possible use case for a custom ingestion image is a custom connector. Build and test the package with the same `openmetadata-ingestion` version as your deployment. After your code is ready, follow these steps:
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/ingestion`:
For example -
```
FROM docker.open-metadata.org/openmetadata/ingestion:x.y.z
USER airflow
# Let's use the home directory of airflow user
WORKDIR /home/airflow
# Install our custom connector
COPY
COPY setup.py .
RUN pip install --no-deps .
```
where `docker.open-metadata.org/openmetadata/ingestion:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/ingestion:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update the airflow in openmetadata dependencies values YAML
The ingestion containers (which is the one shipping Airflow) gets installed in the `openmetadata-dependencies` helm chart. In this step, we use
our own custom values YAML file to point to the image we just created on the previous step. You can create a file named `values.deps.yaml` with the
following contents:
```yaml theme={null}
airflow:
airflow:
image:
repository: # by default, openmetadata/ingestion
tag: # by default, the version you are deploying, e.g., 1.1.0
pullPolicy: "IfNotPresent"
```
### 3. Install / Upgrade helm release
Upgrade/Install your openmetadata-dependencies helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values.deps.yaml
```
## How to disable MySQL and ElasticSearch from OpenMetadata Dependencies Helm Charts ?
If you are using MySQL and ElasticSearch externally, you would want to disable the local installation of mysql and elasticsearch while installing OpenMetadata Dependencies Helm Chart. You can disable the MySQL and ElasticSearch Helm Dependencies by setting `enabled: false` value for each dependency. Below is the command to set helm values from Helm CLI -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --set mysql.enabled=false --set elasticsearch.enabled=false
```
Alternatively, you can create a custom YAML file named `values.deps.yaml` to disable installation of MySQL and Elasticsearch .
```yaml theme={null}
mysql:
enabled: false
...
elasticsearch:
enabled: false
...
...
```
## How to configure external database like PostgreSQL with OpenMetadata Helm Charts ?
OpenMetadata Supports PostgreSQL as one of the Database Dependencies. OpenMetadata Helm Charts by default does not include PostgreSQL as Database Dependencies. In order to configure Helm Charts with External Database like PostgreSQL, follow the below guide to make the helm values change and upgrade / install OpenMetadata helm charts with the same.
## Upgrade Airflow Helm Dependencies Helm Charts to connect to External Database like PostgreSQL
We ship [airflow-helm](https://github.com/airflow-helm/charts/tree/main/charts/airflow) as one of OpenMetadata Dependencies with default values to connect to MySQL Database as part of `externalDatabase` configurations.
You can find more information on setting the `externalDatabase` as part of helm values [here](https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md).
With OpenMetadata Dependencies Helm Charts, your helm values would look something like below -
```yaml theme={null}
...
airflow:
externalDatabase:
type: postgresql
host:
port: 5432
database:
user:
passwordSecret: airflow-postgresql-secrets
passwordSecretKey: airflow-postgresql-password
...
```
For the above code, it is assumed you are creating a kubernetes secret for storing Airflow Database login Credentials. A sample command to create the secret will be `kubectl create secret generic airflow-postgresql-secrets --from-literal=airflow-postgresql-password=`.
## Upgrade OpenMetadata Helm Charts to connect to External Database like PostgreSQL
Update the `openmetadata.config.database.*` helm values for OpenMetadata Application to connect to External Database like PostgreSQL.
With OpenMetadata Helm Charts, your helm values would look something like below -
```yaml theme={null}
openmetadata:
config:
...
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: openmetadata-postgresql-secrets
secretKey: openmetadata-postgresql-password
```
For the above code, it is assumed you are creating a kubernetes secret for storing OpenMetadata Database login Credentials. A sample command to create the secret will be `kubectl create secret generic openmetadata-postgresql-secrets --from-literal=openmetadata-postgresql-password=`.
Once you make the above changes to your helm values, run the below command to install/upgrade helm charts -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values <> --namespace
helm upgrade --install openmetadata open-metadata/openmetadata --values <> --namespace
```
## How to customize OpenMetadata Dependencies Helm Chart with custom helm values
Our OpenMetadata Dependencies Helm Charts are internally depends on three sub-charts -
* [Bitnami MySQL](https://artifacthub.io/packages/helm/bitnami/mysql/9.7.2) (helm chart version 9.7.2)
* [OpenSearch](https://artifacthub.io/packages/helm/opensearch-project-helm-charts/opensearch/2.12.2) (helm chart version 2.12.2)
* [Airflow](https://artifacthub.io/packages/helm/airflow-helm/airflow/8.8.0) (helm chart version 8.8.0)
If you are looking to customize the deployments of any of the above dependencies, please refer to the above links for customizations of helm values for further references.
By default, OpenMetadata Dependencies helm chart provides initial generic customization of these helm values in order to get you started quickly. You can refer to the openmetadata-dependencies helm charts default values [here](https://github.com/open-metadata/openmetadata-helm-charts/blob/main/charts/deps/values.yaml).
# AWS EKS Deployment | OpenMetadata Kubernetes Guide
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/eks
Deploy the OpenMetadata on Amazon EKS for cloud-native scalability with secure identity integration and managed infrastructure support.
# EKS on Amazon Web Services Deployment
OpenMetadata supports the Installation and Running of Application on Elastic Kubernetes Services (EKS) through Helm Charts.
However, there are some additional configurations which needs to be done as prerequisites for the same.
All the code snippets in this section assume the `default` namespace for kubernetes.
This guide presumes you have AWS EKS Cluster already available.
## Prerequisites
### AWS Services for Database as RDS and Search Engine as ElasticSearch
It is recommended to use [Amazon RDS](https://docs.aws.amazon.com/rds/index.html) and [Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/?id=docs_gateway) for Production Deployments.
We support
* Amazon RDS (MySQL) engine version 8 or higher
* Amazon RDS (PostgreSQL) engine version 15 or higher
* Amazon OpenSearch engine version 3.x (minimum 3.0.0, recommended 3.3.0)
When using AWS Services the SearchType Configuration for elastic search should be `opensearch`, for both cases ElasticSearch and OpenSearch, as you can see in the ElasticSearch configuration example below.
We recommend
* Amazon RDS to be in Multiple Availability Zones.
* Amazon OpenSearch (or ElasticSearch) Service with Multiple Availability Zones with minimum 2 Nodes.
Make sure to increase `sort_buffer_size` (for MySQL) or `work_mem` (for PostgreSQL) to the recommended value of **20MB** or more using the [database parameter group setting](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html). This is especially important when running migrations to prevent **Out of Sort Memory Error**. You can revert the setting once the migrations are complete.
## Kubernetes Orchestrator Configuration (Recommended)
Starting with OpenMetadata 1.12, we recommend using the **Kubernetes native orchestrator** for running ingestion pipelines. This eliminates the need for Apache Airflow and simplifies your deployment.
The Kubernetes orchestrator runs ingestion pipelines as native K8s Jobs and CronJobs. For full documentation on features, configuration options, and troubleshooting, see the [Kubernetes Orchestrator Guide](/v2.0.x/deployment/ingestion/kubernetes).
The recommended OMJob Operator approach requires installing Custom Resource Definitions (CRDs), which needs elevated cluster permissions. If your cluster policies don't allow CRDs, you can disable the operator by setting `useOMJobOperator: false` and `omjobOperator.enabled: false` in your values file to use native K8s Jobs instead.
### OpenMetadata Values Configuration
Create your `openmetadata-values.yaml` with the following configuration:
```yaml theme={null}
# openmetadata-values.yaml
openmetadata:
config:
# Database configuration
elasticsearch:
host:
searchType: opensearch
port: 443
scheme: https
connectionTimeoutSecs: 5
socketTimeoutSecs: 60
keepAliveTimeoutSecs: 600
batchSize: 10
auth:
enabled: true
username:
password:
secretRef: elasticsearch-secrets
secretKey: openmetadata-elasticsearch-password
database:
host:
port: 3306
driverClass: com.mysql.cj.jdbc.Driver
dbScheme: mysql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: mysql-secrets
secretKey: openmetadata-mysql-password
# Kubernetes Orchestrator configuration
pipelineServiceClientConfig:
enabled: true
type: "k8s"
metadataApiEndpoint: http://openmetadata:8585/api
k8s:
ingestionImage: "docker.getcollate.io/openmetadata/ingestion-base:2.0.1"
useOMJobOperator: true
# Enable the OMJob Operator (recommended for production)
omjobOperator:
enabled: true
image:
repository: docker.getcollate.io/openmetadata/omjob-operator
tag: "2.0.1"
```
For advanced configuration options such as resource limits, job lifecycle settings, failure diagnostics, RBAC, and security contexts, see the [Kubernetes Orchestrator Guide](/v2.0.x/deployment/ingestion/kubernetes).
### Create Kubernetes Secrets
Create the required secrets for RDS and OpenSearch:
```bash theme={null}
# Database secret
kubectl create secret generic mysql-secrets \
--from-literal=openmetadata-mysql-password=
# OpenSearch secret
kubectl create secret generic elasticsearch-secrets \
--from-literal=openmetadata-elasticsearch-password=
```
### Deploy OpenMetadata
```bash theme={null}
# Add the OpenMetadata Helm repository
helm repo add open-metadata https://helm.open-metadata.org/
helm repo update
# Install OpenMetadata (no dependencies chart needed with K8s orchestrator)
helm install openmetadata open-metadata/openmetadata \
--values openmetadata-values.yaml
```
With the Kubernetes orchestrator, you don't need to deploy the `openmetadata-dependencies` chart that includes Airflow. This significantly simplifies your deployment.
### Verify the Deployment
```bash theme={null}
# Check pods are running
kubectl get pods
# Check the K8s orchestrator health in OpenMetadata UI
# Navigate to Settings → Preferences → Health
```
***
If you prefer to use Apache Airflow as the orchestrator for your EKS deployment, see the [Airflow on EKS](/v2.0.x/deployment/kubernetes/eks/airflow) guide.
# Airflow Orchestrator on EKS | OpenMetadata Kubernetes Guide
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/eks/airflow
Configure Apache Airflow as the orchestrator for OpenMetadata on Amazon EKS with EFS persistent storage.
# Airflow Orchestrator on EKS
If you prefer to use Apache Airflow as the orchestrator (e.g., for existing Airflow investments or complex DAG requirements), follow the configuration below.
Using Airflow requires additional infrastructure: persistent volumes with ReadWriteMany access, the openmetadata-dependencies Helm chart, and more complex configuration.
### Create Elastic File System in AWS
You can follow official AWS Guides [here](https://docs.aws.amazon.com/efs/latest/ug/gs-step-two-create-efs-resources.html) to provision EFS File System in the same VPC which is associated with your EKS Cluster.
### Persistent Volumes with ReadWriteMany Access Modes
OpenMetadata helm chart depends on Airflow and Airflow expects a persistent disk that support ReadWriteMany (the volume can be mounted as read-write by many nodes).
In AWS, this is achieved by Elastic File System (EFS) service. AWS Elastic Block Store (EBS) does not provide ReadWriteMany Volume access mode as EBS will only be attached to one Kubernetes Node at any given point of time.
In order to provision persistent volumes from AWS EFS, you will need to setup and install [aws-efs-csi-driver](https://docs.aws.amazon.com/eks/latest/userguide/efs-csi.html). Note that this is required for Airflow as One OpenMetadata Dependencies.
Also, [aws-ebs-csi-driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) might be required for Persistent Volumes that are to be used for MySQL and ElasticSearch as OpenMetadata Dependencies.
The below guide provides Persistent Volumes provisioning as static volumes (meaning you will be responsible to create, maintain and destroy Persistent Volumes).
### Provision EFS backed PVs, PVCs for Airflow DAGs and Airflow Logs
Please note that we are using one AWS Elastic File System (EFS) service with subdirectories as `airflow-dags` and `airflow-logs` with the reference in this documentation. Also, it is presumed that `airflow-dags` and `airflow-logs` directories are already available on that file system.
In order to create directories inside the AWS Elastic File System (EFS) you would need to follow these [steps](https://docs.aws.amazon.com/efs/latest/ug/accessing-fs-nfs-permissions-per-user-subdirs.html).
#### Code Samples for PV and PVC for Airflow DAGs
```yaml theme={null}
# dags_pv_pvc.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: openmetadata-dependencies-dags-pv
labels:
app: airflow-dags
spec:
capacity:
storage: 10Gi
storageClassName: ""
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
csi:
driver: efs.csi.aws.com
volumeHandle: :/airflow-dags # Replace with EFS File System Id
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: airflow-dags
name: openmetadata-dependencies-dags-pvc
namespace: default
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 10Gi
```
Create Persistent Volumes and Persistent Volume claims with the below command.
```commandline theme={null}
kubectl create -f dags_pv_pvc.yml
```
#### Code Samples for PV and PVC for Airflow Logs
```yaml theme={null}
# logs_pv_pvc.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: openmetadata-dependencies-logs-pv
labels:
app: airflow-logs
spec:
capacity:
storage: 5Gi
storageClassName: ""
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
csi:
driver: efs.csi.aws.com
volumeHandle: :/airflow-logs # Replace with EFS File System Id
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: openmetadata-dependencies-logs-pvc
namespace: default
labels:
app: airflow-dags
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 5Gi
```
Create Persistent Volumes and Persistent Volume claims with the below command.
```commandline theme={null}
kubectl create -f logs_pv_pvc.yml
```
### Change owner and permission manually on disks
Since airflow pods run as non root users, they would not have write access on the nfs server volumes. In order to fix the permission here, spin up a pod with persistent volumes attached and run it once.
You can find more reference on AWS EFS permissions in docs [here](https://docs.aws.amazon.com/efs/latest/ug/using-fs.html).
```yaml theme={null}
# permissions_pod.yml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-permission-pod
name: my-permission-pod
spec:
containers:
- image: nginx
name: my-permission-pod
volumeMounts:
- name: airflow-dags
mountPath: /airflow-dags
- name: airflow-logs
mountPath: /airflow-logs
command:
- "chown -R 50000 /airflow-dags /airflow-logs"
# if needed
- "chmod -R a+rwx /airflow-dags"
volumes:
- name: airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs-pvc
- name: airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags-pvc
dnsPolicy: ClusterFirst
restartPolicy: Always
```
Airflow runs the pods with linux user name as airflow and linux user id as 50000.
Run the below command to create the pod and fix the permissions
```commandline theme={null}
kubectl create -f permissions_pod.yml
```
### Create OpenMetadata dependencies Values
Override openmetadata dependencies airflow helm values to bind the efs persistent volumes for DAGs and logs.
```yaml theme={null}
# values-dependencies.yml
airflow:
airflow:
extraVolumeMounts:
- mountPath: /airflow-logs
name: efs-airflow-logs
- mountPath: /airflow-dags/dags
name: efs-airflow-dags
extraVolumes:
- name: efs-airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs-pvc
- name: efs-airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags-pvc
config:
AIRFLOW__OPENMETADATA_AIRFLOW_APIS__DAG_GENERATED_CONFIGS: "/airflow-dags/dags"
dags:
path: /airflow-dags/dags
persistence:
enabled: false
logs:
path: /airflow-logs
persistence:
enabled: false
```
For more information on airflow helm chart values, please refer to [airflow-helm](https://artifacthub.io/packages/helm/airflow-helm/airflow/8.5.3).
When deploying openmetadata dependencies helm chart, use the below command -
```commandline theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values-dependencies.yaml
```
The above command uses configurations defined [here](https://raw.githubusercontent.com/open-metadata/openmetadata-helm-charts/main/charts/deps/values.yaml).
You can modify any configuration and deploy by passing your own `values.yaml`
```commandline theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values
```
### Deploy OpenMetadata with Airflow Configuration
Create your OpenMetadata values file with Airflow configuration:
```yaml theme={null}
# openmetadata-values-airflow.yaml
openmetadata:
config:
elasticsearch:
host:
searchType: opensearch
port: 443
scheme: https
connectionTimeoutSecs: 5
socketTimeoutSecs: 60
keepAliveTimeoutSecs: 600
batchSize: 10
auth:
enabled: true
username:
password:
secretRef: elasticsearch-secrets
secretKey: openmetadata-elasticsearch-password
database:
host:
port: 3306
driverClass: com.mysql.cj.jdbc.Driver
dbScheme: mysql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: mysql-secrets
secretKey: openmetadata-mysql-password
# Airflow configuration
pipelineServiceClientConfig:
enabled: true
type: "airflow"
metadataApiEndpoint: http://openmetadata:8585/api
airflow:
apiEndpoint: http://openmetadata-dependencies-web:8080
auth:
username: admin
password:
secretRef: airflow-secrets
secretKey: openmetadata-airflow-password
```
Once the openmetadata dependencies helm chart deployed, you can then run the below command to install the openmetadata helm chart -
```commandline theme={null}
helm install openmetadata open-metadata/openmetadata --values openmetadata-values-airflow.yaml
```
Create RDS and OpenSearch credentials as Kubernetes Secrets, as described [here](/v2.0.x/quick-start/local-kubernetes-deployment#2-create-kubernetes-secrets-required-for-helm-charts).
Also, disable MySQL and Elasticsearch from OpenMetadata Dependencies Helm Charts as described in the [FAQ](#how-to-disable-mysql-and-elasticsearch-from-openmetadata-dependencies-helm-charts-).
## FAQs
## Getting an error when install OpenMetadata Dependencies Helm Charts on EKS with EFS
If you are facing the below issue -
```
MountVolume.SetUp failed for volume "openmetadata-dependencies-dags-pv" : rpc error: code = Internal desc = Could not mount "fs-012345abcdef:/airflow-dags" at "/var/lib/kubelet/pods/xyzabc-123-0062-44c3-b0e9-fa193c19f41c/volumes/kubernetes.io~csi/openmetadata-dependencies-dags-pv/mount": mount failed: exit status 1 Mounting command: mount Mounting arguments: -t efs -o tls fs-012345abcdef:/airflow-dags /var/lib/kubelet/pods/xyzabc-123-0062-44c3-b0e9-fa193c19f41c/volumes/kubernetes.io~csi/openmetadata-dependencies-dags-pv/mount Output: Failed to locate an available port in the range [20049, 20449], try specifying a different port range in /etc/amazon/efs/efs-utils.conf
```
This error is typically related to EKS Cluster not able to reach to EFS File systems. You can check the security groups associated between the connectivity EFS and EKS. [Here is an article](https://github.com/kubernetes-sigs/aws-efs-csi-driver/blob/master/docs/efs-create-filesystem.md) which further describes the steps required to create Security Group Rules for EKS to use EFS over `port 2049`.
It can also happen if the mount targets are already available for EKS Nodes but the Nodes do not pick that up. In such cases, you can do an [AWS AutoScaling Group instance refresh](https://docs.aws.amazon.com/autoscaling/ec2/userguide/start-instance-refresh.html) in order for EKS nodes to get the available mount targets.
## Java Memory Heap Issue
If your openmetadata pods are not in ready state at any point in time and the openmetadata pod logs speaks about the below issue -
```
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "AsyncAppender-Worker-async-file-appender"
Exception in thread "pool-5-thread-1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-file-appender" java.lang.OutOfMemoryError: Java heap space
Exception in thread "dw-46" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-console-appender" java.lang.OutOfMemoryError: Java heap space
```
This is due to the default JVM Heap Space configuration (1 GiB) being not enough for your workloads. In order to resolve this issue, head over to your custom openmetadata helm values and append the below environment variable
```yaml theme={null}
extraEnvs:
- name: OPENMETADATA_HEAP_OPTS
value: "-Xmx2G -Xms2G"
```
The flag `Xmx` specifies the maximum memory allocation pool for a Java virtual machine (JVM), while `Xms` specifies the initial memory allocation pool.
Upgrade the helm charts with the above changes using the following command `helm upgrade --install openmetadata open-metadata/openmetadata --values --namespace `. Update this command your `values.yml` filename and `namespaceName` where you have deployed OpenMetadata in Kubernetes.
## PostgreSQL Issue permission denied to create extension "pgcrypto"
If you are facing the below issue with PostgreSQL as Database Backend for OpenMetadata Application,
```
Message: ERROR: permission denied to create extension "pgcrypto"
Hint: Must be superuser to create this extension.
```
It seems the Database User does not have sufficient privileges. In order to resolve the above issue, grant usage permissions to the PSQL User.
```sql theme={null}
GRANT USAGE ON SCHEMA schema_name TO ;
GRANT CREATE ON EXTENSION pgcrypto TO ;
```
In the above command, replace `` with the sql user used by OpenMetadata Application to connect to PostgreSQL Database.
## How to extend and use custom docker images with OpenMetadata Helm Charts ?
## Extending OpenMetadata Server Docker Image
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/server`
OpenMetadata helm charts uses official published docker images from [DockerHub](https://hub.docker.com/u/openmetadata).
A typical scenario will be to install organization certificates for connecting with inhouse systems.
For Example -
```
FROM docker.open-metadata.org/openmetadata/server:x.y.z
WORKDIR /home/
COPY .
RUN update-ca-certificates
```
where `docker.open-metadata.org/openmetadata/server:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/server:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update your openmetadata helm values yaml
The OpenMetadata Application gets installed as part of `openmetadata` helm chart. In this step, update the custom helm values using YAML file to point the image created in the previous step. For example, create a helm values file named `values.yaml` with the following contents -
```yaml theme={null}
...
image:
repository:
# Overrides the image tag whose default is the chart appVersion.
tag:
...
```
### 3. Install / Upgrade your helm release
Upgrade/Install your openmetadata helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata open-metadata/openmetadata--values values.yaml
```
## Extending OpenMetadata Ingestion Docker Image
One possible use case for a custom ingestion image is a custom connector. Build and test the package with the same `openmetadata-ingestion` version as your deployment. After your code is ready, follow these steps:
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/ingestion`:
For example -
```
FROM docker.open-metadata.org/openmetadata/ingestion:x.y.z
USER airflow
# Let's use the home directory of airflow user
WORKDIR /home/airflow
# Install our custom connector
COPY
COPY setup.py .
RUN pip install --no-deps .
```
where `docker.open-metadata.org/openmetadata/ingestion:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/ingestion:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update the airflow in openmetadata dependencies values YAML
The ingestion containers (which is the one shipping Airflow) gets installed in the `openmetadata-dependencies` helm chart. In this step, we use
our own custom values YAML file to point to the image we just created on the previous step. You can create a file named `values.deps.yaml` with the
following contents:
```yaml theme={null}
airflow:
airflow:
image:
repository: # by default, openmetadata/ingestion
tag: # by default, the version you are deploying, e.g., 1.1.0
pullPolicy: "IfNotPresent"
```
### 3. Install / Upgrade helm release
Upgrade/Install your openmetadata-dependencies helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values.deps.yaml
```
## How to disable MySQL and ElasticSearch from OpenMetadata Dependencies Helm Charts ?
If you are using MySQL and ElasticSearch externally, you would want to disable the local installation of mysql and elasticsearch while installing OpenMetadata Dependencies Helm Chart. You can disable the MySQL and ElasticSearch Helm Dependencies by setting `enabled: false` value for each dependency. Below is the command to set helm values from Helm CLI -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --set mysql.enabled=false --set elasticsearch.enabled=false
```
Alternatively, you can create a custom YAML file named `values.deps.yaml` to disable installation of MySQL and Elasticsearch .
```yaml theme={null}
mysql:
enabled: false
...
elasticsearch:
enabled: false
...
...
```
## How to configure external database like PostgreSQL with OpenMetadata Helm Charts ?
OpenMetadata Supports PostgreSQL as one of the Database Dependencies. OpenMetadata Helm Charts by default does not include PostgreSQL as Database Dependencies. In order to configure Helm Charts with External Database like PostgreSQL, follow the below guide to make the helm values change and upgrade / install OpenMetadata helm charts with the same.
## Upgrade Airflow Helm Dependencies Helm Charts to connect to External Database like PostgreSQL
We ship [airflow-helm](https://github.com/airflow-helm/charts/tree/main/charts/airflow) as one of OpenMetadata Dependencies with default values to connect to MySQL Database as part of `externalDatabase` configurations.
You can find more information on setting the `externalDatabase` as part of helm values [here](https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md).
With OpenMetadata Dependencies Helm Charts, your helm values would look something like below -
```yaml theme={null}
...
airflow:
externalDatabase:
type: postgresql
host:
port: 5432
database:
user:
passwordSecret: airflow-postgresql-secrets
passwordSecretKey: airflow-postgresql-password
...
```
For the above code, it is assumed you are creating a kubernetes secret for storing Airflow Database login Credentials. A sample command to create the secret will be `kubectl create secret generic airflow-postgresql-secrets --from-literal=airflow-postgresql-password=`.
## Upgrade OpenMetadata Helm Charts to connect to External Database like PostgreSQL
Update the `openmetadata.config.database.*` helm values for OpenMetadata Application to connect to External Database like PostgreSQL.
With OpenMetadata Helm Charts, your helm values would look something like below -
```yaml theme={null}
openmetadata:
config:
...
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: openmetadata-postgresql-secrets
secretKey: openmetadata-postgresql-password
```
For the above code, it is assumed you are creating a kubernetes secret for storing OpenMetadata Database login Credentials. A sample command to create the secret will be `kubectl create secret generic openmetadata-postgresql-secrets --from-literal=openmetadata-postgresql-password=`.
Once you make the above changes to your helm values, run the below command to install/upgrade helm charts -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values <> --namespace
helm upgrade --install openmetadata open-metadata/openmetadata --values <> --namespace
```
## How to customize OpenMetadata Dependencies Helm Chart with custom helm values
Our OpenMetadata Dependencies Helm Charts are internally depends on three sub-charts -
* [Bitnami MySQL](https://artifacthub.io/packages/helm/bitnami/mysql/9.7.2) (helm chart version 9.7.2)
* [OpenSearch](https://artifacthub.io/packages/helm/opensearch-project-helm-charts/opensearch/2.12.2) (helm chart version 2.12.2)
* [Airflow](https://artifacthub.io/packages/helm/airflow-helm/airflow/8.8.0) (helm chart version 8.8.0)
If you are looking to customize the deployments of any of the above dependencies, please refer to the above links for customizations of helm values for further references.
By default, OpenMetadata Dependencies helm chart provides initial generic customization of these helm values in order to get you started quickly. You can refer to the openmetadata-dependencies helm charts default values [here](https://github.com/open-metadata/openmetadata-helm-charts/blob/main/charts/deps/values.yaml).
# Terraform AWS Deployment | OpenMetadata
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/eks/terraform
Deploy OpenMetadata on Amazon EKS using the official Terraform module with support for RDS, OpenSearch Service, and multiple provisioner modes.
# Deploy OpenMetadata on AWS with Terraform
The [OpenMetadata Terraform module for AWS](https://registry.terraform.io/modules/open-metadata/openmetadata/aws) deploys OpenMetadata and all its dependencies on an existing EKS cluster. Each component (database and search engine) can be independently configured using one of three provisioners: deploy it inside the cluster via Helm, provision a managed AWS service, or connect to an existing resource you already operate.
## Prerequisites
Before using this module, ensure you have:
* **Terraform** `~> 1.0`
* **An existing EKS cluster** with `kubectl` configured to access it
* **Helm and Kubernetes Terraform providers** configured to point to your cluster
* **AWS provider** `~> 6.0` with permissions to create the resources required by your chosen provisioners (see [IAM permissions](#iam-permissions) below)
The module manages OpenMetadata and its dependencies only (it does not create the EKS cluster, VPC, or node groups). See the [complete example](https://github.com/open-metadata/terraform-aws-openmetadata/tree/main/examples/complete) for a reference that provisions the full AWS infrastructure from scratch.
### IAM Permissions
The following permissions are required depending on which provisioners you use:
| Provisioner | Required AWS permissions |
| :------------------- | :-------------------------------------------------------------- |
| `db = "aws"` | RDS: create/manage DB instances, subnet groups, security groups |
| `opensearch = "aws"` | OpenSearch Service: create/manage domains, security groups |
| `kms_key_id` | KMS: use the specified key for encryption |
### Provider Configuration
Your Terraform configuration must include the AWS, Kubernetes, and Helm providers:
```hcl theme={null}
provider "aws" {
region = "us-east-1"
}
provider "kubernetes" {
host = aws_eks_cluster.this.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.this.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.this.token
}
provider "helm" {
kubernetes {
host = aws_eks_cluster.this.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.this.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.this.token
}
}
```
***
## Choosing a Provisioner
Each component supports a different set of provisioners. Mix and match to fit your infrastructure:
| Component | `helm` | `aws` | `existing` |
| :------------------------ | :----: | :---: | :--------: |
| **OpenMetadata** | ✅ | N/A | N/A |
| **OpenMetadata database** | ✅ | ✅ | ✅ |
| **OpenSearch** | ✅ | ✅ | ✅ |
| Provisioner | When to use |
| :---------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `helm` | Development, testing, or when you want everything self-contained inside the cluster. |
| `aws` | Production. Creates a managed AWS resource (RDS or OpenSearch Service) with high availability, automated backups, and encryption. |
| `existing` | You already have a database or search engine running. The module connects OpenMetadata to it without creating anything new. |
***
## Quick Start - Helm
The simplest deployment. All components run inside your cluster via Helm. Suitable for development and evaluation:
```hcl theme={null}
module "omd" {
source = "open-metadata/openmetadata/aws"
version = "1.13"
app_namespace = "openmetadata"
eks_nodes_sg_ids = ["sg-1234abcd5678efgh"]
subnet_ids = ["subnet-1a2b3c4d", "subnet-5e6f7g8h", "subnet-9i0j1k2l"]
vpc_id = "vpc-1a2b3c4d"
}
```
```bash theme={null}
terraform init
```
```bash theme={null}
terraform plan
```
```bash theme={null}
terraform apply
```
***
## Production Deployment - AWS Managed Services
Use the `aws` provisioner for the database and OpenSearch to get production-grade infrastructure. This creates:
* **RDS PostgreSQL** instance (Multi-AZ, `db.t4g.medium`) for OpenMetadata
* **OpenSearch Service** domain (2 nodes, `t3.small.search`) for search
* **Security groups** allowing traffic from your EKS nodes to each resource
* **Kubernetes secrets** with auto-generated credentials in your application namespace
The `aws` provisioner creates billable AWS resources. Run `terraform destroy` when you no longer need them.
```hcl theme={null}
module "omd" {
source = "open-metadata/openmetadata/aws"
version = "1.13"
app_namespace = "openmetadata"
eks_nodes_sg_ids = ["sg-1234abcd5678efgh"]
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
subnet_ids = ["subnet-1a2b3c4d", "subnet-5e6f7g8h", "subnet-9i0j1k2l"]
vpc_id = "vpc-1a2b3c4d"
db = {
provisioner = "aws"
}
opensearch = {
provisioner = "aws"
}
}
```
Credentials for RDS and OpenSearch are generated automatically and stored as Kubernetes secrets in your application namespace. You do not need to manage passwords manually.
### Customizing AWS Resources
Override the defaults for any AWS-managed resource using the `aws` sub-object:
```hcl theme={null}
db = {
provisioner = "aws"
aws = {
instance_class = "db.t4g.large"
multi_az = true
backup_retention_period = 14
deletion_protection = true
skip_final_snapshot = false
}
}
opensearch = {
provisioner = "aws"
aws = {
instance_type = "m6g.large.search"
instance_count = 3
availability_zone_count = 3
engine_version = "OpenSearch_3.3"
}
}
```
***
## Bring Your Own Infrastructure - Existing
Connect OpenMetadata to a database and search engine you already operate. No new AWS resources are created:
```hcl theme={null}
module "omd" {
source = "open-metadata/openmetadata/aws"
version = "1.13"
app_namespace = "openmetadata"
db = {
provisioner = "existing"
host = "omd-db.postgres.example"
port = 5432
db_name = "openmetadata_db"
engine = {
name = "postgres"
}
credentials = {
username = "dbadmin"
password = {
secret_ref = "db-secrets"
secret_key = "password"
}
}
}
opensearch = {
provisioner = "existing"
host = "opensearch.example"
port = "443"
scheme = "https"
}
}
```
The `secret_ref` and `secret_key` values reference a Kubernetes secret that must already exist in your application namespace before `terraform apply`.
***
## Kubernetes Orchestrator (No Airflow)
This is the default mode. The module deploys OpenMetadata without Airflow and configures it to run ingestion pipelines as native Kubernetes Jobs via the OMJob operator. No extra configuration is needed:
```hcl theme={null}
module "omd" {
source = "open-metadata/openmetadata/aws"
version = "1.13"
app_namespace = "openmetadata"
eks_nodes_sg_ids = ["sg-1234abcd5678efgh"]
subnet_ids = ["subnet-1a2b3c4d", "subnet-5e6f7g8h", "subnet-9i0j1k2l"]
vpc_id = "vpc-1a2b3c4d"
}
```
No Airflow deployment, Airflow database, or EFS volumes are created. OpenMetadata is configured automatically to use the OMJob operator:
* `pipelineServiceClientConfig.type` is set to `k8s`
* `pipelineServiceClientConfig.k8s.useOMJobOperator` is set to `true`
* `omjobOperator.enabled` is set to `true`
The OMJob operator installs Custom Resource Definitions (CRDs) on your cluster, which requires elevated permissions during the first `terraform apply`.
***
## Advanced Configuration
### Extra Environment Variables
Inject arbitrary environment variables into the OpenMetadata pod:
```hcl theme={null}
extra_envs = {
"ELASTICSEARCH_BATCH_SIZE" = "250"
"PIPELINE_SERVICE_IP_INFO_ENABLED" = "false"
}
```
Or load them from an existing Kubernetes secret:
```hcl theme={null}
env_from = ["my-app-secrets", "another-secret"]
```
Both can be used together. `env_from` secrets are mounted before `extra_envs`, so individual values in `extra_envs` can override keys from a secret.
### Overriding Helm Values
Pass arbitrary values to any Helm chart using the `*_helm_values` variables. These are merged on top of the values generated by the module, so they can override defaults or configure options not exposed as Terraform variables:
| Variable | Helm chart |
| :------------------------- | :--------------------------------- |
| `openmetadata_helm_values` | OpenMetadata |
| `opensearch_helm_values` | OpenSearch (inside the deps chart) |
```hcl theme={null}
openmetadata_helm_values = {
"replicaCount" = "2"
}
```
***
## Accessing Your Deployment
```bash theme={null}
kubectl port-forward service/openmetadata 8585:8585 -n
```
Open `http://localhost:8585` in your browser.
Keep the terminal session with `kubectl port-forward` open while accessing OpenMetadata. If port 8585 is already in use on your machine, change the local port number (the first number in `local:remote`, e.g. `9585:8585`).
***
## Complete AWS Example
The [complete example](https://github.com/open-metadata/terraform-aws-openmetadata/tree/main/examples/complete) provisions a full AWS environment from scratch, including:
* VPC with public/private subnets, Internet Gateway, and NAT Gateway
* EKS cluster with EBS and EFS CSI driver addons
* KMS key for encrypting all resources
* RDS instance for OpenMetadata (Multi-AZ, deletion protection enabled)
* OpenSearch domain with a security group allowing inbound traffic from EKS nodes
* Kubernetes namespace, storage classes, and secrets
It is a good reference for production deployments and for understanding how to wire together the AWS, Kubernetes, and Helm providers.
***
## Next Steps
Run ingestion pipelines as native Kubernetes Jobs
Manual Helm-based deployment on Amazon EKS
Full reference for OpenMetadata Helm chart values
Store and rotate credentials securely using AWS Secrets Manager
# Kubernetes GKE Deployment | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/gke
Run your deployment on Google Kubernetes Engine (GKE) for a reliable, managed Kubernetes experience with secure configurations.
# GKE on Google Cloud Platform Deployment
OpenMetadata supports the Installation and Running of Application on Google Kubernetes Engine through Helm Charts.
However, there are some additional configurations which needs to be done as prerequisites for the same.
Google Kubernetes Engine (GKE) Auto Pilot Mode is not compatible with one of OpenMetadata Dependencies - ElasticSearch.
The reason being that ElasticSearch Pods require Elevated permissions to run initContainers for changing configurations which is not allowed by GKE AutoPilot PodSecurityPolicy.
All the code snippets in this section assume the `default` namespace for kubernetes.
## Prerequisites
### Cloud Database with CloudSQL and ElasticCloud for GCP as Search Engine
It is recommended to use GCP [Cloud SQL](https://cloud.google.com/sql/) services for Database and [Elastic Cloud GCP](https://www.elastic.co/partners/google-cloud) for Search Engine for Production.
We support -
* Cloud SQL (MySQL) engine version 8 or higher
* Cloud SQL (postgreSQL) engine version 15 or higher
* ElasticSearch version 9.x (minimum 9.0.0, recommended 9.3.0)
We recommend -
* CloudSQL to be Multi Zone Available
* Elastic Cloud Environment with multiple zones and minimum 2 nodes
Make sure to increase `sort_buffer_size` ([for MySQL](https://cloud.google.com/sql/docs/mysql/flags)) or `work_mem` ([for PostgreSQL](https://cloud.google.com/sql/docs/postgres/flags)) to the recommended value of **20MB** or more using flags. This is especially important when running migrations to prevent **Out of Sort Memory Error**. You can revert the setting once the migrations are complete.
## Kubernetes Orchestrator Configuration (Recommended)
Starting with OpenMetadata 1.12, we recommend using the **Kubernetes native orchestrator** for running ingestion pipelines. This eliminates the need for Apache Airflow and simplifies your deployment.
The Kubernetes orchestrator runs ingestion pipelines as native K8s Jobs and CronJobs. For full documentation on features, configuration options, and troubleshooting, see the [Kubernetes Orchestrator Guide](/v2.0.x/deployment/ingestion/kubernetes).
The recommended OMJob Operator approach requires installing Custom Resource Definitions (CRDs), which needs elevated cluster permissions. If your cluster policies don't allow CRDs, you can disable the operator by setting `useOMJobOperator: false` and `omjobOperator.enabled: false` in your values file to use native K8s Jobs instead.
### OpenMetadata Values Configuration
Create your `openmetadata-values.yaml` with the following configuration:
```yaml theme={null}
# openmetadata-values.yaml
openmetadata:
config:
# Database configuration
elasticsearch:
host:
searchType: elasticsearch
port: 443
scheme: https
connectionTimeoutSecs: 5
socketTimeoutSecs: 60
keepAliveTimeoutSecs: 600
batchSize: 10
auth:
enabled: true
username:
password:
secretRef: elasticsearch-secrets
secretKey: openmetadata-elasticsearch-password
database:
host:
port: 3306
driverClass: com.mysql.cj.jdbc.Driver
dbScheme: mysql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: mysql-secrets
secretKey: openmetadata-mysql-password
# Kubernetes Orchestrator configuration
pipelineServiceClientConfig:
enabled: true
type: "k8s"
metadataApiEndpoint: http://openmetadata:8585/api
k8s:
useOMJobOperator: true
# Enable the OMJob Operator (recommended for production)
omjobOperator:
enabled: true
```
For advanced configuration options such as resource limits, job lifecycle settings, failure diagnostics, RBAC, and security contexts, see the [Kubernetes Orchestrator Guide](/v2.0.x/deployment/ingestion/kubernetes).
For Database as PostgreSQL, use the below config for database values:
```yaml theme={null}
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: sql-secrets
secretKey: openmetadata-sql-password
```
### Create Kubernetes Secrets
Create the required secrets for CloudSQL and ElasticSearch:
```bash theme={null}
# Database secret
kubectl create secret generic mysql-secrets \
--from-literal=openmetadata-mysql-password=
# ElasticSearch secret
kubectl create secret generic elasticsearch-secrets \
--from-literal=openmetadata-elasticsearch-password=
```
### Deploy OpenMetadata
```bash theme={null}
# Add the OpenMetadata Helm repository
helm repo add open-metadata https://helm.open-metadata.org/
helm repo update
# Install OpenMetadata (no dependencies chart needed with K8s orchestrator)
helm install openmetadata open-metadata/openmetadata \
--values openmetadata-values.yaml
```
With the Kubernetes orchestrator, you don't need to deploy the `openmetadata-dependencies` chart that includes Airflow. This significantly simplifies your deployment.
### Verify the Deployment
```bash theme={null}
# Check pods are running
kubectl get pods
# Check the K8s orchestrator health in OpenMetadata UI
# Navigate to Settings → Preferences → Health
```
For deployments using Apache Airflow as the orchestrator, see the [GKE Airflow Orchestrator](/v2.0.x/deployment/kubernetes/gke/airflow) guide.
# GKE with Apache Airflow Orchestrator | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/gke/airflow
Configure Apache Airflow as the ingestion orchestrator on Google Kubernetes Engine with NFS persistent volumes and ReadWriteMany access.
# GKE with Apache Airflow Orchestrator
If you prefer to use Apache Airflow as the orchestrator (e.g., for existing Airflow investments or complex DAG requirements), follow the configuration below.
Using Airflow requires additional infrastructure: persistent volumes with ReadWriteMany access, the openmetadata-dependencies Helm chart, and more complex configuration.
## Persistent Volumes with ReadWriteMany Access Modes
OpenMetadata helm chart depends on Airflow and Airflow expects a persistent disk that support ReadWriteMany (the volume can be mounted as read-write by many nodes).
The workaround is to create nfs-server disk on Google Kubernetes Engine and use that as the persistent claim and deploy OpenMetadata by implementing the following steps in order.
### Create NFS Share
#### Provision GCP Persistent Disk for Google Kubernetes Engine
Run the below command to create a gcloud compute zonal disk. For more information on Google Cloud Disk Options, please visit [here](https://cloud.google.com/compute/docs/disks).
```commandline theme={null}
gcloud compute disks create --size=100GB --zone= nfs-disk
```
#### Deploy NFS Server in GKE
```yaml theme={null}
# nfs-server-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-server
spec:
replicas: 1
selector:
matchLabels:
role: nfs-server
template:
metadata:
labels:
role: nfs-server
spec:
initContainers:
- name: init-airflow-directories
image: busybox
command: ['sh', '-c', 'mkdir -p /exports/airflow-dags /exports/airflow-logs']
volumeMounts:
- mountPath: /exports
name: nfs-pvc
containers:
- name: nfs-server
image: itsthenetwork/nfs-server-alpine
env:
- name: SHARED_DIRECTORY
value: /exports
ports:
- name: nfs
containerPort: 2049
securityContext:
privileged: true
volumeMounts:
- mountPath: /exports
name: nfs-pvc
volumes:
- name: nfs-pvc
gcePersistentDisk:
pdName: nfs-disk
fsType: ext4
---
# nfs-cluster-ip-service.yml
apiVersion: v1
kind: Service
metadata:
name: nfs-server
spec:
ports:
- name: nfs
port: 2049
selector:
role: nfs-server
```
Run the commands below and ensure the pods are running.
```commandline theme={null}
kubectl create -f nfs-server-deployment.yml
kubectl create -f nfs-cluster-ip-service.yml
```
We create a ClusterIP Service for pods to access NFS within the cluster at a fixed IP/DNS.
#### Provision NFS backed PV and PVC for Airflow DAGs and Airflow Logs
Update `` with the NFS Service Cluster IP Address for below code snippets.
You can get the clusterIP using the following command
```commandline theme={null}
kubectl get service nfs-server -o jsonpath='{.spec.clusterIP}'
```
#### Code Samples for PV and PVC for Airflow DAGs
```yaml theme={null}
# dags_pv_pvc.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: openmetadata-dependencies-dags-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server:
path: "/airflow-dags"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: airflow
release: openmetadata-dependencies
name: openmetadata-dependencies-dags
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: ""
```
Create Persistent Volumes and Persistent Volume claims with the below command.
```commandline theme={null}
kubectl create -f dags_pv_pvc.yml
```
#### Code Samples for PV and PVC for Airflow Logs
```yaml theme={null}
# logs_pv_pvc.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: openmetadata-dependencies-logs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server:
path: "/airflow-logs"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: airflow
name: openmetadata-dependencies-logs
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: ""
```
Create Persistent Volumes and Persistent Volume claims with the below command.
```commandline theme={null}
kubectl create -f logs_pv_pvc.yml
```
### Change owner and permission manually on disks
Since airflow pods run as non root users, they would not have write access on the nfs server volumes. In order to fix the permission here, spin up a pod with persistent volumes attached and run it once.
```yaml theme={null}
# permissions_pod.yml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-permission-pod
name: my-permission-pod
spec:
containers:
- image: nginx
name: my-permission-pod
volumeMounts:
- name: airflow-dags
mountPath: /airflow-dags
- name: airflow-logs
mountPath: /airflow-logs
volumes:
- name: airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs
- name: airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags
dnsPolicy: ClusterFirst
restartPolicy: Always
```
Airflow runs the pods with linux user name as airflow and linux user id as 50000.
Run the below command to create the pod and fix the permissions
```commandline theme={null}
kubectl create -f permissions_pod.yml
```
Once the permissions pod is up and running, execute the below commands within the container.
```commandline theme={null}
kubectl exec --tty my-permission-pod --container my-permission-pod -- chown -R 50000 /airflow-dags /airflow-logs
# If needed
kubectl exec --tty my-permission-pod --container my-permission-pod -- chmod -R a+rwx /airflow-dags
```
### Create OpenMetadata dependencies Values
Override openmetadata dependencies airflow helm values to bind the nfs persistent volumes for DAGs and logs.
```yaml theme={null}
# values-dependencies.yml
airflow:
airflow:
extraVolumeMounts:
- mountPath: /airflow-logs
name: nfs-airflow-logs
- mountPath: /airflow-dags/dags
name: nfs-airflow-dags
extraVolumes:
- name: nfs-airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs
- name: nfs-airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags
config:
AIRFLOW__OPENMETADATA_AIRFLOW_APIS__DAG_GENERATED_CONFIGS: "/airflow-dags/dags"
dags:
path: /airflow-dags/dags
persistence:
enabled: false
logs:
path: /airflow-logs
persistence:
enabled: false
```
For more information on airflow helm chart values, please refer to [airflow-helm](https://artifacthub.io/packages/helm/airflow-helm/airflow/8.8.0).
When deploying openmeteadata dependencies helm chart, use the below command -
```commandline theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values-dependencies.yaml
```
The above command uses configurations defined [here](https://raw.githubusercontent.com/open-metadata/openmetadata-helm-charts/main/charts/deps/values.yaml).
You can modify any configuration and deploy by passing your own `values.yaml`
```commandline theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values
```
Once the openmetadata dependencies helm chart deployed, you can then run the below command to install the openmetadata helm chart -
```commandline theme={null}
helm install openmetadata open-metadata/openmetadata --values
```
Create CloudSQL and Elasticsearch credentials as Kubernetes Secrets, as described [here](/v2.0.x/quick-start/local-kubernetes-deployment#2-create-kubernetes-secrets-required-for-helm-charts).
Also, disable MySQL and Elasticsearch from OpenMetadata Dependencies Helm Charts as described in the [FAQ](#how-to-disable-mysql-and-elasticsearch-from-openmetadata-dependencies-helm-charts-).
## Troubleshooting
### Pods are stuck in Pending State due to Persistent Volume Creation Failure
If you came across `invalid access type while creating the pvc`, and the permission pod is stuck in "pending" state.
The above error might have occurred due to the pvc volumes not setup or pvc volumes are not mounted properly.
Please validate:
* all the prerequisites mentioned in this [section](/v2.0.x/deployment/kubernetes/gke#prerequisites)
* the configuration of `dags_pv_pvc.yml` file
* `storageClassName` field in YAML file
## FAQs
## Java Memory Heap Issue
If your openmetadata pods are not in ready state at any point in time and the openmetadata pod logs speaks about the below issue -
```
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "AsyncAppender-Worker-async-file-appender"
Exception in thread "pool-5-thread-1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-file-appender" java.lang.OutOfMemoryError: Java heap space
Exception in thread "dw-46" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-console-appender" java.lang.OutOfMemoryError: Java heap space
```
This is due to the default JVM Heap Space configuration (1 GiB) being not enough for your workloads. In order to resolve this issue, head over to your custom openmetadata helm values and append the below environment variable
```yaml theme={null}
extraEnvs:
- name: OPENMETADATA_HEAP_OPTS
value: "-Xmx2G -Xms2G"
```
The flag `Xmx` specifies the maximum memory allocation pool for a Java virtual machine (JVM), while `Xms` specifies the initial memory allocation pool.
Upgrade the helm charts with the above changes using the following command `helm upgrade --install openmetadata open-metadata/openmetadata --values --namespace `. Update this command your `values.yml` filename and `namespaceName` where you have deployed OpenMetadata in Kubernetes.
## PostgreSQL Issue permission denied to create extension "pgcrypto"
If you are facing the below issue with PostgreSQL as Database Backend for OpenMetadata Application,
```
Message: ERROR: permission denied to create extension "pgcrypto"
Hint: Must be superuser to create this extension.
```
It seems the Database User does not have sufficient privileges. In order to resolve the above issue, grant usage permissions to the PSQL User.
```sql theme={null}
GRANT USAGE ON SCHEMA schema_name TO ;
GRANT CREATE ON EXTENSION pgcrypto TO ;
```
In the above command, replace `` with the sql user used by OpenMetadata Application to connect to PostgreSQL Database.
## How to extend and use custom docker images with OpenMetadata Helm Charts ?
## Extending OpenMetadata Server Docker Image
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/server`
OpenMetadata helm charts uses official published docker images from [DockerHub](https://hub.docker.com/u/openmetadata).
A typical scenario will be to install organization certificates for connecting with inhouse systems.
For Example -
```
FROM docker.open-metadata.org/openmetadata/server:x.y.z
WORKDIR /home/
COPY .
RUN update-ca-certificates
```
where `docker.open-metadata.org/openmetadata/server:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/server:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update your openmetadata helm values yaml
The OpenMetadata Application gets installed as part of `openmetadata` helm chart. In this step, update the custom helm values using YAML file to point the image created in the previous step. For example, create a helm values file named `values.yaml` with the following contents -
```yaml theme={null}
...
image:
repository:
# Overrides the image tag whose default is the chart appVersion.
tag:
...
```
### 3. Install / Upgrade your helm release
Upgrade/Install your openmetadata helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata open-metadata/openmetadata--values values.yaml
```
## Extending OpenMetadata Ingestion Docker Image
One possible use case for a custom ingestion image is a custom connector. Build and test the package with the same `openmetadata-ingestion` version as your deployment. After your code is ready, follow these steps:
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/ingestion`:
For example -
```
FROM docker.open-metadata.org/openmetadata/ingestion:x.y.z
USER airflow
# Let's use the home directory of airflow user
WORKDIR /home/airflow
# Install our custom connector
COPY
COPY setup.py .
RUN pip install --no-deps .
```
where `docker.open-metadata.org/openmetadata/ingestion:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/ingestion:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update the airflow in openmetadata dependencies values YAML
The ingestion containers (which is the one shipping Airflow) gets installed in the `openmetadata-dependencies` helm chart. In this step, we use
our own custom values YAML file to point to the image we just created on the previous step. You can create a file named `values.deps.yaml` with the
following contents:
```yaml theme={null}
airflow:
airflow:
image:
repository: # by default, openmetadata/ingestion
tag: # by default, the version you are deploying, e.g., 1.1.0
pullPolicy: "IfNotPresent"
```
### 3. Install / Upgrade helm release
Upgrade/Install your openmetadata-dependencies helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values.deps.yaml
```
## How to disable MySQL and ElasticSearch from OpenMetadata Dependencies Helm Charts ?
If you are using MySQL and ElasticSearch externally, you would want to disable the local installation of mysql and elasticsearch while installing OpenMetadata Dependencies Helm Chart. You can disable the MySQL and ElasticSearch Helm Dependencies by setting `enabled: false` value for each dependency. Below is the command to set helm values from Helm CLI -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --set mysql.enabled=false --set elasticsearch.enabled=false
```
Alternatively, you can create a custom YAML file named `values.deps.yaml` to disable installation of MySQL and Elasticsearch .
```yaml theme={null}
mysql:
enabled: false
...
elasticsearch:
enabled: false
...
...
```
## How to configure external database like PostgreSQL with OpenMetadata Helm Charts ?
OpenMetadata Supports PostgreSQL as one of the Database Dependencies. OpenMetadata Helm Charts by default does not include PostgreSQL as Database Dependencies. In order to configure Helm Charts with External Database like PostgreSQL, follow the below guide to make the helm values change and upgrade / install OpenMetadata helm charts with the same.
## Upgrade Airflow Helm Dependencies Helm Charts to connect to External Database like PostgreSQL
We ship [airflow-helm](https://github.com/airflow-helm/charts/tree/main/charts/airflow) as one of OpenMetadata Dependencies with default values to connect to MySQL Database as part of `externalDatabase` configurations.
You can find more information on setting the `externalDatabase` as part of helm values [here](https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md).
With OpenMetadata Dependencies Helm Charts, your helm values would look something like below -
```yaml theme={null}
...
airflow:
externalDatabase:
type: postgresql
host:
port: 5432
database:
user:
passwordSecret: airflow-postgresql-secrets
passwordSecretKey: airflow-postgresql-password
...
```
For the above code, it is assumed you are creating a kubernetes secret for storing Airflow Database login Credentials. A sample command to create the secret will be `kubectl create secret generic airflow-postgresql-secrets --from-literal=airflow-postgresql-password=`.
## Upgrade OpenMetadata Helm Charts to connect to External Database like PostgreSQL
Update the `openmetadata.config.database.*` helm values for OpenMetadata Application to connect to External Database like PostgreSQL.
With OpenMetadata Helm Charts, your helm values would look something like below -
```yaml theme={null}
openmetadata:
config:
...
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: openmetadata-postgresql-secrets
secretKey: openmetadata-postgresql-password
```
For the above code, it is assumed you are creating a kubernetes secret for storing OpenMetadata Database login Credentials. A sample command to create the secret will be `kubectl create secret generic openmetadata-postgresql-secrets --from-literal=openmetadata-postgresql-password=`.
Once you make the above changes to your helm values, run the below command to install/upgrade helm charts -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values <> --namespace
helm upgrade --install openmetadata open-metadata/openmetadata --values <> --namespace
```
## How to customize OpenMetadata Dependencies Helm Chart with custom helm values
Our OpenMetadata Dependencies Helm Charts are internally depends on three sub-charts -
* [Bitnami MySQL](https://artifacthub.io/packages/helm/bitnami/mysql/9.7.2) (helm chart version 9.7.2)
* [OpenSearch](https://artifacthub.io/packages/helm/opensearch-project-helm-charts/opensearch/2.12.2) (helm chart version 2.12.2)
* [Airflow](https://artifacthub.io/packages/helm/airflow-helm/airflow/8.8.0) (helm chart version 8.8.0)
If you are looking to customize the deployments of any of the above dependencies, please refer to the above links for customizations of helm values for further references.
By default, OpenMetadata Dependencies helm chart provides initial generic customization of these helm values in order to get you started quickly. You can refer to the openmetadata-dependencies helm charts default values [here](https://github.com/open-metadata/openmetadata-helm-charts/blob/main/charts/deps/values.yaml).
# Kubernetes On Premises Deployment | Official Documentation
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/on-prem
Set up the OpenMetadata on-premises with Kubernetes for full control over infrastructure, security, and compliance requirements.
# On Premises Kubernetes Deployment
OpenMetadata supports the Installation and Running of application on OnPremises Kubernetes through Helm Charts.
However, there are some additional configurations which needs to be done as prerequisites for the same.
This guide presumes you have an on premises Kubernetes cluster setup, and you are installing OpenMetadata in `default` namespace.
## Prerequisites
### External Database and Search Engine as ElasticSearch / OpenSearch
We support
* MySQL engine version 8 or higher
* PostgreSQL engine version 15 or higher
* ElasticSearch version 9.x (minimum 9.0.0, recommended 9.3.0) or OpenSearch version 3.x (minimum 3.0.0, recommended 3.3.0)
Once you have the External Database and Search Engine configured, you can update the environment variables below for OpenMetadata kubernetes deployments to connect with Database and ElasticSearch.
```yaml theme={null}
# openmetadata-values.prod.yaml
...
openmetadata:
config:
elasticsearch:
host:
searchType: elasticsearch # or `opensearch` if Search Engine is OpenSearch
port: 443
scheme: https
connectionTimeoutSecs: 5
socketTimeoutSecs: 60
keepAliveTimeoutSecs: 600
batchSize: 10
auth:
enabled: true
username:
password:
secretRef: elasticsearch-secrets
secretKey: openmetadata-elasticsearch-password
database:
host:
port: 3306
driverClass: com.mysql.cj.jdbc.Driver
dbScheme: mysql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: mysql-secrets
secretKey: openmetadata-mysql-password
...
```
Create database and search engine credentials as Kubernetes Secrets, as described [here](/v2.0.x/quick-start/local-kubernetes-deployment#2-create-kubernetes-secrets-required-for-helm-charts).
Also, disable MySQL and Elasticsearch from OpenMetadata Dependencies Helm Charts as described in the [FAQ](/v2.0.x/deployment/kubernetes/on-prem/airflow#how-to-disable-mysql-and-elasticsearch-from-openmetadata-dependencies-helm-charts-).
### Persistent Volumes with ReadWriteMany Access Modes
OpenMetadata helm chart depends on Airflow and Airflow expects a persistent disk that support ReadWriteMany (the volume can be mounted as read-write by many nodes).
The workaround is to create nfs-share and use that as the persistent claim to deploy OpenMetadata by implementing the following steps in order.
This guide assumes you have NFS Server already setup with Hostname or IP Address which is reachable from your on premises Kubernetes cluster, and you have configured a path to be used for OpenMetadata Airflow Helm Dependency.
### Dynamic Provisioning using StorageClass
To provision PersistentVolume dynamically using the StorageClass, you need to install the NFS provisioner.
It is recommended to use [nfs-subdir-external-provisioner](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner) helm charts for this case.
```commandline theme={null}
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--create-namespace \
--namespace nfs-provisioner \
--set nfs.server= \
--set nfs.path=/airflow
```
Replace the `NFS_HOSTNAME_OR_IP` with your NFS Server value and run the commands.
This will create a new StorageClass with `nfs-subdir-external-provisioner`. You can view the same using the kubectl command `kubectl get storageclass -n nfs-provisioner`.
***
Continue to [On-Prem Airflow Storage Setup](/v2.0.x/deployment/kubernetes/on-prem/airflow) to provision NFS-backed persistent volumes, configure Airflow dependencies, and deploy OpenMetadata.
# On-Prem Airflow Storage Setup | OpenMetadata Kubernetes Guide
Source: https://docs.open-metadata.org/v2.0.x/deployment/kubernetes/on-prem/airflow
Provision NFS-backed persistent volumes for Airflow DAGs and logs on on-premises Kubernetes and deploy OpenMetadata.
# On-Prem Airflow Storage Setup
This guide walks through provisioning NFS-backed PVCs for Airflow DAGs and logs on an on-premises Kubernetes cluster, then deploying OpenMetadata. For prerequisites and StorageClass setup, see the [On-Prem Kubernetes Deployment](/v2.0.x/deployment/kubernetes/on-prem) page.
## Provision NFS backed PVC for Airflow DAGs and Airflow Logs
### Code Samples for PVC for Airflow DAGs
```yaml theme={null}
# dags_pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: default
name: openmetadata-dependencies-dags
labels:
storage.k8s.io/name: nfs
app: airflow
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 1Gi
```
Create Persistent Volumes and Persistent Volume claims with the below command.
```commandline theme={null}
kubectl create -f dags_pvc.yml
```
### Code Samples for PVC for Airflow Logs
```yaml theme={null}
# logs_pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: default
name: openmetadata-dependencies-logs
labels:
storage.k8s.io/name: nfs
app: airflow
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 10Gi
```
Create Persistent Volumes and Persistent Volume claims with the below command.
```commandline theme={null}
kubectl create -f logs_pvc.yml
```
## Change owner and permission manually on disks
Since airflow pods run as non-root users, they would not have write access on the nfs server volumes. In order to fix the permission here, spin up a pod with persistent volumes attached and run it once.
```yaml theme={null}
# permissions_pod.yml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-permission-pod
name: my-permission-pod
spec:
containers:
- image: busybox
name: my-permission-pod
volumeMounts:
- name: airflow-dags
mountPath: /airflow-dags
- name: airflow-logs
mountPath: /airflow-logs
command:
- "chown -R 50000 /airflow-dags /airflow-logs"
# if needed
- "chmod -R a+rwx /airflow-dags"
volumes:
- name: airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs
- name: airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags
dnsPolicy: ClusterFirst
restartPolicy: Always
```
Airflow runs the pods with linux username as airflow and linux user id as 50000.
Run the below command to create the pod and fix the permissions
```commandline theme={null}
kubectl create -f permissions_pod.yml
```
## Create OpenMetadata dependencies Values
Override openmetadata dependencies airflow helm values to bind the nfs persistent volumes for DAGs and logs.
```yaml theme={null}
# values-dependencies.yml
airflow:
airflow:
extraVolumeMounts:
- mountPath: /airflow-logs
name: nfs-airflow-logs
- mountPath: /airflow-dags/dags
name: nfs-airflow-dags
extraVolumes:
- name: nfs-airflow-logs
persistentVolumeClaim:
claimName: openmetadata-dependencies-logs
- name: nfs-airflow-dags
persistentVolumeClaim:
claimName: openmetadata-dependencies-dags
config:
AIRFLOW__OPENMETADATA_AIRFLOW_APIS__DAG_GENERATED_CONFIGS: "/airflow-dags/dags"
dags:
path: /airflow-dags/dags
persistence:
enabled: false
logs:
path: /airflow-logs
persistence:
enabled: false
```
For more information on airflow helm chart values, please refer to [airflow-helm](https://artifacthub.io/packages/helm/airflow-helm/airflow/8.8.0).
When deploying openmetadata dependencies helm chart, use the below command -
```commandline theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values-dependencies.yaml
```
The above command uses configurations defined [here](https://raw.githubusercontent.com/open-metadata/openmetadata-helm-charts/main/charts/deps/values.yaml).
You can modify any configuration and deploy by passing your own `values.yaml`
```commandline theme={null}
helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values
```
Once the openmetadata dependencies helm chart deployed, you can then run the below command to install the openmetadata helm chart -
```commandline theme={null}
helm install openmetadata open-metadata/openmetadata
```
Again, this uses the values defined [here](https://github.com/open-metadata/openmetadata-helm-charts/blob/main/charts/openmetadata/values.yaml).
Use the `--values` flag to point to your own YAML configuration if needed.
## Troubleshooting
Starting with **OpenMetadata v1.12.4**, the dependency Helm chart no longer supports passing database passwords using individual Kubernetes secret keys (for example, `passwordSecret` and `passwordSecretKey`).
Instead, database credentials must be provided via a **single Kubernetes Secret** referenced using `metadataSecretName`. This secret must contain the **full database connection string**, including the password.
```yaml theme={null}
data:
metadataSecretName: your-connection-string-secret
```
This change applies **only to the dependency `values.yml` configuration** and aligns with the database configuration approach used by the **Airflow Helm chart**.
## FAQs
## Java Memory Heap Issue
If your openmetadata pods are not in ready state at any point in time and the openmetadata pod logs speaks about the below issue -
```
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "AsyncAppender-Worker-async-file-appender"
Exception in thread "pool-5-thread-1" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-file-appender" java.lang.OutOfMemoryError: Java heap space
Exception in thread "dw-46" java.lang.OutOfMemoryError: Java heap space
Exception in thread "AsyncAppender-Worker-async-console-appender" java.lang.OutOfMemoryError: Java heap space
```
This is due to the default JVM Heap Space configuration (1 GiB) being not enough for your workloads. In order to resolve this issue, head over to your custom openmetadata helm values and append the below environment variable
```yaml theme={null}
extraEnvs:
- name: OPENMETADATA_HEAP_OPTS
value: "-Xmx2G -Xms2G"
```
The flag `Xmx` specifies the maximum memory allocation pool for a Java virtual machine (JVM), while `Xms` specifies the initial memory allocation pool.
Upgrade the helm charts with the above changes using the following command `helm upgrade --install openmetadata open-metadata/openmetadata --values --namespace `. Update this command your `values.yml` filename and `namespaceName` where you have deployed OpenMetadata in Kubernetes.
## PostgreSQL Issue permission denied to create extension "pgcrypto"
If you are facing the below issue with PostgreSQL as Database Backend for OpenMetadata Application,
```
Message: ERROR: permission denied to create extension "pgcrypto"
Hint: Must be superuser to create this extension.
```
It seems the Database User does not have sufficient privileges. In order to resolve the above issue, grant usage permissions to the PSQL User.
```sql theme={null}
GRANT USAGE ON SCHEMA schema_name TO ;
GRANT CREATE ON EXTENSION pgcrypto TO ;
```
In the above command, replace `` with the sql user used by OpenMetadata Application to connect to PostgreSQL Database.
## How to extend and use custom docker images with OpenMetadata Helm Charts ?
## Extending OpenMetadata Server Docker Image
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/server`
OpenMetadata helm charts uses official published docker images from [DockerHub](https://hub.docker.com/u/openmetadata).
A typical scenario will be to install organization certificates for connecting with inhouse systems.
For Example -
```
FROM docker.open-metadata.org/openmetadata/server:x.y.z
WORKDIR /home/
COPY .
RUN update-ca-certificates
```
where `docker.open-metadata.org/openmetadata/server:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/server:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update your openmetadata helm values yaml
The OpenMetadata Application gets installed as part of `openmetadata` helm chart. In this step, update the custom helm values using YAML file to point the image created in the previous step. For example, create a helm values file named `values.yaml` with the following contents -
```yaml theme={null}
...
image:
repository:
# Overrides the image tag whose default is the chart appVersion.
tag:
...
```
### 3. Install / Upgrade your helm release
Upgrade/Install your openmetadata helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata open-metadata/openmetadata--values values.yaml
```
## Extending OpenMetadata Ingestion Docker Image
One possible use case for a custom ingestion image is a custom connector. Build and test the package with the same `openmetadata-ingestion` version as your deployment. After your code is ready, follow these steps:
### 1. Create a `Dockerfile` based on `docker.open-metadata.org/openmetadata/ingestion`:
For example -
```
FROM docker.open-metadata.org/openmetadata/ingestion:x.y.z
USER airflow
# Let's use the home directory of airflow user
WORKDIR /home/airflow
# Install our custom connector
COPY
COPY setup.py .
RUN pip install --no-deps .
```
where `docker.open-metadata.org/openmetadata/ingestion:x.y.z` needs to point to the same version of the OpenMetadata server, for example `docker.open-metadata.org/openmetadata/ingestion:1.3.1`.
This image needs to be built and published to the container registry of your choice.
### 2. Update the airflow in openmetadata dependencies values YAML
The ingestion containers (which is the one shipping Airflow) gets installed in the `openmetadata-dependencies` helm chart. In this step, we use
our own custom values YAML file to point to the image we just created on the previous step. You can create a file named `values.deps.yaml` with the
following contents:
```yaml theme={null}
airflow:
airflow:
image:
repository: # by default, openmetadata/ingestion
tag: # by default, the version you are deploying, e.g., 1.1.0
pullPolicy: "IfNotPresent"
```
### 3. Install / Upgrade helm release
Upgrade/Install your openmetadata-dependencies helm charts with the below single command:
```bash theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --values values.deps.yaml
```
## How to disable MySQL and ElasticSearch from OpenMetadata Dependencies Helm Charts ?
If you are using MySQL and ElasticSearch externally, you would want to disable the local installation of mysql and elasticsearch while installing OpenMetadata Dependencies Helm Chart. You can disable the MySQL and ElasticSearch Helm Dependencies by setting `enabled: false` value for each dependency. Below is the command to set helm values from Helm CLI -
```commandline theme={null}
helm upgrade --install openmetadata-dependencies open-metadata/openmetadata-dependencies --set mysql.enabled=false --set elasticsearch.enabled=false
```
Alternatively, you can create a custom YAML file named `values.deps.yaml` to disable installation of MySQL and Elasticsearch .
```yaml theme={null}
mysql:
enabled: false
...
elasticsearch:
enabled: false
...
...
```
## How to configure external database like PostgreSQL with OpenMetadata Helm Charts ?
OpenMetadata Supports PostgreSQL as one of the Database Dependencies. OpenMetadata Helm Charts by default does not include PostgreSQL as Database Dependencies. In order to configure Helm Charts with External Database like PostgreSQL, follow the below guide to make the helm values change and upgrade / install OpenMetadata helm charts with the same.
## Upgrade Airflow Helm Dependencies Helm Charts to connect to External Database like PostgreSQL
We ship [airflow-helm](https://github.com/airflow-helm/charts/tree/main/charts/airflow) as one of OpenMetadata Dependencies with default values to connect to MySQL Database as part of `externalDatabase` configurations.
You can find more information on setting the `externalDatabase` as part of helm values [here](https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md).
With OpenMetadata Dependencies Helm Charts, your helm values would look something like below -
```yaml theme={null}
...
airflow:
externalDatabase:
type: postgresql
host:
port: 5432
database:
user:
passwordSecret: airflow-postgresql-secrets
passwordSecretKey: airflow-postgresql-password
...
```
For the above code, it is assumed you are creating a kubernetes secret for storing Airflow Database login Credentials. A sample command to create the secret will be `kubectl create secret generic airflow-postgresql-secrets --from-literal=airflow-postgresql-password=`.
## Upgrade OpenMetadata Helm Charts to connect to External Database like PostgreSQL
Update the `openmetadata.config.database.*` helm values for OpenMetadata Application to connect to External Database like PostgreSQL.
With OpenMetadata Helm Charts, your helm values would look something like below -
```yaml theme={null}
openmetadata:
config:
...
database:
host:
port: 5432
driverClass: org.postgresql.Driver
dbScheme: postgresql
dbUseSSL: true
databaseName:
auth:
username:
password:
secretRef: openmetadata-postgresql-secrets
secretKey: openmetadata-postgresql-password
```
For the above code, it is assumed you are creating a kubernetes secret for storing OpenMetadata Database login Credentials. A sample command to create the secret will be `kubectl create secret generic openmetadata-postgresql-secrets --from-literal=openmetadata-postgresql-password=