Building OpenMetadata on Local Kubernetres
自宅のKubernetes環境にOpenMetadataを構築し、Cloudflare Tunnelで外部公開して遊んでみたので備忘録です。
ローカルKubernetesにOpenMetadataをデプロイする
手順については、次の2つの公式ドキュメントを参照し、一部修正を行い構築しました。
ローカルマシンスペックについて
公式ドキュメントには、4vCPUと8GiBのメモリを備えた minikube クラスターをスペック条件と記載があります。
minikube start --cpus=4 --memory=8192
最低でもこのスペックは確保しておきます。
今回はminikubeではなく、kubeadmで構築したクラスターで各ノードは4coreのRAM16GiBで構築しています。
事前準備
Helmをインストールする
すでにHelmをインストールされている場合はこの手順はスキップしてOKです。
今回の構成では全ノードでHelmを利用するので、各ノードで実行します。
$ curl -O https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ bash ./get-helm-3
$ helm version
version.BuildInfo{Version:"v3.16.4", GitCommit:"7877b45b63f95635153b29a42c0c2f4273ec45ca", GitTreeState:"clean", GoVersion:"go1.22.7"}
StorageClass を使用した動的プロビジョニング
今回Helmを使ったOpenMetadata構築を行います。
OpenMetadata HelmチャートはAirflowに依存しており、AirflowはReadWriteMany(ボリュームは多くのノードによって読み取り/書き込みとしてマウントできます)をサポートする永続ディスクを想定しています。
そのため、nfs-shareを作成し、それを永続的なストレージとして使用してます。
StorageClassを使用してPersistentVolumeを動的にプロビジョニングするには、NFSプロビジョナーをインストールする必要があります。
今回は、公式推奨のnfs-subdir-external-provisioner Helmチャートを使用します。
ただし、すでに動的ストレージが存在する場合はこの手順はスキップしてOKです。
NFSサーバの構築
私の環境にNFSサーバが存在しないので、作っていきます。
今回はワーカーノードにNFSサーバを用意しています。
$ sudo apt update
$ sudo apt install nfs-kernel-server -y
$ sudo mkdir -p /airflow
$ sudo chmod 777 /airflow
$ sudo mkdir -p /airflow/airflow-dags /airflow/airflow-logs
$ sudo chown -R nobody:nogroup /airflow
$ sudo chmod -R 777 /airflow
/etc/exportsにマウント時に指定するパスを設定
この後Airflow用のPersistentVolumeClaimを作成しますが、そのマウントパスが/airflow-dagsや/airflow-logsとするので、ルートのマウントパスは/airflowとし、その配下にそれぞれのPersistentVolumeClaimのパスが来るようにしています。
$ sudo cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/airflow *(rw,sync,no_root_squash,no_subtree_check) #追記
設定の反映を行う
$ sudo exportfs -ra
nfs-subdir-external-provisionerのインストール
パラメータ説明
| パラメータ | 概要 |
|---|---|
| nfs.server | NFSサーバのホスト名もしくはIPアドレスを指定 |
| nfs.path | NFSサーバ側で/etc/exportsに設定したマウントパスを指定 |
$ sudo helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --create-namespace --namespace nfs-provisioner --set nfs.server=k8s-worker01 --set nfs.path=/airflow
nfs-serverを有効にする
$ sudo systemctl enable nfs-server
$ sudo systemctl start nfs-server
$ sudo exportfs -v
/airflow <world>(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
PersistentVolumeClaimを作成
動的プロビジョニングでAirflowのdagとlogsのPersistentVolumeClaimを作成します。
dag用とlog用のPVCは以下のGithubの通りに作成しました。
作成した定義ファイルからPVCを作成する。
$ sudo kubectl apply -f dags-pvc.yml
persistentvolumeclaim/openmetadata-dependencies-dags created
$ sudo kubectl apply -f logs-pvc.yml
persistentvolumeclaim/openmetadata-dependencies-logs created
$ sudo kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
openmetadata-dependencies-dags Bound pvc-82f91642-b6ce-4a97-930c-************ 1Gi RWX nfs-client <unset> 46s
openmetadata-dependencies-logs Bound pvc-d0c781f9-3784-41ef-a503-************ 1Gi RWX nfs-client <unset> 18s
ディスクの所有者と権限を手動で変更する
Airflowポッドは非ルートユーザーとして実行されるため、NFSサーバー ボリュームへの書き込みアクセス権がありません。
ここで権限を修正するには、永続ボリュームが接続されたポッドを起動し、それを1回実行します。
定義ファイル
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
権限を修正するためにmy-permission-podを起動します。
$ sudo kubectl create -f permissions_pod.yml
Podの起動以外にNFSサーバにログインして以下のコマンドを実行するでもOKです。
$ sudo chown -R 50000 /airflow/airflow-dags /airflow/airflow-logs
$ sudo chmod -R a+rwx /airflow/airflow-dags
OpenMetadata依存関係値を作成する
openmetadata依存関係のAirflow Helm値をオーバーライドして、DAGとログのnfs永続ボリュームをバインドします。
Opensearch/MySQL用のローカルストレージを用意
ワーカーノードにてOpensearch/MySQL用のローカルストレージ領域を確保します。
$ sudo mkdir -p /mnt/data/mysql
$ sudo chmod 777 /mnt/data/mysql
$ sudo mkdir -p /mnt/data/opensearch
$ sudo chmod 777 /mnt/data/opensearch
OpensearchのPVとPVC定義ファイル
MySQLのPVとPVC定義ファイル
$ sudo kubectl apply -f mysql-pv.yaml
$ sudo kubectl apply -f mysql-pvc.yaml
$ sudo kubectl apply -f opensearch-pv.yaml
$ sudo kubectl apply -f opensearch-pvc.yaml
$ sudo kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
mysql-pv 50Gi RWO Retain Bound default/mysql-pvc mysql-local-storage <unset> 24s
opensearch-pv 30Gi RWO Retain Bound default/opensearch-pvc opensearch-local-storage <unset> 16s
$ sudo kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-pvc Bound mysql-pv 50Gi RWO mysql-local-storage <unset> 25s
opensearch-pvc Bound opensearch-pv 30Gi RWO opensearch-local-storage <unset> 17s
openmetadata-dependencies-dags Bound pvc-82f91642-b6ce-4a97-930c-************ 1Gi RWX nfs-client <unset> 46s
openmetadata-dependencies-logs Bound pvc-d0c781f9-3784-41ef-a503-************ 1Gi RWX nfs-client <unset> 18s
Helm Chartsに必要なKubernetes Secretを作成する
$ sudo kubectl create secret generic mysql-secrets --from-literal=openmetadata-mysql-password=openmetadata_password
$ sudo kubectl create secret generic airflow-secrets --from-literal=openmetadata-airflow-password=admin
$ sudo kubectl create secret generic airflow-mysql-secrets --from-literal=airflow-mysql-password=airflow_pass
ローカル展開用のHelmリポジトリを追加する
$ sudo helm repo add open-metadata https://helm.open-metadata.org/
Helmリポジトリが追加されたことを確認
$ sudo helm repo list
NAME URL
open-metadata https://helm.open-metadata.org/
OpenMetadata Dependencies Helm Chart をインストールする
前提条件で作成したAirflowの
$ sudo helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values ./values-dependencies.yml
NAME: openmetadata-dependencies
LAST DEPLOYED: Tue Dec 24 12:51:11 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
Pod起動確認
kyami@k8s-master:~/openmetadata$ sudo kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 2m18s
openmetadata-dependencies-db-migrations-5b5d54cf78-kbx4q 1/1 Running 0 2m18s
openmetadata-dependencies-scheduler-699ff94db6-jck27 1/1 Running 0 2m18s
openmetadata-dependencies-sync-users-86f7fd7978-fgddp 1/1 Running 0 2m18s
openmetadata-dependencies-triggerer-7d968c7955-jcbgf 1/1 Running 0 2m18s
openmetadata-dependencies-web-6b9b65dbb6-dvx6q 1/1 Running 0 2m18s
opensearch-0 1/1 Running 0 2m18s
kyami@k8s-master:~/openmetadata$
OpenMetadata Helm Chartをインストールする
公式のHelmだと、OpenMetadataのサインイン画面上に「CREATE ACCOUNT」のボタンがあり、誰でもユーザを作成できてしまいます。
それを回避するために、Helmの「openmetadata.config.authentication.enableSelfSignup」をfalseに変更する定義ファイルを作成し、Helmインストール時に定義ファイルを指定します。
kyami@k8s-master:~/openmetadata$ sudo helm install openmetadata open-metadata/openmetadata --values ./openmetadata_values.yml
W0406 03:00:19.364696 9052 warnings.go:70] unknown field "spec.jobTemplate.spec.app.kubernetes.io/instance"
W0406 03:00:19.364875 9052 warnings.go:70] unknown field "spec.jobTemplate.spec.app.kubernetes.io/name"
W0406 03:00:19.364891 9052 warnings.go:70] unknown field "spec.jobTemplate.spec.matchLabels"
W0406 03:00:19.364698 9052 warnings.go:70] unknown field "spec.jobTemplate.spec.app.kubernetes.io/instance"
W0406 03:00:19.365000 9052 warnings.go:70] unknown field "spec.jobTemplate.spec.app.kubernetes.io/name"
W0406 03:00:19.365013 9052 warnings.go:70] unknown field "spec.jobTemplate.spec.matchLabels"
NAME: openmetadata
LAST DEPLOYED: Sun Apr 6 03:00:18 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=openmetadata,app.kubernetes.io/instance=openmetadata" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8585 to use your application"
kubectl --namespace default port-forward $POD_NAME 8585:$CONTAINER_PORT
Pod起動確認
kyami@k8s-master:~/openmetadata$ sudo kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 1/1 Running 0 4m26s
openmetadata-6cbbc9d994-p4xsm 1/1 Running 0 99s
openmetadata-dependencies-db-migrations-5b5d54cf78-kbx4q 1/1 Running 0 4m26s
openmetadata-dependencies-scheduler-699ff94db6-jck27 1/1 Running 0 4m26s
openmetadata-dependencies-sync-users-86f7fd7978-fgddp 1/1 Running 0 4m26s
openmetadata-dependencies-triggerer-7d968c7955-jcbgf 1/1 Running 0 4m26s
openmetadata-dependencies-web-6b9b65dbb6-dvx6q 1/1 Running 0 4m26s
opensearch-0 1/1 Running 0 4m26s
kyami@k8s-master:~/openmetadata$
Cloudflare Tunnelを使った外部公開
Cloudflare Tunnel設定は以下の記事を参考ください。
cloudflared.yamlの定義ファイル
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflared
spec:
selector:
matchLabels:
app: cloudflared
replicas: 2 # You could also consider elastic scaling for this deployment
template:
metadata:
labels:
app: cloudflared
spec:
containers:
- name: cloudflared
image: cloudflare/cloudflared:2022.3.0
args:
- tunnel
# Points cloudflared to the config file, which configures what
# cloudflared will actually do. This file is created by a ConfigMap
# below.
- --config
- /etc/cloudflared/config/config.yaml
- run
livenessProbe:
httpGet:
# Cloudflared has a /ready endpoint which returns 200 if and only if
# it has an active connection to the edge.
path: /ready
port: 2000
failureThreshold: 1
initialDelaySeconds: 10
periodSeconds: 10
volumeMounts:
- name: config
mountPath: /etc/cloudflared/config
readOnly: true
# Each tunnel has an associated "credentials file" which authorizes machines
# to run the tunnel. cloudflared will read this file from its local filesystem,
# and it'll be stored in a k8s secret.
- name: creds
mountPath: /etc/cloudflared/creds
readOnly: true
volumes:
- name: creds
secret:
# By default, the credentials file will be created under ~/.cloudflared/<tunnel ID>.json
# when you run `cloudflared tunnel create`. You can move it into a secret by using:
# ```sh
# kubectl create secret generic tunnel-credentials \
# --from-file=credentials.json=/Users/yourusername/.cloudflared/<tunnel ID>.json
# ```
secretName: openmetadata-tunnel-credentials
# Create a config.yaml file from the ConfigMap below.
- name: config
configMap:
name: cloudflared
items:
- key: config.yaml
path: config.yaml
---
# This ConfigMap is just a way to define the cloudflared config.yaml file in k8s.
# It's useful to define it in k8s, rather than as a stand-alone .yaml file, because
# this lets you use various k8s templating solutions (e.g. Helm charts) to
# parameterize your config, instead of just using string literals.
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflared
data:
config.yaml: |
# Name of the tunnel you want to run
tunnel: openmetadata-tunnel
credentials-file: /etc/cloudflared/creds/credentials.json
# Serves the metrics server under /metrics and the readiness server under /ready
metrics: 0.0.0.0:2000
no-autoupdate: true
ingress:
- hostname: omd.kyamisama.com
service: http://openmetadata:8585
- service: http_status:404
$ sudo kubectl apply -f cloudflared.yaml
ブラウザからアクセスしてみる

デフォルトのログイン資格情報はadmin@open-metadata.org:adminです。
おわり
私自身そこまでKuberneteに詳しくないのですが、それでも公式ドキュメントやググりながらなんとか構築できたので、もし興味がある人は是非お試しください。
あといつの間にか機能が増えていたので別記事で色々と検証してみたいと思います!
その前に次回はOpenMetadataの認証認可周りをkeycloakでやる記事を書こうと思います!
Discussion