> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 在 VM 创建期间运行自定义脚本(预览)

> 使用 VmStartupScript 预览功能在 PlayFab Multiplayer Servers 的 VM 创建期间运行自定义脚本,支持代理安装和日志路由。

# 在 VM 创建期间运行自定义脚本 - VmStartupScript(预览)

## 简介

<Info>
  此功能处于预览阶段。欢迎您今天开始使用它并向我们提供反馈。有关如何与我们联系的说明将在文章末尾提供。请注意,预览期间技术支持有限。
</Info>

VmStartupScript 允许您在 PlayFab Multiplayer Servers (MPS) 中使用的虚拟机 (VM) 上运行自定义脚本。MPS 针对游戏服务器托管进行了优化,使您的标题可以轻松地根据需求动态扩展。为了在 VM 初始化期间提高快速自定义大量服务器的便利性,自定义脚本可以在托管您的游戏服务器的每个底层 VM 上运行。它可用于完成诸如安装自定义软件、修改安全设置、使用自定义服务记录游戏服务器输出和指标等任务。

<Note>
  这是一个高级功能,应极其谨慎地使用。运行的脚本在虚拟机 (VM) 级别以管理员(root)权限执行。如果使用不当,可能会破坏正在运行的游戏服务器的常规流程,甚至完全阻止它们运行。最终用户对脚本的内容负责。
</Note>

<iframe src="https://www.youtube.com/embed/oc-X7rHCwUU" width="100%" height="400" allowFullScreen frameBorder="0" />

## 如何使用 VmStartupScript

要使用 VmStartupScript 功能,您必须提供自定义脚本以及您计划安装的所有相关软件(可选)。脚本在虚拟机初始化时开始执行。此操作发生在游戏服务器在每个 VM 上启动之前。脚本成功完成执行后,MPS 服务将继续完成初始化游戏服务器,并将它们交付到 **StandingBy** 状态。要了解有关不同游戏服务器状态的更多信息,请参见[多人游戏服务器的生命周期](/services/playfab/multiplayer/servers/multiplayer-game-server-lifecycle)。

要在实际生产环境中使用此功能,请在开始之前参见[推荐的开发者工作流](#recommended-development-workflow)。

### 创建脚本

* 为 Linux VM 创建一个名为 **PF\_StartupScript.sh** 的文件,或为 Windows VM 创建一个名为 **PF\_StartupScript.ps1** 的文件。
* 在文件中添加设置/执行命令。如果需要,这里有一些常用的[环境变量](#environment-variables),您可以在脚本中使用。某些操作不受支持或会导致 VM 无法成功启动,从而产生不必要的费用。有关详细信息,请参见[不支持的内容](#what-is-not-supported)部分。

有关脚本示例,请参见 [VmStartupScriptGallery](https://github.com/PlayFab/VmStartupScriptGallery)。

### 创建并上传压缩文件

1. 在您的脚本计划使用或调用的文件夹中收集所有相关软件。如果您的脚本安装第三方软件,您的脚本可以在执行期间下载它,或者可以将其与压缩文件捆绑在一起。如果您不安装任何内容,请跳过此步骤。
2. 使用您在前面部分创建的脚本 (.sh 或 .ps1) 和您在前面步骤中收集的软件(如果需要),创建一个压缩文件 (.zip)。脚本文件应位于压缩文件的根目录,而不是在目录内。此外,如果您的脚本文件未命名为 **PF\_StartupScript.sh** (Linux) 或 **PF\_StartupScript.ps1** (Windows),它将不会被执行,并且游戏服务器将无法启动。
3. 使用以下方法之一上传压缩文件:

* 使用 [PlayFab Game Manager](/services/playfab/multiplayer/servers/deploy-using-game-manager#assets-for-builds)
* 使用标头 \{"x-ms-blob-type": "BlockBlob"} 在 [GetAssetUploadUrl API](https://learn.microsoft.com/en-us/rest/api/playfab/multiplayer/multiplayer-server/get-asset-upload-url) 调用返回的 URL 上发出 PUT 请求。
* 使用 [PowerShell cmdlet](/services/playfab/multiplayer/servers/deploy-using-powershell-api#upload-an-asset)。

<Note>
  我们建议在压缩文件中包含脚本所需的所有二进制文件和资产,因为这将导致更快的执行速度和 MPS 交付游戏服务器所需的更短时间。请确保包含适用于您游戏服务器将运行的相关平台的资产。例如,如果您使用 Linux 服务器,则应包含 "amd64" Debian/Ubuntu 包。
</Note>

### 将自定义脚本应用于新构建

上传 .zip 文件后,在配置 **VmStartupScriptAssetReference** 属性后,使用 MPS API 创建新构建。有关说明,请参见[如何使用 MPS API 创建构建](/services/playfab/multiplayer/servers/deploy-using-powershell-api)。

* 添加 **VmStartupScriptConfiguration.VmStartupScriptAssetReference** 属性,其中包含对上传的资产文件的引用。此属性是所有 "CreateBuild" 相关 API 的一部分,例如 [CreateBuildWithCustomContainer](https://learn.microsoft.com/en-us/rest/api/playfab/multiplayer/multiplayer-server/create-build-with-custom-container)、[CreateBuildWithManagedContainer](https://learn.microsoft.com/en-us/rest/api/playfab/multiplayer/multiplayer-server/create-build-with-managed-container) 和 [CreateBuildWithProcessBasedServer](https://learn.microsoft.com/en-us/rest/api/playfab/multiplayer/multiplayer-server/create-build-with-process-based-server)。
* 为 **VmStartupScriptAssetReference.FileName** 属性添加有效值。此值必须与您的资产文件的名称相同,例如 **vmstartupscriptassets.zip**。
* **VmStartupScriptAssetReference.MountPath** 属性必须为空,因为 VmStartupScript 功能不支持它。

<Note>
  如果您为 **MountPath** 属性设置值,则创建构建操作将失败。
</Note>

以下代码示例创建一个具有 Linux 容器的构建,然后使用 **vmstartupscriptassets.zip** 中的脚本自定义 VM:

```csharp theme={null}
var request = new CreateBuildWithCustomContainerRequest()
{ 
    ContainerImageReference = new ContainerImageReference()
    {
         ImageName= "testimagename",
         Tag= "0.1"
    },
    ContainerFlavor = ContainerFlavor.CustomLinux,
    BuildName = "testbuildwithvmstartupscript",
    VmSize = AzureVmSize.Standard_D2as_v4,
    MultiplayerServerCountPerVm = 3,
    Ports= new List<Port>
    {
        new Port()
        {
            Name= "port",
            Num= 123,
            Protocol = ProtocolType.TCP   
        }
    },
    RegionConfigurations = new List<BuildRegionParams> { new BuildRegionParams()
    {
        Region = "EastUs",
        StandbyServers = 3,
        MaxServers = 6

    } 
    },
    VmStartupScriptConfiguration = new VmStartupScriptParams()
    {
        VmStartupScriptAssetReference = new AssetReferenceParams()
        {
            FileName = "vmstartupscriptassets.zip"
        }
    }
};
var result = await PlayFabMultiplayerAPI.CreateBuildWithCustomContainerAsync(request);
```

VmStartupScript 在 VM 的 "Propping" 阶段执行,并且必须成功终止才能启动游戏服务器。如果它失败(退出代码不是 0),VM 将不会转换到 "Running" 状态,您需要通过 RDP/SSH 连接到 VM 进行调试。要了解更多信息,请参见[推荐的开发者工作流](#recommended-development-workflow)。VM 将继续重试执行 VmStartupScript。

## 特殊注意事项

### 在 Linux 上,我是否需要将 PF\_StartupScript.sh 文件标记为可执行?

在 MPS 运行脚本文件之前,它将其标记为可执行,然后将任何 Windows 行结尾 ("\r\n") 转换为 Linux 行结尾 ("\n")。因此,您无需担心这两件事。

### 环境变量

以下是您可以在启动脚本中使用的环境变量。

| 名称                              | 描述                   |
| ------------------------------- | -------------------- |
| PF\_TITLE\_ID                   | PlayFab 标题 ID        |
| PF\_BUILD\_ID                   | PlayFab MPS 构建 ID    |
| PF\_VM\_ID                      | MPS 虚拟机 ID           |
| PF\_REGION                      | 托管 VM 的 Azure 区域     |
| PF\_PUBLIC\_IPV4\_ADDRESS       | VM 的公共 IP 地址         |
| PF\_FQDN                        | 与 VM 公共 IP 对应的完全限定域名 |
| PF\_SHARED\_CONTENT\_FOLDER\_VM | 具有 VM 范围共享内容的文件夹     |

### 不支持的内容

您不应从脚本中执行这些操作,因为它们很可能会破坏 VM 和游戏服务器的生命周期:

* 不要在启动脚本执行期间阻塞。脚本必须成功结束,游戏服务器才能被创建。如果您需要某些内容在后台运行,可以将其作为 Linux 上的 systemd 服务或 Windows 服务安装。
* 不要使用从 30000 开始的端口,因为它们用于游戏服务器,也不要使用 56001 端口,因为它由 VmAgent 进程(MPS 游戏服务器编排器可执行文件)使用。
* 不要修改 D:(Windows)或 /mnt(Linux)路径中的任何文件,因为这些文件对于 VmAgent 操作是必需的(除了包含可编辑内容的文件夹,如 `PF_SHARED_CONTENT_FOLDER_VM`)。
* 您不应从 VmStartupScript 或由它启动的应用程序中使用 [GSDK](https://github.com/PlayFab/gsdk)。GSDK 应仅从 GameServer 使用。
* 您不应手动重启虚拟机,因为此操作将在与 MPS Control Plane 的通信中产生挑战。

## 端口

当您使用 VmStartupScript 功能时,可以请求在每个 VM 上公开一些端口。这些端口可由您的脚本启动的任何程序使用,并且不同于 MPS 为您的游戏服务器打开的端口。

### 用法

您可以为每个 VM 请求最多五个端口。对于每个端口,您必须指定协议(TCP 或 UDP)和名称。以下是请求两个端口的示例:

```csharp theme={null}
VmStartupScriptConfiguration = new VmStartupScriptParams()
{
    VmStartupScriptAssetReference = new AssetReferenceParams()
    {
        FileName = "vmstartupscriptassets.zip"
    },
    PortRequests = new List<VmStartupScriptPortRequest>()
        {
            new VmStartupScriptPortRequest()
            {
                Name = "port0",
                Protocol = ProtocolType.TCP
            },
            new VmStartupScriptPortRequest()
            {
                Name = "port1",
                Protocol = ProtocolType.UDP
            }
        }
}
```

只要您请求任何端口,您的脚本就可以使用以下环境变量来帮助您获取有关端口的信息:

| 名称                                           | 描述                                           |
| -------------------------------------------- | -------------------------------------------- |
| PF\_STARTUP\_SCRIPT\_PORT\_COUNT             | VmStartupScript 端口数                          |
| PF\_STARTUP\_SCRIPT\_PORT\_NAME\_(index)     | 端口的名称,如请求中所述                                 |
| PF\_STARTUP\_SCRIPT\_PORT\_PROTOCOL\_(index) | 端口的协议,如请求中所述                                 |
| PF\_STARTUP\_SCRIPT\_PORT\_INTERNAL\_(index) | 您的程序应在 VM 中绑定的端口                             |
| PF\_STARTUP\_SCRIPT\_PORT\_EXTERNAL\_(index) | 在外部端点中打开的端口。外部客户端应使用此端口连接到绑定到 INTERNAL 端口的程序 |

例如,对于上面示例脚本中请求的两个端口,您应该期望在您的 VmStartupScript 中找到这些环境变量:

```bash theme={null}
PF_STARTUP_SCRIPT_PORT_COUNT

PF_STARTUP_SCRIPT_PORT_INTERNAL_0
PF_STARTUP_SCRIPT_PORT_EXTERNAL_0
PF_STARTUP_SCRIPT_PORT_NAME_0
PF_STARTUP_SCRIPT_PORT_PROTOCOL_0

PF_STARTUP_SCRIPT_PORT_INTERNAL_1
PF_STARTUP_SCRIPT_PORT_EXTERNAL_1
PF_STARTUP_SCRIPT_PORT_NAME_1
PF_STARTUP_SCRIPT_PORT_PROTOCOL_1
```

<Info>
  与我们为游戏服务器打开的端口类似,由您来验证连接到您端口的客户端。MPS 不为这些端口提供任何身份验证机制。
</Info>

<Note>
  客户会发现分配的端口从编号 20000 开始向上。但是,我们建议您不要在脚本中硬编码此值,因为它将来可能会更改,并始终使用环境变量以获取正确的端口信息。
</Note>

## 开发/调试

在使用 VmStartupScript 功能之前,我们建议您查看我们在 GitHub 上开源存储库 ([VmStartupScriptGallery](https://github.com/PlayFab/VmStartupScriptGallery)) 上的这些示例脚本。欢迎贡献!

### 推荐的开发工作流

最初,您应该使用单个 VM 创建一个测试构建。此 VM 应具有与您计划部署生产构建的类似规格。当此单个 VM 部署时,您可以通过 RDP/SSH 连接,复制必要的文件并尝试编辑/运行脚本,直到成功。

一旦此 VM 启动并运行,并且您验证您的脚本按预期运行,您可以将脚本和资产放入 .zip 文件中。之后,您可以上传它并尝试使用它创建构建。再次尝试创建单个 VM 构建以节省成本,一旦您确信您的脚本可以工作,就可以扩容。

如果您在运行脚本时遇到挑战,可以通过 RDP/SSH 登录 VM 进行调试,并分别检查 **PF\_StartupScriptStdOut.txt** 和 **PF\_StartupScriptStdErr.txt** 文件以获取脚本的标准输出和标准错误流。这些文件位于 Windows 上的 D: 驱动器或 Linux 上的 /mnt 上。

脚本应该是幂等的,因为有可能会被执行多次。例如,如果脚本尝试下载外部资源,但由于网络问题而失败,MPS 将重试整个脚本执行。

## 支持

MPS 服务将运行您在 VmStartupScript 上拥有的任何内容。但是,团队不会为作为脚本一部分安装/执行的单个操作和可执行文件提供支持。

在预览期间,使用 [PlayFab 社区论坛](https://community.playfab.com/) 和 [Discord](https://aka.ms/msftgamedevdiscord) 获取支持并提供反馈。如果您对 VmStartupScriptGallery 存储库中的任何脚本有问题或想要请求新脚本,请在 GitHub 上[打开一个 issue](https://github.com/PlayFab/VmStartupScriptGallery/issues)。


## Related topics

- [管理机密(预览版)](/zh-CN/services/playfab/multiplayer/servers/manage-secrets.md)
- [脚本创作工具](/zh-CN/tools/tools-console/wdp/script-authoring-tool.md)
- [使用 GDKX 构建并运行你的第一款主机游戏](/zh-CN/home/build-first-title/first-console-title-walkthrough.md)
- [自定义安装操作](/zh-CN/build/core-features/common/packaging/packaging-custom-install-actions.md)
- [创建大厅](/zh-CN/services/playfab/multiplayer/lobby/create-a-lobby.md)
