# 托管集群

了解托管集群并快速开始使用自定义后端解决方案。

## ✔️ 介绍

托管集群让托管自管理游戏服务和游戏后端变得简单快捷。您准备服务镜像，我们提供高可用、弹性的云环境来运行它们：

* 玩家认证，
* 数据存储 - 帐号、进度、物品、奖励，...
* 社交服务 - 聊天、氏族、排行榜、比赛，...
* 自定义匹配 - 使用 [#advanced-matchmaker](#advanced-matchmaker "mention"), [#nakama-by-heroic-labs](#nakama-by-heroic-labs "mention"), ...
* 无服务器计算 - 托管的 [函数即服务](https://github.com/openfaas/faas) （或云脚本、lambda），...

私有集群确保您的服务拥有 **专用计算以 24/7 为玩家提供服务**.

{% hint style="info" %}
我们的集群机器使用时钟频率为 2.4 - 3.2 GHz 的 AMD/Intel CPU。如需协调负载测试并确保服务器有足够的资源可用，请通过 [社区 Discord](https://discord.gg/MmJf8fWjnt) 联系以进行协调并确保您的服务器有足够的可用资源。
{% endhint %}

## 🛠️ 开发者工具

如果您看到改进的机会，请在我们的 [社区 Discord](https://discord.gg/NgCnkHbsGp).

我们希望您能享受顺畅的体验。🚀

### Docker

为了帮助使您的服务器可靠，我们使用 [Docker](https://www.docker.com/) - 虚拟化软件以确保从操作系统级别到所有服务器代码依赖项始终完全相同，无论服务器如何或在哪里启动。

### Kubernetes (K8s)

[Kubernetes](https://kubernetes.io/docs/concepts/overview/)，也称为 K8s，是一个用于自动化部署、扩展和管理容器化应用程序（Docker 镜像）的开源系统。它将构成应用程序的容器分组为便于管理和发现的逻辑单元。

Edgegap 托管集群提供用于管理目的的 Kubernetes API。

### K8s Lens

拥有超过一百万用户， [K8s Lens](https://k8slens.dev/) 是世界上最受欢迎的 Kubernetes IDE。连接到集群、浏览、获取洞见、学习并在需要时采取操作。Lens 实时提供来自您的工作负载和资源的所有信息，并始终以正确的上下文呈现。

Edgegap 集群的 Kubernetes API 可通过 Lens 或其他 Kubernetes IDE 使用。

### Helm 包管理器

[Helm](https://helm.sh/) 是查找、共享和使用为 Kubernetes 构建的软件的最佳方式。Helm 帮助您管理 Kubernetes 应用程序——Helm Chart 帮助您定义、安装和升级即使是最复杂的 Kubernetes 应用程序。Chart 易于创建、版本控制、共享和发布——所以开始使用 Helm，停止复制粘贴。

[安装 Helm CLI](https://helm.sh/docs/intro/install/) 为开发者提供了一个简单的界面来管理其集群包。

## 🚀 快速开始

☑️ [已注册您的免费 Edgegap 帐户](https://app.edgegap.com/auth/register) 并升级到按需付费等级以解锁集群功能。

☑️ 导航到 [托管集群](https://app.edgegap.com/cluster-management/clusters/list) 页面。

☑️ 点击 **创建集群** 首先，然后输入：

* **标签** 用于您的集群以便以后轻松找到，
* **集群规模 -** 参见 [#introduction](#introduction "mention").

{% hint style="danger" %}
**我们强烈建议为开发和生产环境创建单独的集群。**
{% endhint %}

☑️ 查看估算费用并点击 **创建集群** 以开始您的新集群。

☑️ 一旦集群就绪， **点击 Kubeconfig 下载您的配置和凭据** 以便连接和管理您的新集群。

☑️ [将您的 kubeconfig 文件移动](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) 到 `kubectl` 可以找到它的位置。

☑️ Lens 用户： [导入您的 kubeconfig 文件](https://docs.k8slens.dev/getting-started/add-cluster/#specify-kubeconfig-files).

☑️ 使用命令测试您的集群连接 `kubectl get nodes` :

```bash
kubectl get nodes
名称                            状态     角色      运行时长    版本
lke334087-533013-294dcfe70000   Ready    <none>   10m   v1.31.0
lke334087-533013-4e69edc10000   Ready    <none>   10m   v1.31.0
lke334087-533013-50bf39880000   Ready    <none>   10m   v1.31.0
```

🙌 恭喜，您已完成托管集群设置！现在可以安装您的服务了。

## 📦 Heroic Labs 的 Nakama

{% hint style="info" %}
将 Edgegap 与 [Nakama 插件](https://github.com/edgegap/nakama-edgegap) + [Unity 插件](https://github.com/edgegap/edgegap-server-nakama-plugin-unity)! [集成。其他平台/功能请联系我们。](https://discord.gg/NgCnkHbsGp)
{% endhint %}

按照以下步骤在托管集群上托管您自己的 [Nakama 游戏后端](https://heroiclabs.com/docs/nakama/getting-started/) ：

☑️ 创建一个 **DNS A 记录类型** 在您的 DNS 提供商处（例如 [Cloudflare](https://developers.cloudflare.com/dns/get-started/)），记下 URL 以备后用。您的 **用于该 DNS 记录的外部 IP** 可以在 Lens 的 Services / `ingress-nginx-controller 下找到。` .

☑️ 使用 DNSchecker 执行查找以验证您的 DNS 是否正确设置 [使用 DNSchecker](https://dnschecker.org/ns-lookup.php).

☑️ 创建名为 `values.yaml 的文件` 并包含以下内容（使用您自己的值）：

<pre class="language-yaml"><code class="lang-yaml">isProductionEnvironment: true

# Nakama 服务器的外部主机名 - 来自上一步的 DNS 记录
externalHostName: <a data-footnote-ref href="#user-content-fn-1">&#x3C;DNS_A_RECORD_URL></a>

nakama:
  # 要部署的 Nakama 版本。
  # 可用版本请参见 https://hub.docker.com/r/heroiclabs/nakama/tags 。
  version: 3.26.0
  # Nakama 控制台的用户名和密码
  username: <a data-footnote-ref href="#user-content-fn-2">&#x3C;USERNAME></a>
  password: <a data-footnote-ref href="#user-content-fn-3">&#x3C;PASSWORD></a>
</code></pre>

{% hint style="danger" %}
**将上面 \<VALUES> 替换为您自己的值** 在上述文件中。
{% endhint %}

☑️ 部署 Nakama helm chart：

```bash
helm upgrade --install \
  --namespace nakama --create-namespace -f <FILE_PATH>/values.yaml \
  --version 1.0.0 <RELEASE_NAME> oci://registry-1.docker.io/edgegap/heroiclabs-nakama
```

☑️ 在 Lens 的 Workloads / Deployments 部分验证安装， `nakama` 应处于运行状态。

✅ **使用来自** 文件的 URL 和凭据连接到您的 Nakama 控制台。 `values.yaml 的文件` 文件。

🙌 恭喜，您已完成自托管 Nakama 游戏后端的设置！

### 服务更新

按照以下步骤更新托管在托管集群中的服务：

☑️ 更新您的 `value.yaml` 文件以包含新文件。

☑️ 使用以下命令更新您的 helm chart：

```bash
helm upgrade --reuse-values \
  --namespace nakama -f <FILE_PATH>/values.yaml \
  --version 1.0.0 <RELEASE_NAME> oci://registry-1.docker.io/edgegap/heroiclabs-nakama
```

☑️ 通过关闭已更新的 pod 来重新加载更改，这将导致在我们自动重启 pod 后使用新的 helm chart。

🙌 恭喜，您已完成 Nakama 集群更新！

## 👷 高级匹配器

按照以下步骤在托管集群上托管您的 [OpenMatch](https://open-match.dev/site/) ：

☑️ 在您的 DNS 提供商处创建一个 A 类型 DNS 记录（例如 [Cloudflare](https://developers.cloudflare.com/dns/get-started/)），记下 URL 以备后用。您的 **用于该 DNS 记录的外部 IP** 可以在 Lens 的 Services / `ingress-nginx-controller 下找到。` .

☑️ 使用 DNSchecker 执行查找以验证您的 DNS 是否正确设置 [使用 DNSchecker](https://dnschecker.org/ns-lookup.php).

☑️ 创建名为 `values.yaml 的文件` 并包含以下内容（使用您自己的值）：

```yaml
isProductionEnvironment: false

director:
  credential:
    registry: <YOUR_DIRECTOR_REGISTRY>
    username: <YOUR_DIRECTOR_REGISTRY_USERNAME>
    password: <YOUR_DIRECTOR_REGISTRY_PASSWORD>
  image: <DIRECTOR_IMAGE>
  env: {
    "KEY": "VALUE"
  }

mmf:
  credential:
    registry: <MATCHMAKER_FUNCTION_REGISTRY>
    username: <MATCHMAKER_FUNCTION_REGISTRY_USERNAME>
    password: <MATCHMAKER_FUNCTION_REGISTRY_PASSWORD>
  image: <MATCHMAKER_FUNCTION_IMAGE>
  env: {
    "KEY": "VALUE"
  }

frontend:
  credential:
    registry: <FRONTEND_REGISTRY>
    username: <FRONTEND_REGISTRY_USERNAME>
    password: <FRONTEND_REGISTRY_PASSWORD>
  externalHostName: <YOUR_CLOUDFLARE_HOST_NAME> # 例如： exemple.test.com
  image: <FRONTEND_IMAGE>
  env: {
    "KEY": "VALUE"
  }

# 对所有子图可见的全局配置
global:
  kubernetes:
    resources:
      requests:
        memory: 100Mi
        cpu: 100m
      limits:
        memory: 100Mi
        cpu: 100m
```

{% hint style="danger" %}
**将上面 \<VALUES> 替换为您自己的值** 在上述文件中。
{% endhint %}

☑️ 将 **Edgegap 仓库** 添加到您的仓库列表：

```bash
helm repo add edgegap-public https://registry.edgegap.com/chartrepo/edgegap-public
```

☑️ 部署高级匹配器 helm chart：

```bash
helm upgrade --install \
  --namespace matchmaker --create-namespace -f <FILE_PATH>/values.yaml \
  --version 1.0.1 <RELEASE_NAME> edgegap-public/open-match-edgegap
```

🙌 恭喜，您已完成高级匹配器设置！

### 服务更新

按照以下步骤更新托管在托管集群中的服务：

☑️ 更新您的 `value.yaml` 文件以包含新文件。

☑️ 使用以下命令更新您的 helm chart：

```bash
helm upgrade --reuse-values \
  --namespace matchmaker -f <FILE_PATH>/values.yaml \
  --version 1.0.1 <RELEASE_NAME> edgegap-public/open-match-edgegap
```

☑️ 通过关闭已更新的 pod（director、mmf、frontend）来重新加载更改，这将导致在我们自动重启 pod 后使用新的 helm chart。

🙌 恭喜，您已完成高级匹配器更新！

### 持续部署

通过将此 shell 脚本添加到您的部署管道来自动化更新您的服务：

```bash
#!/bin/bash

RELEASE_NAME="<RELEASE_NAME>"
NAMESPACE="matchmaker"  # 如果您更改了命名空间，请更改此项。

helm upgrade --reuse-values -f <FILE_PATH>/value.yaml --namespace $NAMESPACE --version 1.0.1 $RELEASE_NAME edgegap-public/open-match-edgegap

echo "正在安装 redis-tools"
apt-get update
apt-get install -y redis-tools

DIRECTOR_DEPLOYMENT_NAME="$RELEASE_NAME-director"
MMF_DEPLOYMENT_NAME="$RELEASE_NAME-mmf"
CUSTOM_FRONTEND_DEPLOYMENT_NAME="$RELEASE_NAME-custom-frontend"
REDIS_HOST="$RELEASE_NAME-redis-master"

declare -A replicas

# 对于每个部署（director、mmf、custom-frontend），停止这些 pod
for deployment in $DIRECTOR_DEPLOYMENT_NAME $MMF_DEPLOYMENT_NAME $CUSTOM_FRONTEND_DEPLOYMENT_NAME
do
  echo "正在停止部署的 pod：$deployment"
  replicas[$deployment]=$(kubectl get deployment $deployment -o=jsonpath='{.spec.replicas}' --namespace $NAMESPACE)
  kubectl scale deployment/$deployment --replicas=0 --namespace $NAMESPACE
done

# 等待直到 pod 被终止
for deployment in $DIRECTOR_DEPLOYMENT_NAME $MMF_DEPLOYMENT_NAME $CUSTOM_FRONTEND_DEPLOYMENT_NAME
do

  echo "正在等待部署的 pod 被终止：$deployment"
  kubectl wait --for=delete pod -l app=$deployment --timeout=60s --namespace $NAMESPACE

  # 检查 wait 命令是否成功。如果不成功，则退出脚本
  if [ $? -ne 0 ]; then
    echo "等待部署的 pod 被终止失败：$deployment"
    exit 1
  fi
done

# 清理 redis 数据库
echo "正在清理 redis 数据库"
redis-cli -h $REDIS_HOST flushall

# 对于每个部署（director、mmf、custom-frontend），将 pod 缩放回原始数量
for deployment in $DIRECTOR_DEPLOYMENT_NAME $MMF_DEPLOYMENT_NAME $CUSTOM_FRONTEND_DEPLOYMENT_NAME
do
  echo "正在将部署的 pod 缩放到 ${replicas[$deployment]}：$deployment"
  kubectl scale deployment/$deployment --replicas=${replicas[$deployment]} --namespace $NAMESPACE
done
```

### Letsencrypt 证书验证（C#）

对于某些客户端，推荐的 Letsencrypt 证书验证可能会失败并出现错误：

```bash
Curl 错误 60：证书验证失败。证书已过期。UnityTls 错误代码：7
```

{% hint style="success" %}
更新您的操作系统可能会解决由于过时根证书颁发机构导致的问题。
{% endhint %}

作为最后手段，游戏客户端可以实现自定义证书处理函数：

````csharp
```csharp
public class CustomCertificateHandler : CertificateHandler
{
  private readonly string EXPECTED_CERT = "-----BEGIN CERTIFICATE-----<key>-----END CERTIFICATE-----\r\n";
  protected override bool ValidateCertificate(byte[] certificateData)
  {
    X509Certificate2 certificate = new X509Certificate2(certificateData);
    X509Certificate2 expectedCert = new X509Certificate2(Encoding.ASCII.GetBytes(EXPECTED_CERT));

    using (SHA256 sha256 = SHA256.Create())
    {
      Debug.Log("certificate.Thumbprint: " + certificate.Thumbprint);
      Debug.Log("expectedCert.Thumbprint: " + expectedCert.Thumbprint);

      return certificate.Thumbprint == expectedCert.Thumbprint;
    }
  }
}
```
````

用法：

```csharp
UnityWebRequest request = UnityWebRequest.Get(...);
request.certificateHandler = new BypassCertificateHandler();
request.SendWebRequest();
request.certificateHandler.Dispose();
```

我们建议将 `EXPECTED_CERT` 值存储在您自己的文件存储中，并在运行时检索，以便您可以在不发布游戏客户端更新的情况下更新它。

## 🟢 运营与可观测性

### 集群层级更改

为成功做好准备并在发布后优化，这样您就不会在发布日阻塞玩家体验。

{% hint style="warning" %}
&#x20;更改集群规模需要停止您的集群。参见 [蓝/绿部署](https://circleci.com/blog/canary-vs-blue-green-downtime/) 以实现零停机更新。
{% endhint %}

### 支持与未来更新

**您的成功是我们的优先事项。** 如果您想发送自定义请求、要求缺失的关键功能，或表达任何想法， [请在我们的社区 Discord 中联系](https://discord.gg/MmJf8fWjnt).

[^1]: 来自上一步的 DNS 记录

[^2]: 选择您自己的管理员用户名

[^3]: 为管理员选择一个安全密码
