🌩️

AWS で構築するセルフホスト版 Lightdash

に公開

Lightdash は、dbt プロジェクト内で定義した dimension や metrics を用いて可視化を行うことができる BI ツールです。データ層と BI/AI 層の中間に位置する抽象化層、いわゆる「セマンティックレイヤー」の整備がデータ利活用における大きな課題として認識されつつありますが、Lightdash は dbt モデルのスキーマ YAML 定義がそのままセマンティックレイヤーとして機能するという点で、昨今特に注目を集めているツールの一つではないかと思います。

https://www.lightdash.com/

そんな Lightdash ですが、利用形態としてセルフホスト版(以下、OSS 版)と Cloud SaaS 版があります。今回、OSS 版 Lightdash を AWS 上に構築してみたので、本記事ではそのアーキテクチャ概要と構築時のポイントについてご紹介できればと思います。

OSS 版と Cloud 版の違いについて

本題に入る前に、OSS 版と Cloud 版の違いについて少し触れておきたいと思います。

OSS 版

OSS 版は、自前のインフラ環境に Lightdash サーバーを構築することで、ライセンス費用なく利用可能な形態です。dbt とのネイティブ統合や、ダッシュボード・可視化の作成 など、基本的なユースケースに対応できます。

Lightdash 自体の利用に費用は発生しませんが、Lightdash が稼働するインフラの金銭的コストや、その構築・保守のための人的コストはかかってきます。また、AI agents のような AI 系の機能など、高度な機能については利用できないようです。

Cloud 版

Cloud 版は SaaS 製品として、Lightdash 社 (Telescope Technology Limited.) とライセンス契約を結ぶことで利用可能な形態です。インフラの構築・保守なく、簡単に利用を開始できます。

利用プランとしては Cloud Starter / Cloud Pro / Enterprise の 3 つが存在します。プランごとに利用可能な機能に違いがあり、前述の AI 機能については Cloud Pro 以上が必要です。


Lightdash Cloud Plan & Pricing - 公式 Docs

ikki さんの記事 にも言及がある通り、Lightdash Cloud は多くの BI 製品が採用しているような「ユーザーアカウント数課金」ではなく「月額固定料金」であるため、ユーザー数が増えるほどコストメリットが大きくなります。

規模感の小さいスタートアップにとっては、基本的な機能の使用感を掴むためにまずは OSS 版から始めてみて、利用規模が拡大してきたら Cloud 版への移行を検討するというのは、導入の道筋として良いかもしれません。

OSS 版のデプロイ方式

OSS 版の場合、基本的には Kubernetes + Helm によるデプロイ方式が推奨されているようです。
ただし、非 Kubernetes 環境での選択肢として Docker ComposeRestack でのデプロイ方法についても言及されています。

docker-compose.yml を見てみると、Lightdash サーバー以外は MinIO (S3), PostgreSQL DB, Headless Browser があるだけなので、Lightdash の Docker イメージを使用すれば AWS 上で ECS サービスとしても構築できそうです。

https://github.com/lightdash/lightdash/blob/main/docker-compose.yml

アーキテクチャ

以上を踏まえて、今回以下のようなアーキテクチャで構築してみました。
(想定する URL は https://lightdash.example.com とします)

Architecture

概要

Docker Hub 上に Lightdash の Docker イメージ lightdash/lightdash があるので、これを元にプライベートサブネット上の ECS サービスとして稼働させます。Lightdash が使用する DB として PostgreSQL が必要なので、今回は Aurora PostgreSQL 17.4 の DB クラスターを構築します。

ECS サービスコンテナをターゲットグループに登録する Application Load Balancer (ALB) を、パブリックサブネット上に構築します。ユーザーからのリクエストは ALB をオリジンとする CloudFront ディストリビューションを経由させるようにしています。

SSO 認証

ECS サービスコンテナの環境変数を定義しておくことで、SSO 認証を実装できます。[1]
認証プロバイダーとしては ( Okta / Google / OneLogin / Microsoft Entra ID / OpenID Connect ) あたりが利用できそうなので、今回は Google OAuth 2.0 を利用したいと思います。(詳細は後述)

実行結果保存用 S3 バケット

Lightdash では、Slack 投稿のプレビュー画像や実行結果(JSONL 形式など)などのファイルの保存先として、S3 互換のオブジェクトストレージを必要とします。[2] ローカル用に MinIO なども利用可能ですが、本記事ではクラウド上にデプロイするので S3 を利用します。

また、Lightdash が稼働する ECS サービスのタスクロールに対して、S3 バケットへの PutObject 権限を付与する必要があります。

その他

その他、Slack・GitHub との統合や E メール通知用の SMTP 設定なども出来そうですが、本記事では最小構成ということで割愛したいと思います。

構築手順

例えば以下のような順序で構築を進めると良さそうです。
(Route 53 ホストゾーンの作成や ACM 証明書の作成・検証などに関しては割愛しています)

  • 【手順1】 Application Load Balancer (ALB) を作成する
  • 【手順2】 ALB をオリジンとする CloudFront ディストリビューションを作成し、ディストリビューションドメイン名を lightdash.example.com のエイリアス A レコードとする
  • 【手順3】実行結果保存用 S3 バケットを作成する
  • 【手順4】Lightdash 用 Aurora PostgreSQL データベースを作成する
  • 【手順5】Google OAuth 2.0 クライアントを作成する
  • 【手順6】機微情報格納用の Secrets Manager シークレットを作成する
  • 【手順7】ALB ターゲットグループに登録されるように設定した ECS サービスを作成する

構築時のポイント

各手順の詳細については割愛しますが、構築時のポイントになりそうな点について、以降でいくつか言及しておきたいと思います。

【手順4】Lightdash 用 DB の作成

拡張機能のインストール

Lightdash では PostgreSQL データベースの拡張機能として、uuid-ossp を必要とします。[3]
これは DB 内で UUID を生成するためのものです。

Aurora PostgreSQL データベース作成後、DB 接続して拡張機能一覧を表示すると初期状態で以下のようになっており、UUID を生成しようとしてもエラーになります。

-- 拡張機能一覧の表示
lightdash=> \dx;
                 List of installed extensions
  Name   | Version |   Schema   |         Description
---------+---------+------------+------------------------------
 plpgsql | 1.0     | pg_catalog | PL/pgSQL procedural language
(1 row)

-- UUID の生成(失敗)
lightdash=> SELECT uuid_generate_v4();
ERROR:  function uuid_generate_v4() does not exist
LINE 1: SELECT uuid_generate_v4();
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

以下のように uuid-ossp をインストールすると、UUID を生成できるようになります。

-- 拡張機能のインストール
lightdash=> CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION

-- 拡張機能一覧の表示
lightdash=> \dx;
                            List of installed extensions
   Name    | Version |   Schema   |                   Description
-----------+---------+------------+-------------------------------------------------
 plpgsql   | 1.0     | pg_catalog | PL/pgSQL procedural language
 uuid-ossp | 1.1     | public     | generate universally unique identifiers (UUIDs)

-- UUID の生成(成功)
lightdash=> SELECT uuid_generate_v4();
           uuid_generate_v4
--------------------------------------
 6996c424-6bb5-4382-918a-89632cbb5f26
(1 row)

【手順5】Google OAuth クライアントの作成

自社ドメインのメールアドレスを持ち、かつ管理者が招待したメンバーのみが SSO ログイン可能という要件で実装してみたいと思います。

  • Google Cloud コンソール にログインし、プロジェクトを選択または新規に作成します。
  • 検索から「OAuth 同意画面」を選択し、Google Auth Platform を開きます。
  • 左メニューの [ クライアント ] より、「クライアントを作成」を押下します。
  • 以下の通り入力し、「作成」を押下します。
    設定名
    アプリケーションの種類 ウェブアプリケーション
    名前 Lightdash
    承認済みの JavaScript 生成元 https://lightdash.example.com
    承認済みのリダイレクト URI https://lightdash.example.com/api/v1/oauth/redirect/
    google
  • 「OAuth クライアントを作成しました」の画面に表示される "クライアントID" および、"クライアントシークレット" の値を控える、または JSON をダウンロードします。

【手順6】Secrets Manager シークレットの作成

AWS Secrets Manager を使用して、シークレットを作成します。
Aurora PostgreSQL データベース認証情報、Lightdash Secret、控えておいた Google OAuth クライアント ID・シークレットなどを管理します。

  • PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE
  • LIGHTDASH_SECRET
  • AUTH_GOOGLE_OAUTH2_CLIENT_ID, AUTH_GOOGLE_OAUTH2_CLIENT_SECRET

各環境変数の詳細はこちらで確認できます。

【手順7】ECS サービスの作成

ECS タスク定義の環境変数

Secrets Manager シークレット経由で設定するものに加えて、以下あたりの環境変数もタスク定義内で定義しておきます。

環境変数名 値の例 説明
LIGHTDASH_LOG_LEVEL info ログレベルを指定
SITE_URL https://lightdash.example.com
S3_ENDPOINT https://s3.ap-northeast-1.
amazonaws.com
S3_BUCKET Lightdash 用 S3 バケット名 ※東京リージョンの場合
S3_REGION ap-northeast-1 ※東京リージョンの場合
LD_SETUP_ADMIN_EMAIL first.last@example.com 管理者Eメールアドレス
LD_SETUP_ORGANIZATION_
EMAIL_DOMAIN
example.com 自社Eメールドメイン
LD_SETUP_ORGANIZATION_
NAME
example-corp 組織名
AUTH_DISABLE_PASSWORD_
AUTHENTICATION
true パスワード認証を無効化
AUTH_GOOGLE_ENABLED true SSO 認証に Google OAuth を
利用

ECS タスク定義・ECS サービス

ECS タスク定義の Terraform 実装は例えば以下のようになります。

Terraform 実装例 - locals ブロック
local.tf
locals {
  domain_name = var.env == "prod" ? "example.com" : "${var.env}.example.com"

  # コンテナ定義
  container_images = {
    lightdash = "lightdash/lightdash:0.1740.2"
  }
  containers = [
    {
      name  = "server"
      image = local.container_images.lightdash
      portMappings = [
        { containerPort = 8080, hostPort = 8080, protocol = "tcp" },
      ]
    },
    {
      name  = "headless-browser"
      image = "ghcr.io/browserless/chromium:v2.24.3"
      portMappings = [
        { containerPort = 3000, hostPort = 3000, protocol = "tcp" },
      ]
    },
  ]
  # 環境変数
  envvars = {
    LIGHTDASH_LOG_LEVEL = "info"
    SITE_URL            = "https://lightdash.${local.domain_name}"
    # S3 Configuration
    S3_ENDPOINT = "https://s3.ap-northeast-1.amazonaws.com"
    S3_BUCKET   = data.aws_s3_bucket.lightdash.id
    S3_REGION   = "ap-northeast-1"
    # Lighdash Setup
    LD_SETUP_ORGANIZATION_EMAIL_DOMAIN = "example.com"
    LD_SETUP_ORGANIZATION_NAME         = "example-corp"
    # Authentication
    AUTH_DISABLE_PASSWORD_AUTHENTICATION = "true"
    AUTH_GOOGLE_ENABLED                  = "true"
  }
  environment_variables = [
    for name, value in local.envvars : {
      name  = name
      value = value
    }
  ]
  # Secrets Manager から取得するシークレット情報
  secret_items = [
    "PGHOST",
    "PGPORT",
    "PGUSER",
    "PGPASSWORD",
    "PGDATABASE",
    "LIGHTDASH_SECRET",
    "AUTH_GOOGLE_OAUTH2_CLIENT_ID",
    "AUTH_GOOGLE_OAUTH2_CLIENT_SECRET",
  ]
  secrets = [
    for secret_item in local.secret_items : {
      name      = secret_item
      valueFrom = "${data.aws_secretsmanager_secret.lightdash.arn}:${secret_item}::"
    }
  ]
}

Terraform 実装例 - ECS タスク定義
main.tf | ECS Task Definition
resource "aws_ecs_task_definition" "default" {
  family = local.service_name

  cpu    = var.task_definition_config.cpu
  memory = var.task_definition_config.memory

  task_role_arn      = aws_iam_role.task.arn
  execution_role_arn = data.aws_iam_role.task_execution.arn

  # Infrastructure
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]

  runtime_platform {
    operating_system_family = "LINUX"
  }

  container_definitions = jsonencode([
    for container in local.containers : {
      name         = container.name
      image        = container.image
      cpu          = 0
      essential    = true
      portMappings = try(container.portMappings, [])
      command      = try(container.command, null)
      secrets      = local.secrets
      environment = concat(
        local.environment_variables,
        [
          for name, value in try(container.custom_envvars, {}) : {
            name  = name
            value = value
          }
        ]
      )
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "${aws_cloudwatch_log_group.default.name}"
          awslogs-region        = var.region
          awslogs-stream-prefix = container.name
        }
      }
    }
  ])
}
Terraform 実装例 - ECS サービス
main.tf | ECS Service
resource "aws_ecs_service" "default" {
  name    = local.service_name
  cluster = aws_ecs_cluster.default.arn

  desired_count   = var.service_config.desired_count
  task_definition = aws_ecs_task_definition.default.arn

  launch_type      = "FARGATE"
  platform_version = "1.4.0"
  propagate_tags   = "SERVICE"

  enable_ecs_managed_tags = true

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }

  load_balancer {
    container_name   = "server"
    container_port   = 8080
    target_group_arn = var.load_balancer.target_group_arn
  }

  network_configuration {
    security_groups  = [aws_security_group.default.id]
    subnets          = var.network.private_subnets
    assign_public_ip = false
  }

  tags = {
    Name = local.service_name
  }
}

ECS タスクコンテナが起動すると、初回起動時に DB マイグレーションが実行されます。
(もしかしたら ECS サービスの ALB ターゲットグループ関連付けは、DB マイグレーション実行後に追加する必要があるかもしれません)

初回セットアップ

管理者初回ログイン

ブラウザから https://lightdash.example.com にアクセスし、"Sign up with Google" を押下して SSO 認証します。

遷移先の初期設定画面にて、組織名とロール、各種オプションを設定します。

組織内ユーザーによるセルフサインアップを許可する場合は Allow users with @example.com to join the organization as a viewer にチェックを入れますが、ここでは一旦外しておきます。

"Next" を押下するとプロジェクト作成画面に遷移し、Lightdash の利用を開始できます。
(プロジェクト作成の手順については後述します)

ユーザーの招待

セルフサインアップを許可しなかった場合には、自社Eメールドメインユーザーであってもログインはできません。

ログインを許可する場合は、管理者からユーザーを招待します。右上プロフィールアイコンの ① "User settings" から、メニューの ② "User management" に遷移すると、新規ユーザーの招待やロール変更、ユーザー削除などができます。

[ + Add user ] を押下し、招待したいユーザーのメールアドレスとロールを設定して "Generate invite" を押下すると、招待用 URL が生成されます。招待されたユーザーが招待用 URL にアクセスすると、ログインできるようになります。

プロジェクトデプロイ

既にデプロイ可能な dbt プロジェクト (Snowflake) がある前提で、最初のプロジェクトのデプロイ方法について紹介します。

プロジェクト作成画面で、"Snowflake" を選択し、遷移先画面で "Using your CLI" を押下します。

実行すべきコマンドが表示されるので、dbt_project.yml が存在するディレクトリにて、指示の通り実行してみます。

# Lightdash CLI のインストール
% npm install -g @lightdash/cli@0.1740.2

# Lightdash へのログイン
% lightdash login https://lightdash.example.com --token ldpat_xxxxxxxx

  ✅️ Login successful

Now you can add your first project to lightdash by doing: 

  ⚡️ lightdash deploy --create

Done 🕶

# プロジェクトのデプロイ
% lightdash deploy --create

- SUCCESS> my_first_model

Compiled 1 explores, SUCCESS=1 ERRORS=0

? Add a project name or press enter to use the default: [MyProjectName]  MyProjectName

? Do you confirm Lightdash can store your warehouse credentials so you can run queries in Lightdash? Yes
? Do you want to save this answer for next time? Yes
✔   New project MyProjectName created

Successfully deployed project:

      ⚡️ https://lightdash.example.com/createProject/cli?projectUuid=393e322a-d631-4315-9fbf-bef0d1c50620

Done 🕶

Lightdash の画面に戻ると、無事分析が始められる状態になっていることを確認できました 🎉

さいごに

OSS 版の Lightdash を AWS 上に構築する方法について書いてみました。

今回は dimension や metrics を定義するところまで紹介できませんでしたが、以下あたりのドキュメントを参考に定義できます。

筆者自身はまだ試せていませんが、他にも dbt Write-BackDashboards as code など気になる機能がありそうなので、今回構築した環境を使って色々と試していきたいと思います。

最後まで読んでいただき、ありがとうございました。

脚注
  1. Configure Lightdash to use passwords or SSO for authentication - Lightdash Documentation ↩︎

  2. Configure Lightdash to use external storage - Lightdash Documentation ↩︎

  3. Configure Lightdash to use an external database - Lightdash Documentation ↩︎

SimpleForm Tech Blog

Discussion