mirror of https://github.com/OpenVidu/openvidu.git
Add Terraform configuration for OpenVidu deployment on GCP
parent
8a236e1b67
commit
4b37bf4304
|
@ -0,0 +1,467 @@
|
|||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = ">= 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
project = var.projectId
|
||||
region = var.region
|
||||
zone = var.zone
|
||||
}
|
||||
|
||||
# Enable APIs the deployment needs
|
||||
resource "google_project_service" "compute_api" { service = "compute.googleapis.com" }
|
||||
resource "google_project_service" "secretmanager_api" { service = "secretmanager.googleapis.com" }
|
||||
resource "google_project_service" "storage_api" { service = "storage.googleapis.com" }
|
||||
|
||||
resource "random_id" "bucket_suffix" { byte_length = 3 }
|
||||
|
||||
# GCS bucket (conditional)
|
||||
resource "google_storage_bucket" "bucket" {
|
||||
count = 1
|
||||
name = local.isEmpty ? "openvidu-appdata" : var.bucketName
|
||||
location = var.region
|
||||
force_destroy = false
|
||||
uniform_bucket_level_access = true
|
||||
}
|
||||
|
||||
# Secret Manager secret that stores deployment info and seed secrets
|
||||
resource "google_secret_manager_secret" "openvidu" {
|
||||
secret_id = "openvidu-${var.region}-${var.stackName}"
|
||||
replication {
|
||||
auto {}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_version" "openvidu_version" {
|
||||
secret = google_secret_manager_secret.openvidu.id
|
||||
secret_data = jsonencode({
|
||||
domainName = "none",
|
||||
LIVEKIT_turnDomainName = "none",
|
||||
LETSENCRYPT_EMAIL = "none",
|
||||
REDIS_PASSWORD = "none",
|
||||
MONGO_ADMIN_USERNAME = "none",
|
||||
MONGO_ADMIN_PASSWORD = "none",
|
||||
MONGO_REPLICA_SET_KEY = "none",
|
||||
MINIO_URL = "none",
|
||||
MINIO_ACCESS_KEY = "none",
|
||||
MINIO_SECRET_KEY = "none",
|
||||
DASHBOARD_URL = "none",
|
||||
DASHBOARD_ADMIN_USERNAME = "none",
|
||||
DASHBOARD_ADMIN_PASSWORD = "none",
|
||||
GRAFANA_URL = "none",
|
||||
GRAFANA_ADMIN_USERNAME = "none",
|
||||
GRAFANA_ADMIN_PASSWORD = "none",
|
||||
LIVEKIT_API_KEY = "none",
|
||||
LIVEKIT_API_SECRET = "none",
|
||||
MEET_ADMIN_USER = "none",
|
||||
MEET_ADMIN_SECRET = "none",
|
||||
MEET_API_KEY = "none",
|
||||
ENABLED_MODULES = "none"
|
||||
})
|
||||
}
|
||||
|
||||
# Service account for the instance
|
||||
resource "google_service_account" "openvidu_sa" {
|
||||
account_id = "openvidu-sa-${substr(var.stackName, 0, 12)}"
|
||||
display_name = "OpenVidu instance service account"
|
||||
}
|
||||
|
||||
# IAM bindings for the service account so the instance can access Secret Manager and GCS
|
||||
resource "google_project_iam_member" "sa_secret_accessor" {
|
||||
project = var.projectId
|
||||
role = "roles/secretmanager.secretAccessor"
|
||||
member = "serviceAccount:${google_service_account.openvidu_sa.email}"
|
||||
}
|
||||
|
||||
resource "google_project_iam_member" "sa_storage_object_admin" {
|
||||
project = var.projectId
|
||||
role = "roles/storage.objectAdmin"
|
||||
member = "serviceAccount:${google_service_account.openvidu_sa.email}"
|
||||
}
|
||||
|
||||
# Firewall (similar ports to your AWS SG)
|
||||
resource "google_compute_firewall" "openvidu_fw" {
|
||||
name = "openvidu-fw-${var.stackName}"
|
||||
network = "default"
|
||||
|
||||
allow {
|
||||
protocol = "tcp"
|
||||
ports = ["22", "80", "443", "1935", "7881"]
|
||||
}
|
||||
allow {
|
||||
protocol = "udp"
|
||||
ports = ["443", "7885", "50000-60000"]
|
||||
}
|
||||
|
||||
source_ranges = ["0.0.0.0/0"]
|
||||
target_tags = ["openvidu-server-${var.stackName}"]
|
||||
}
|
||||
|
||||
# Static external IP (optional)
|
||||
resource "google_compute_address" "openvidu_ip" {
|
||||
count = var.publicStaticIp == "" ? 0 : 1
|
||||
name = "openvidu-eip-${var.stackName}"
|
||||
address = var.publicStaticIp
|
||||
}
|
||||
|
||||
# Compute instance for OpenVidu
|
||||
resource "google_compute_instance" "openvidu" {
|
||||
name = "openvidu-${var.stackName}"
|
||||
machine_type = var.instanceType
|
||||
zone = var.zone
|
||||
|
||||
tags = ["openvidu-server-${var.stackName}"]
|
||||
|
||||
boot_disk {
|
||||
initialize_params {
|
||||
image = var.boot_image
|
||||
size = 200
|
||||
type = "pd-standard"
|
||||
}
|
||||
}
|
||||
|
||||
network_interface {
|
||||
network = "default"
|
||||
access_config {
|
||||
nat_ip = length(google_compute_address.openvidu_ip) > 0 ? google_compute_address.openvidu_ip[0].address : null
|
||||
}
|
||||
}
|
||||
|
||||
metadata = {
|
||||
# metadata values are accessible from the instance
|
||||
secret_name = google_secret_manager_secret.openvidu.secret_id
|
||||
region = var.region
|
||||
stackName = var.stackName
|
||||
certificateType = var.certificateType
|
||||
domainName = var.domainName
|
||||
letsEncryptEmail = var.letsEncryptEmail
|
||||
ownPublicCertificate = var.ownPublicCertificate
|
||||
ownPrivateCertificate = var.ownPrivateCertificate
|
||||
additional_install_flags = var.additional_install_flags
|
||||
turnDomainName = var.turnDomainName
|
||||
turnOwnPublicCertificate = var.turnOwnPublicCertificate
|
||||
turnOwnPrivateCertificate = var.turnOwnPrivateCertificate
|
||||
s3_bucket_name = var.bucketName == "" ? google_storage_bucket.appdata[0].name : var.bucketName
|
||||
}
|
||||
|
||||
service_account {
|
||||
email = google_service_account.openvidu_sa.email
|
||||
scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
}
|
||||
|
||||
|
||||
metadata_startup_script = <<EOF
|
||||
#!/bin/bash -x
|
||||
set -euo pipefail
|
||||
|
||||
# Metadata helper
|
||||
METADATA_URL="http://metadata.google.internal/computeMetadata/v1"
|
||||
get_meta() { curl -s -H "Metadata-Flavor: Google" "$${METADATA_URL}/$1"; }
|
||||
|
||||
projevar.projectId=$(get_meta "project/project-id")
|
||||
REGION=$(get_meta "instance/attributes/region")
|
||||
stackName=$(get_meta "instance/attributes/stackName")
|
||||
SECRET_NAME=$(get_meta "instance/attributes/secret_name")
|
||||
CERT_TYPE=$(get_meta "instance/attributes/certificateType")
|
||||
domainName=$(get_meta "instance/attributes/domainName")
|
||||
LE_EMAIL=$(get_meta "instance/attributes/letsEncryptEmail")
|
||||
ADDITIONAL_FLAGS=$(get_meta "instance/attributes/additional_install_flags")
|
||||
turnDomainName=$(get_meta "instance/attributes/turnDomainName")
|
||||
OWN_CERT_URL=$(get_meta "instance/attributes/ownPublicCertificate")
|
||||
OWN_KEY_URL=$(get_meta "instance/attributes/ownPrivateCertificate")
|
||||
S3_BUCKET_NAME=$(get_meta "instance/attributes/s3_bucket_name")
|
||||
|
||||
# Install deps
|
||||
apt-get update
|
||||
apt-get install -y curl unzip jq wget ca-certificates gnupg lsb-release openssl
|
||||
|
||||
# Install google-cloud-sdk (to read secrets)
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
|
||||
apt-get update && apt-get install -y google-cloud-sdk
|
||||
fi
|
||||
|
||||
# Install yq
|
||||
YQ_VERSION=v4.44.5
|
||||
wget https://github.com/mikefarah/yq/releases/download/$${YQ_VERSION}/yq_linux_amd64.tar.gz -O - | tar xz && mv yq_linux_amd64 /usr/bin/yq
|
||||
|
||||
# Fetch secret (the secret contains a JSON string as in Terraform)
|
||||
SHARED_SECRET_JSON=$(gcloud secrets versions access latest --secret="$${SECRET_NAME}" --project="$${projevar.projectId}") || SHARED_SECRET_JSON='{}'
|
||||
|
||||
# Helper to update secret using gcloud (we will use it to save values)
|
||||
save_secret() {
|
||||
KEY=$1
|
||||
VALUE=$2
|
||||
# read current, update key, and write a new version
|
||||
TMP=$(mktemp)
|
||||
echo "$SHARED_SECRET_JSON" | jq ". + { \"$${KEY}\": \"$${VALUE}\" }" > "$TMP" || echo '{ }' > "$TMP"
|
||||
gcloud secrets versions add "$${SECRET_NAME}" --data-file="$TMP" --project="$${projevar.projectId}" >/dev/null
|
||||
SHARED_SECRET_JSON=$(cat "$TMP")
|
||||
rm -f "$TMP"
|
||||
}
|
||||
|
||||
# Generate randoms and save to secret when needed (similar to CFN store_secret.sh)
|
||||
generate_and_save() {
|
||||
KEY=$1
|
||||
PREFIX=$${2:-}
|
||||
LENGTH=$${3:-44}
|
||||
RAND=$(openssl rand -base64 64 | tr -d '+/=\n' | cut -c -$${LENGTH})
|
||||
VALUE="$${PREFIX}$${RAND}"
|
||||
save_secret "$KEY" "$VALUE"
|
||||
echo "$VALUE"
|
||||
}
|
||||
|
||||
# Configure domain
|
||||
if [[ -z "$domainName" || "$domainName" == "none" ]]; then
|
||||
# Use external IP
|
||||
EXTERNAL_IP=$(curl -s ifconfig.co || true)
|
||||
DOMAIN="$EXTERNAL_IP"
|
||||
else
|
||||
DOMAIN="$domainName"
|
||||
fi
|
||||
save_secret domainName "$DOMAIN"
|
||||
|
||||
# Generate/store secrets used by OpenVidu
|
||||
REDIS_PASSWORD=$(generate_and_save REDIS_PASSWORD)
|
||||
MONGO_ADMIN_USERNAME=$(save_secret MONGO_ADMIN_USERNAME "mongoadmin")
|
||||
MONGO_ADMIN_PASSWORD=$(generate_and_save MONGO_ADMIN_PASSWORD)
|
||||
MONGO_REPLICA_SET_KEY=$(generate_and_save MONGO_REPLICA_SET_KEY)
|
||||
MINIO_ACCESS_KEY=$(save_secret MINIO_ACCESS_KEY "minioadmin")
|
||||
MINIO_SECRET_KEY=$(generate_and_save MINIO_SECRET_KEY)
|
||||
DASHBOARD_ADMIN_USERNAME=$(save_secret DASHBOARD_ADMIN_USERNAME "dashboardadmin")
|
||||
DASHBOARD_ADMIN_PASSWORD=$(generate_and_save DASHBOARD_ADMIN_PASSWORD)
|
||||
GRAFANA_ADMIN_USERNAME=$(save_secret GRAFANA_ADMIN_USERNAME "grafanaadmin")
|
||||
GRAFANA_ADMIN_PASSWORD=$(generate_and_save GRAFANA_ADMIN_PASSWORD)
|
||||
MEET_ADMIN_USER=$(save_secret MEET_ADMIN_USER "meetadmin")
|
||||
MEET_ADMIN_SECRET=$(generate_and_save MEET_ADMIN_SECRET)
|
||||
MEET_API_KEY=$(generate_and_save MEET_API_KEY)
|
||||
ENABLED_MODULES=$(save_secret ENABLED_MODULES "observability,openviduMeet")
|
||||
LIVEKIT_API_KEY=$(generate_and_save LIVEKIT_API_KEY "API" 12)
|
||||
LIVEKIT_API_SECRET=$(generate_and_save LIVEKIT_API_SECRET)
|
||||
|
||||
# Build install command and args
|
||||
INSTALL_COMMAND="sh <(curl -fsSL http://get.openvidu.io/community/singlenode/main/install.sh)"
|
||||
COMMON_ARGS=(
|
||||
"--no-tty"
|
||||
"--install"
|
||||
"--environment=gcp"
|
||||
"--deployment-type=single_node"
|
||||
"--domain-name=$DOMAIN"
|
||||
"--enabled-modules='$ENABLED_MODULES'"
|
||||
"--redis-password=$REDIS_PASSWORD"
|
||||
"--mongo-admin-user=$MONGO_ADMIN_USERNAME"
|
||||
"--mongo-admin-password=$MONGO_ADMIN_PASSWORD"
|
||||
"--mongo-replica-set-key=$MONGO_REPLICA_SET_KEY"
|
||||
"--minio-access-key=$MINIO_ACCESS_KEY"
|
||||
"--minio-secret-key=$MINIO_SECRET_KEY"
|
||||
"--dashboard-admin-user=$DASHBOARD_ADMIN_USERNAME"
|
||||
"--dashboard-admin-password=$DASHBOARD_ADMIN_PASSWORD"
|
||||
"--grafana-admin-user=$GRAFANA_ADMIN_USERNAME"
|
||||
"--grafana-admin-password=$GRAFANA_ADMIN_PASSWORD"
|
||||
"--meet-admin-user=$MEET_ADMIN_USER"
|
||||
"--meet-admin-password=$MEET_ADMIN_SECRET"
|
||||
"--meet-api-key=$MEET_API_KEY"
|
||||
"--livekit-api-key=$LIVEKIT_API_KEY"
|
||||
"--livekit-api-secret=$LIVEKIT_API_SECRET"
|
||||
)
|
||||
|
||||
# Include additional installer flags (trimmed)
|
||||
if [[ -n "$ADDITIONAL_FLAGS" && "$ADDITIONAL_FLAGS" != "none" ]]; then
|
||||
IFS=',' read -ra EXTRA_FLAGS <<< "$ADDITIONAL_FLAGS"
|
||||
for extra_flag in "$${EXTRA_FLAGS[@]}"; do
|
||||
extra_flag="$(echo -e "$extra_flag" | sed -e 's/^\s*//' -e 's/\s*$//')"
|
||||
if [[ -n "$extra_flag" ]]; then
|
||||
COMMON_ARGS+=("$extra_flag")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# TURN domain
|
||||
if [[ -n "$turnDomainName" && "$turnDomainName" != "none" ]]; then
|
||||
save_secret LIVEKIT_turnDomainName "$turnDomainName"
|
||||
COMMON_ARGS+=("--turn-domain-name=$turnDomainName")
|
||||
fi
|
||||
|
||||
# Certificate handling
|
||||
if [[ "$CERT_TYPE" == "selfsigned" ]] ; then
|
||||
CERT_ARGS=("--certificate-type=selfsigned")
|
||||
elif [[ "$CERT_TYPE" == "letsencrypt" ]] ; then
|
||||
save_secret LETSENCRYPT_EMAIL "$LE_EMAIL"
|
||||
CERT_ARGS=("--certificate-type=letsencrypt" "--letsencrypt-email=$LE_EMAIL")
|
||||
else
|
||||
# owncert: download from provided URLs and convert to base64
|
||||
mkdir -p /tmp/owncert
|
||||
if [[ -n "$OWN_CERT_URL" && -n "$OWN_KEY_URL" ]]; then
|
||||
wget -O /tmp/owncert/fullchain.pem "$OWN_CERT_URL"
|
||||
wget -O /tmp/owncert/privkey.pem "$OWN_KEY_URL"
|
||||
OWN_CERT_CRT=$(base64 -w 0 /tmp/owncert/fullchain.pem)
|
||||
OWN_CERT_KEY=$(base64 -w 0 /tmp/owncert/privkey.pem)
|
||||
CERT_ARGS=("--certificate-type=owncert" "--owncert-public-key=$OWN_CERT_CRT" "--owncert-private-key=$OWN_CERT_KEY")
|
||||
else
|
||||
echo "owncert selected but cert URLs not provided"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final command
|
||||
FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "$${COMMON_ARGS[@]}") $(printf "%s " "$${CERT_ARGS[@]}")"
|
||||
|
||||
# Execute installation
|
||||
bash -c "$FINAL_COMMAND"
|
||||
|
||||
# Configure GCS bucket in OpenVidu config if needed
|
||||
if [[ -n "$S3_BUCKET_NAME" && "$S3_BUCKET_NAME" != "none" ]]; then
|
||||
# Wait for openvidu config dir
|
||||
CONFIG_DIR="/opt/openvidu/config"
|
||||
if [[ -f "$${CONFIG_DIR}/openvidu.env" ]]; then
|
||||
sed -i "s|EXTERNAL_S3_BUCKET_APP_DATA=.*|EXTERNAL_S3_BUCKET_APP_DATA=$${S3_BUCKET_NAME}|" "$${CONFIG_DIR}/openvidu.env" || true
|
||||
fi
|
||||
fi
|
||||
EOF
|
||||
|
||||
labels = {
|
||||
stack = var.stackName
|
||||
}
|
||||
}
|
||||
|
||||
# ------------------------- outputs.tf -------------------------
|
||||
|
||||
output "openvidu_instance_name" {
|
||||
value = google_compute_instance.openvidu.name
|
||||
}
|
||||
|
||||
output "openvidu_public_ip" {
|
||||
value = length(google_compute_address.openvidu_ip) > 0 ? google_compute_address.openvidu_ip[0].address : google_compute_instance.openvidu.network_interface[0].access_config[0].nat_ip
|
||||
}
|
||||
|
||||
output "services_and_credentials_secret_id" {
|
||||
value = google_secret_manager_secret.openvidu.secret_id
|
||||
}
|
||||
|
||||
output "appdata_bucket" {
|
||||
value = var.bucketName == "" ? google_storage_bucket.appdata[0].name : var.bucketName
|
||||
}
|
||||
|
||||
# ------------------------- local values -------------------------
|
||||
|
||||
locals {
|
||||
isEmpty = var.bucketName == ""
|
||||
}
|
||||
|
||||
# ------------------------- variables -------------------------
|
||||
|
||||
# Variables used by the configuration
|
||||
variable "projectId" {
|
||||
description = "GCP project id"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "GCP region"
|
||||
type = string
|
||||
default = "europe-west1"
|
||||
}
|
||||
|
||||
variable "zone" {
|
||||
description = "GCP zone"
|
||||
type = string
|
||||
default = "europe-west1-b"
|
||||
}
|
||||
|
||||
variable "stackName" {
|
||||
description = "Stack name for OpenVidu deployment"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "certificateType" {
|
||||
description = "[selfsigned] Not recommended for production use. If you don't have a FQDN, (DomainName parameter) you can use this option to generate a self-signed certificate. [owncert] Valid for productions environments. If you have a FQDN, (DomainName parameter) and an Elastic IP, you can use this option to use your own certificate. [letsencrypt] Valid for production environments. If you have a FQDN, (DomainName parameter) and an Elastic IP, you can use this option to generate a Let's Encrypt certificate."
|
||||
type = string
|
||||
default = "selfsigned"
|
||||
validation {
|
||||
condition = contains(["selfsigned", "owncert", "letsencrypt"], var.certificateType)
|
||||
error_message = "certificateType must be one of: selfsigned, owncert, letsencrypt"
|
||||
}
|
||||
}
|
||||
|
||||
variable "publicStaticIp" {
|
||||
description = "Previously created Public IP address for the OpenVidu Deployment. Blank will generate a public IP"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "domainName" {
|
||||
description = "Optional domain name for the deployment"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "ownPublicCertificate" {
|
||||
description = "If owncert: URL to fullchain.pem"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "ownPrivateCertificate" {
|
||||
description = "If owncert: URL to privkey.pem"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "letsEncryptEmail" {
|
||||
description = "If letsencrypt: email for LE"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "additional_install_flags" {
|
||||
description = "Comma-separated additional flags passed to the OpenVidu installer"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "turnDomainName" {
|
||||
description = "Optional TURN server TLS domain"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "turnOwnPublicCertificate" {
|
||||
description = "Optional TURN public cert URL for owncert"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "turnOwnPrivateCertificate" {
|
||||
description = "Optional TURN private key URL for owncert"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "instanceType" {
|
||||
description = "GCE machine type"
|
||||
type = string
|
||||
default = "e2-standard-8"
|
||||
}
|
||||
|
||||
variable "boot_image" {
|
||||
description = "Boot image for the instance (family or specific image)"
|
||||
type = string
|
||||
default = "projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts"
|
||||
}
|
||||
|
||||
variable "bucketName" {
|
||||
description = "If empty, a GCS bucket will be created for app data and recordings"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
Loading…
Reference in New Issue