LivekitをGKEで動かす

Livekit
GKE
terraform
helm

2024-09-21

#目次

#はじめに

今回は、LivekitをGKEで動かす方法について紹介します。

#Livekitとは

LiveKit is an open source project that provides scalable, multi-user conferencing based on WebRTC. It's designed to provide everything you need to build real-time video audio data capabilities in your applications.

出典:Livekit

Livekitは、WebRTCをベースにしたスケーラブルなマルチユーザー会議を提供するオープンソースプロジェクトです。リアルタイムのビデオオーディオデータ機能をアプリケーションに組み込むために必要なすべてを提供するように設計されています。
Google ChatやZoomのようなビデオ会議アプリケーションを作成するための基盤として利用できます。

各種言語に対応したSDKも豊富に提供されています。

  • Web
    • React Components
    • JavaScript
    • Unity (WebGL)
  • Native
    • Swift Components
    • Swift
    • Android Components
    • Android
    • Flutter
    • React Native
    • Rust
    • Unity
  • Server
    • Go
    • Node
    • Ruby
    • Kotlin/Java
    • Python
    • PHP

この他にも、動画を入力にしてWebRTCで配信したり、配信されている動画や音声を録画、録音する機能も提供されています。

昨今では、ChatGPTでLivekitが利用されているということで、注目されています。

#システム構成

デモ用に構築するので、シンプルな構成で構築します。

システム構成図
システム構成図

  • GKE
    • コストを抑えるために、1ノードのみのクラスタを作成します。
    • preenptibleノードを利用します。
  • cloudflare tunnel
    • GKE上にデプロイします。
  • Redis
    • GKE上にデプロイします。
    • standaloneモードでデプロイします。
  • Livekit Server
    • GKE上にデプロイします。
  • STUN/TURN
    • Livekit Serverのデフォルトでは、GoogleのSTUNサーバを利用します。
    • ※GoogleのSTUNサーバは、開発用途のみで利用することとされているので、本来は別途立てたほうが良いですが、今回はデモが終わったら破棄するので、利用します。
  • Livekit WebApp
    • この記事ではデプロイせず、Livekitのサンプルアプリをローカルで動かしてGKE上のLivekit Server経由でビデオチャットを行います。

#準備

今回のソースコードは、こちらにあります。

https://github.com/cloudandbuild/livekit-gke

必要なツールのリストを記載します。

  • .tool-versions
helm      3.14
helmfile  0.164.0
sops      3.8.1
age       1.1.1
kubectl   1.28
kubectx   0.9
k9s       0.27
jq        1.7.1
terraform 1.7
direnv    2.34.0
gcloud    493.0.0

miseを使って、必要なツールをインストールします。

mise install

前提として、cloudflareとgoogle cloudのアカウントを持っていることです。

#ネットワークとファイアウォールの設定

#Cloudflare Tunnel

CloudflareのコンソールからZero Trustを選択し、TunnelsからCreate Tunnelを選択。
作成したTunnelのTokenを取得しておきます。

Public Routeには、適当なドメインとそれに紐づくGKE側のサービスを指定します。

例えば以下のように。

  • Livekit APIドメイン(livekit-api.example.com): http://livekit-server.livekit.svc.cluster.local
  • Livekit WebAppドメイン(livekit-webapp.example.com): http://livekit-webapp.livekit.svc.cluster.local

#Google Cloud

事前にGoogle Cloudのプロジェクトを作成してあることが前提です。

gcloudで対象プロジェクトを設定します。

gcloud config set project  [PROJECT_ID]

VPCを事前に作成する必要があります。defaultを使う場合は不要ですが、public_subnetを作成する必要があります。また、GKEのpodsのIPアドレス範囲を指定できるようsecondary rangeを設定しておきます。

また、LivekitのFirewallのドキュメントを参考に、以下のファイアウォールルールを設定します。

resource "google_compute_firewall" "allow-livekit" {
  name    = "${var.env}-allow-livekit"
  network = var.vpc_name

  allow {
    protocol = "tcp"
    ports    = ["7881"]
  }
  allow {
    protocol = "udp"
    ports    = ["50000-60000"]
  }

  source_ranges = ["0.0.0.0/0"]
}

terraformを適用すると、GKEのクラスタが作成されます。

cd terraform/demo/base
terraform init
terraform apply

#GKEのクラスタを作成

ポイントは、public subnetを指定することです。Livekitは、Hostネットワークを利用して直接インターネットに接続するためです。商用にデプロイを検討するとpublic subnetのnode poolとprivateのnode poolを両方作っておき、publicに置く必要のあるワークロードだけを配置することが、セキュリティ上、望ましいです。

locationには、リージョンではなくゾーンまで指定するとZonalクラスターが作成されます。冗長性などを考慮する場合はリージョナルクラスターを作成すると良いでしょう。

以下は、terraformでGKEのクラスタを作成する例です。

  • variables.tf
variable "project" {
  description = "The project ID for the network"
  type        = string
}
variable "env" {
}
variable "location" {
  description = "The location for the network"
  type        = string
}
variable "network" {
  description = "The network name"
  type        = string
}
variable "public_subnet" {
  description = "The public subnet name"
  type        = string
}
variable "cluster_secondary_range_name" {
  description = "The name of the secondary range for the cluster"
  type        = string
}
  • main.tf
resource "google_service_account" "default" {
  account_id   = "service-account-id"
  display_name = "Service Account"
}

resource "google_container_cluster" "primary" {
  name     = "${var.env}-livekit-gke-cluster"
  location = var.location

  network    = var.network
  subnetwork = var.public_subnet
  ip_allocation_policy {
    cluster_secondary_range_name = var.cluster_secondary_range_name
  }
  deletion_protection = false
  # We can't create a cluster with no node pool defined, but we want to only use
  # separately managed node pools. So we create the smallest possible default
  # node pool and immediately delete it.
  remove_default_node_pool = true
  initial_node_count       = 1
}

resource "google_container_node_pool" "primary_nodes" {
  name       = "${var.env}-node-pool"
  location   = var.location
  cluster    = google_container_cluster.primary.name
  node_count = 1

  node_config {
    machine_type = "e2-medium"
    preemptible  = true

    # Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.
    service_account = google_service_account.default.email
    oauth_scopes = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]
  }
}

terraformを適用すると、GKEのクラスタが作成されます。

cd terraform/demo/infra
terraform init
terraform apply

kubectlを使えるよう、credentialを設定します。

gcloud container clusters get-credentials demo-livekit-gke-cluster --zone asia-northeast1-c --project [PROJECT_ID]

#Helm Chartで各種コンポーネントのデプロイ

ここまでで、GKEのクラスタが作成されました。
ここからは、Helm Chartを使って各種コンポーネントをデプロイします。

helmでは、環境変数で値を渡して設定を行います。
.envrcに以下のように設定します。

export HF_CF_TUNNEL_TOKEN=<cloudflare tunnel token>
export HF_REDIS_PASSWORD=<redis password>
export HF_LK_API_KEY=<your apikey>
export HF_LK_API_SECRET=<your api secret>

direnvを使って、環境変数を読み込みます。

direnv allow

helmfileを使って、デプロイします。

helmfile -e demo apply

#Redis

以下、valuesの例です。

  • values.yaml
global:
  redis:
    password: {{ requiredEnv "HF_REDIS_PASSWORD" | quote }}
architecture: standalone

#Livekit Server

  • values.yaml
replicaCount: 1

# Suggested value for gracefully terminate the pod: 5 hours
terminationGracePeriodSeconds: 18000

livekit:
  # port: 7880
  # Uncomment to enable prometheus metrics
  # prometheus_port: 6789
  log_level: info
  rtc:
    use_external_ip: true
    # default ports used
    port_range_start: 50000
    port_range_end: 60000
    tcp_port: 7881
  redis:
    address: redis-headless.redis.svc.cluster.local:6379
    # db: 0
    # username:
    password: {{ requiredEnv "HF_REDIS_PASSWORD" | quote }}
    # use_tls: true
  # one or more API key/secret pairs
  # see https://docs.livekit.io/guides/getting-started/#generate-api-key-and-secret
  keys:
    {{ requiredEnv "HF_LK_API_KEY" }}: {{ requiredEnv "HF_LK_API_SECRET" | quote }}

注意点

Livekitのhelm chartでは、secretを使っていないので、ConfigMapに直接設定しています。
そのため、Redis のパスワードやAPIキーなどが平文でConfigMap設定されてしまいます。

本番環境でデプロイする場合は、Livekitの設定ファイルをシークレットで管理するよう別途helm chartを作成するのがよいでしょう。

#Cloudflare Tunnel

フロントエンドからLivekitにアクセスするため、Livekit APIを公開する必要があります。
Google CloudのLoadBalancerとNEGを使っても良いですが、最近は使い勝手のよいcloudflare tunnelを使ってTLS終端をすることが多いです。DNSの伝搬もすぐに反映されるのが、気に入っています。
valuesは以下のように設定します。Tokenのみ設定するだけで動くので、簡単です。

cloudflare:
  tunnel_token: {{ requiredEnv "HF_CF_TUNNEL_TOKEN" | quote }}

#Livekitのサンプルアプリを動かす

LivekitはSDKとそのExampleが豊富に提供されています。
ここでは、Next.jsを使ったサンプルアプリを動かします。

git clone https://github.com/livekit/components-js.git

cd components-js
pnpm install

examples/nextjs/.env.localファイルを作成して、以下のように設定します。

  • examples/nextjs/.env.local
LK_API_KEY=<your apikey>
LK_API_SECRET=<your api secret>

NEXT_PUBLIC_LK_TOKEN_ENDPOINT=/api/livekit/token
NEXT_PUBLIC_LK_SERVER_URL=wss://livekit-server.example.com

サンプルアプリを起動します。

pnpm run dev:next

疎通確認を行います。
これは、ブラウザでhttp://localhost:3000にアクセスして、ビデオチャットができることを確認します。スクリーンの共有もできます。
ビデオチャット
ローカルで疎通確認しましたが、exampleのアプリは、Next.jsで構成されているので、これのNode.jsのDockerイメージを作って、GKEにデプロイして、cloudflare tunnelのRoute設定をしてあげるとインターネット上からアクセスできるようになります。
サンプルアプリをGKEにデプロイする部分は、この記事では割愛します。

#参考リンク

#まとめ

今回は、LivekitをGKEで動かす方法について紹介しました。
商用で動かすには、セキュリティや冗長性などを考慮する必要がありますが、デモやプロトタイプを作るには、このような構成で十分です。
また、STUN/TURNサーバなども別途立てる必要がありますが、今回はGoogleのSTUNサーバを利用しました。
Livekitのセルフホストは、結構難易度が高いので、LiveKit CloudというSaaSを利用するのも一つの手です。
ChatGPTのようなマルチモーダルなアプリケーションで音声による会話や、カメラによる画像の入力を行う場合には、Livekitは非常に便利ですから、今後も注目していきたいです。