使用 Cloud Run 和 Terraform 在 GCP 上搭建几乎免费的 PeerJS 服务器

什么是 PeerJS?

PeerJS 是浏览器 WebRTC 实现的 JavaScript 封装程序,用于简化 Web 应用程序中 WebRTC 的实现。WebRTC 是一个开源项目,为网络浏览器和移动应用程序提供了一个通信框架。PeerJS 库提供了一个易于使用的界面,可简化 WebRTC API 的采用。

PeerJS API 为连接事件和接口提供监听器,以简化数据交换。这种连接还支持实时通信,这意味着您可以为视频聊天应用程序集成音频或视频流。

你可能想知道这一切的注意事项是什么,没错,有一个。你需要一台服务器来充当信令服务器(可能还有 STUN 或 TURN 服务器),以便在对等网络之间建立中介连接。虽然 PeerJS 提供了免费使用的 PeerJS 服务器版本,但这种设置并不适合网络实时通信的生产环境。

通过 Cloud Run 配置 PeerJS 服务器

对于希望实施 WebRTC API 的开发人员来说,运行专用服务器的主要障碍是成本高昂。考虑到对公共 IP 和负载平衡的需求,其成本也在不断增加。为了解决这个问题,您可以使用 Cloud Run。

Cloud Run 是一个用于容器化应用的全面管理平台。它允许您定义使用容器作为底层映像的工作负载和服务,这些工作负载和服务由 Cloud Run 平台管理并自动扩展(向上和向下)。

开发人员可使用 Cloud Run 运行 PeerJS 服务器的开源镜像,并拥有一个可完全控制环境的服务器实例。这是 PeerJS 客户端从浏览器连接的服务器。

Cloud Run 的主要优势在于其慷慨的免费套餐,允许业余开发者和小规模应用程序在该平台上运行应用程序。这些应用程序可以处理媒体连接和媒体流(音频或视频流)。

Cloud Run 的工作原理与 Lambda Functions 相似,容器在需要时开启,确保只需为使用付费。这意味着它也有同样的缺点,那就是冷启动。这是指服务器关闭后又重新开启,这可能会给初始请求增加几秒钟的时间。

配置实现

下文将介绍如何配置所有组件,以确保它们都能正常工作。所有基础架构元素都将通过 Terraform 进行设置,以确保一致性。

在开始实施之前,您应该确保您希望用户使用的网络浏览器支持点对点通信。

请注意以下免责声明。该解决方案实际上是免费的,因为免费套餐涵盖了一定的使用级别。例如,如果远程对等方开始打开多个连接,并持续使用该应用一个月,你可能会产生一些费用。设置计费预算和警报是您应该遵循的良好做法,您需要对自己的云使用情况负责。

配置 GCP 帐户

首先在Google Cloud中创建一个帐户。您可能需要添加信用卡详细信息,但不应开始产生费用。如果您是新用户,您可以获得价值 300 美元的积分。

创建帐户后,安装 gcloud CLI。它将确保 Terraform 在执行所有请求时具有必要的凭据。安装完成后,运行登录命令对工具进行授权。

配置 Google Cloud 提供商

在定义服务器的 Terraform 资源和计费预算之前,您应该在 Terraform 中为 Google Cloud 模块创建提供商配置。

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "5.3.0"
    }
  }
}

# Configure the GCP provider
provider "google" {
  project               = var.gcp_project_id
  region                = var.gcp_region
  billing_project       = var.gcp_project_id
  user_project_override = true
}te

你可能会注意到,有些配置使用了变量或本地值,定义:

locals {
  display_name = "tictactoe"
  # GCP APIS
  apis = ["billingbudgets.googleapis.com", "run.googleapis.com", "iam.googleapis.com", "cloudresourcemanager.googleapis.com", "cloudbilling.googleapis.com"]
  # Server locals
  server_name  = "${local.display_name}-server"
  server_image = "docker.io/peerjs/peerjs-server:latest"
  server_key   = md5(var.base_server_key)
}
variable "base_server_key" {
  type        = string
  description = "Server key to be used in connections."
}
variable "gcp_region" {
  type        = string
  description = "Region to use for GCP. By default is `europe-west1`."
  default     = "europe-west1"
}
variable "gcp_project_id" {
  type        = string
  description = "ID for the GCP project compute resources."
}

启用相关的 Google Cloud API

Google Cloud 的服务模型与 API 配合使用。在使用服务之前,需要启用其API。可以使用 Terraform 以编程方式执行此操作,这样做的好处是可以声明您在项目中配置的内容。

resource "google_project_service" "apis" {
  project            = var.gcp_project_id
  for_each           = toset(local.apis)
  service            = each.key
  disable_on_destroy = false
}

创建 Cloud Run 服务

对于 PeerJS 服务器,我们使用 PeerJS 提供的开源实现。PeerJS 背后的团队非常友好地提供了一个已构建好的 Docker 镜像,网址如下:docker.io/peerjs/peerjs-server。你也可以自己生成镜像,但这样可以节省托管镜像的成本。

下面的 Terraform 代码段包含了在 Cloud Run 中部署 PeerJS 服务器的过程:

resource "google_cloud_run_service" "default" {
  name     = local.server_name
  project  = var.gcp_project_id
  location = var.gcp_region

  # Metadata to note allow all ingress.
  metadata {
    annotations = {
      "run.googleapis.com/ingress"        = "all"
      "run.googleapis.com/ingress-status" = "all"
    }
  }

  # Container template
  template {
    metadata {
      # Ensure the correct annotations for scale and not continuously allocated.
      annotations = {
        "run.googleapis.com/startup-cpu-boost" = false
        "autoscaling.knative.dev/maxScale"     = 1
      }
    }
    spec {
      # Container request limits.
      container_concurrency = 50
      timeout_seconds       = 300
      containers {
        # Image and port configuration for the Peer JS server.
        image = local.server_image
        ports {
          container_port = 9000
        }
        # Arguments you send to the container.
        args = ["--allow_discovery", "--key", "${local.server_key}"]
        # Resources allocated for the container.
        resources {
          limits = {
            cpu    = "1000m"
            memory = "512Mi"
          }
        }
        # Probe to determine if the container started correctly.
        startup_probe {
          timeout_seconds   = 240
          period_seconds    = 240
          failure_threshold = 1
          tcp_socket {
            port = 9000
          }
        }

      }
    }
  }
  # Traffic allocation to the revisions is always 100%.
  traffic {
    percent         = 100
    latest_revision = true
  }
}

请注意以下配置:

  • 入口注释允许来自任何地方的流量。
  • 模板元数据注释将容器实例的最大数量限制为 1,并防止 CPU 持续开启(这会增加成本)。文档没有明确说明这一点,但容器实例的最大数量是 1(在App Engine 部署示例中概述了这一点)。
  • 容器并发限制了同时处理的请求数量。如果预期流量较高,可以更改。
  • 根据 PeerJS 服务器文档,容器端口必须是 9000 端口。
  • 你可以向容器发送参数,从而定制部署。我建议添加 allow_discovery 标记和服务器密钥选项,以使你的实施更加稳健。
  • 容器的容量限制为 1 个 vCPU 和 512 MB 内存,这对于初始实施来说绰绰有余。全面生产部署可能需要分配更多资源。
  • 配置启动探针时,确保端口是容器端口,如端口 9000。
  • 所有流量都会自动转到最新版本。有必要防止远程对等方使用旧版本。

要使实例对互联网开放,还需要一个允许所有用户(包括未验证用户)向服务器发送请求的策略。除此之外,还有 Cloud Run 的策略附件。以下 Terraform 资源可以解决这个问题。

# Policy for no authentication.
data "google_iam_policy" "noauth" {
  binding {
    role = "roles/run.invoker"
    members = [
      "allUsers",
    ]
  }
}

# Policy to allow unauthenticated requests.
resource "google_cloud_run_service_iam_policy" "noauth" {
  location = google_cloud_run_service.default.location
  project  = google_cloud_run_service.default.project
  service  = google_cloud_run_service.default.name

  policy_data = data.google_iam_policy.noauth.policy_data
}

如果从 Google Cloud Console 部署,请复制 Terraform 文件中的配置,并确保允许所有未经验证的请求。这样,任何远程对等设备都能访问服务器。

创建计费预算

计费预算是部署中最相关的方面之一,因为它能确保您花费(或消耗)的金额是您可以接受的。下面是我配置的计费预算示例。该预算为 1 美元,用完一半后会收到通知。

data "google_billing_account" "account" {
  provider     = google
  display_name = "My Billing Account"
}

# Billing limits for the GCP project.
resource "google_billing_budget" "budget" {
  billing_account = data.google_billing_account.account.id
  display_name    = "Billing Budget"
  # Max budget is 1 USD.
  amount {
    specified_amount {
      currency_code = "USD"
      units         = "1"
    }
  }
  # Alerts at 50% of the budget.
  threshold_rules {
    threshold_percent = 0.5
  }
  depends_on = [google_project_service.apis]
}

请注意,要分配预算,首先需要获取与项目关联的计费账户。

配置 Terraform 输出

为 Terraform 创建输出文件,以便更轻松地获取服务器 URI 和服务器密钥的值。

output "server_url" {
  value       = google_cloud_run_service.default.status[0].url
  description = "URL of the Cloud Run server."
}
output "server_key" {
  value       = local.server_key
  description = "Key for the server."
}

如何配置 PeerJS 客户端

首先包含来自 Peer JS CDN 的 PeerJS 库的 JavaScript 文件,还可以自己托管peer min JS 文件。可以在这里获取更多相关信息:

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Head content... -->
</head>
<body>
    <!-- Add this at the bottom of the body... -->
    <script src="https://unpkg.com/peerjs@1.5.1/dist/peerjs.min.js"></script>
    <!-- Any other scripts... -->
</body>
</html>

然后,就可以创建一个对等对象,它将处理与其他对等对象的所有连接。请注意,在初始化对等对象时,可以定义对等 ID,该 ID 在所有用户中应该是唯一的。在我的实现中,我在初始化对等对象时定义的对等 ID 如下:

// Generate the ID for the connection, once per load of the tab.
const UUID = window.crypto.randomUUID();
// Server connection information.
// Complete with the URI of your Cloud Run server.
const SERVER_URI = 'example.a.run.app';
// Key will be exposed to front end, that's fine.
// This is the MD5 of the key from Terraform.
const SERVER_KEY = 'XXXX';
const SERVER_CONNECTION = {
  host: SERVER_URI,
  port: 443,
  ping: 1000 * 15, // 15s ping
  secure: true,
  debug: 2,
  key: SERVER_KEY,
};
// Create the peer instance object.
let peer = new Peer(UUID, SERVER_CONNECTION);
// Configure callback when a correct connection with
// the Cloud Run server is established.
peer.on('open', (id) => {
  console.log(`My peer ID is ${id}`);
});

您也可以允许用户定义自己的对等 ID,但应正确处理重复 ID。我建议你坚持使用随机 ID,它甚至可以是人类可读的。上述方法可让当前浏览器中的每个窗口都成为一个新的对等点。在某些浏览器中,可能无法使用加密 API,除非在安全环境(即启用 SSL 的网站)下工作。这就改变了你部署前端的方式,因为你应该使用 SSL。

创建对等对象的一部分工作是定义连接参数。请注意你是如何定义服务器密钥和服务器 URI 的。

有了对等对象后,就可以定义连接监听器,以便在 Peer JS 支持的任何连接事件上采取行动。您可以将它们定义为在事件触发后执行的函数。客户端代码将包含处理连接的所有逻辑,而 Peerserver 实例则是连接的中介。这里的部分工作是处理连接过程中可能出现的任何错误(这是非常容易出错的)。

// Start a connection on one end.
let conn = peer.connect('YOUR_DESTINATION_PEER');
conn.on('open', function() {
    // Configure other connection listeners like send or
  // received data using the conn object.
});
// Receive a connection on the other end.
peer.on('connection', (conn) => {
  // Add connection listeners to the conn object.
});

请注意,连接的双方都应该有创建或接收连接的代码。这是因为两端都应能启动或接受连接。

有了连接侦听器后,只要知道其他对等节点的 ID,就可以与它们建立直接连接。具体方法是调用 connect 方法。请注意,这将在连接的接收端触发一个连接事件。

作为客户端实现的最后一部分,您必须能够使用对等对象提供的发送函数发送数据。

conn.send({
  strings: 'hi!',
  numbers: 150,
  arrays: [1, 2, 3],
  evenBinary: new Blob([1, 2, 3]),
  andMore: { bool: true },
});

文档中的 Peer JS 教程有一个引人注目的 Peer JS 示例,可以帮助您开始学习。

实现对等查找

对等查询是不容忽视的一个方面。它为用户提供了良好的体验,因为他们可以轻松地建立远程连接。

实施这种查找有两个方面。一方面,你需要允许远程对等设备从服务器获取数据,比如对等设备列表。我们已经配置了 PeerJS 服务器的 Cloud Run 实例,以允许同行发现。它将在服务器上启用一个 /peers 路由,任何人都可以通过以下 URI 进行查询。

`https:// ${SERVER_URI} / ${SERVER_KEY} /peers`;

然后,您的客户端代码需要支持用户从服务器获取数据,并选择要连接到哪个对等体。

请注意,您可以自定义运行在 Peer JS 服务器中的逻辑,也就是说,您可以在服务器上做任何您想做的事情。但这需要构建自己的 Docker 镜像。

结束语

正如本文所概述的,在使用点对点连接时,物理服务器是一个关键的依赖因素。选择一个经济高效(或几乎免费)的托管解决方案对于初始测试和小规模项目非常重要。在转向更可定制的解决方案(如有必要)之前,GCP 中的云运行可以作为业余爱好或生产环境的重要选择。本文概述了仅部署解决方案后端部分的步骤,但仍有整个前端方面的内容。

你可以在点对点的终极井字游戏中前往我的 PeerJS 实现,并在线挑战你的朋友。

作者:Gonzalo Hirsch
原文:https://gonzalohirsch.com/blog/virtually-free-peer-js-server-on-gcp/

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/webrtc/38715.html

(0)

发表回复

登录后才能评论