Snowpark Container Services:使用服务

Snowpark Container Services 使您能够轻松部署、管理和扩展容器化应用程序。创建应用程序并将应用程序映像上传到 Snowflake 账户中的存储库后,您就可以将应用程序容器作为服务运行。

服务表示 Snowflake 在计算池上运行您的容器化应用程序。服务分为两类:

  • 长期运行的服务。 长期运行的服务就像不会自动结束的 Web 服务。创建服务后,Snowflake 会管理正在运行的服务。例如,如果某个服务容器出于某种原因停止,Snowflake 会重新启动该容器,以便服务不间断地运行。

  • 作业服务。 作业服务会在代码退出时终止,与存储过程类似。当所有容器都退出时,作业服务便已完成。

如果您的服务需要更多的资源,例如更多的计算能力,Snowflake 会在 计算池 中预置额外的节点。

Snowpark Container Services 提供了一组 SQL 命令,您可以使用这些命令创建并管理服务。这些对象包括:

启动服务

启动服务所需的最低限度信息包括:

  • 名称: 服务的名称。

  • 服务规范:规范 为 Snowflake 提供运行服务所需的信息。规范是一个 YAML 文件。

  • 计算池: Snowflake 在指定的 计算池 中运行您的服务。

创建长期服务

使用 CREATE SERVICE 创建长期运行的服务。

  • 使用内联规范创建服务。大多数情况下,在开发过程中,您可以选择内联规范,如下所示:

    CREATE SERVICE echo_service
       IN COMPUTE POOL tutorial_compute_pool
       FROM SPECIFICATION $$
       spec:
         containers:
         - name: echo
           image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:tutorial
           readinessProbe:
             port: 8000
             path: /healthcheck
         endpoints:
         - name: echoendpoint
           port: 8000
           public: true
       $$;
    
    Copy
  • 使用暂存区信息创建服务。在生产环境中部署服务时,建议应用关注点分离设计原则,并将规范上传到一个暂存区,提供暂存区信息 CREATE SERVICE 命令,如下所示:

    CREATE SERVICE echo_service
      IN COMPUTE POOL tutorial_compute_pool
      FROM @tutorial_stage
      SPECIFICATION_FILE='echo_spec.yaml';
    
    Copy

执行作业服务

使用 EXECUTE JOB SERVICE 创建作业服务。此命令同步运行,在作业服务的所有容器退出后返回响应。

  • 使用内联规范执行作业服务:

    EXECUTE JOB SERVICE
       IN COMPUTE POOL tutorial_compute_pool
       NAME = example_job_service
       FROM SPECIFICATION $$
       spec:
         container:
         - name: main
           image: /tutorial_db/data_schema/tutorial_repository/my_job_image:latest
           env:
             SNOWFLAKE_WAREHOUSE: tutorial_warehouse
           args:
           - "--query=select current_time() as time,'hello'"
           - "--result_table=results"
       $$;
    
    Copy
  • 使用暂存区信息执行作业服务:

    EXECUTE JOB SERVICE
      IN COMPUTE POOL tutorial_compute_pool
      NAME = example_job_service
      FROM @tutorial_stage
      SPECIFICATION_FILE='my_job_spec.yaml';
    
    Copy

创建多个服务实例并配置自动扩缩

默认情况下,Snowflake 会在指定的计算池中运行一个服务实例。要管理繁重的工作负载,您可以通过设置 MIN_INSTANCES 和 MAX_INSTANCES 属性运行多个服务实例,这两个属性指定了服务启动时的最小实例数和需要时 Snowflake 可扩展到的最大实例数。

备注

一个作业服务只能有一个实例正在运行。

示例

CREATE SERVICE echo_service
   IN COMPUTE POOL tutorial_compute_pool
   FROM @tutorial_stage
   SPECIFICATION_FILE='echo_spec.yaml'
   MIN_INSTANCES=2
   MAX_INSTANCES=4;
Copy

当多个服务实例正在运行时,Snowflake 会自动提供一个负载均衡器来分配传入请求。

要配置 Snowflake 以自动扩缩正在运行的服务实例数量,请按照以下步骤操作:

  1. 在服务规范文件中指定服务实例的内存和 CPU 要求。有关详细信息,请参阅 container.resources 字段。

    示例

    resources:
      requests:
       memory: <memory-reserved>
       cpu: <cpu-units>
    
    Copy
  2. 运行 CREATE SERVICE 命令时,设置 MIN_INSTANCES 和 MAX_INSTANCES 参数。您还可以使用 ALTER SERVICE 更改这些值。

Snowflake 会首先在指定的计算池上创建最少数量的服务实例,然后根据需要自动调整实例数量。Snowflake 使用 80% 的阈值(对于 CPU 和内存)来增加或减少服务实例的数量。

任何时候,指定的计算池上都可以运行一个或多个服务实例。Snowflake 可持续监控计算池内的资源利用率(内存或 CPU),汇总当前运行的所有服务实例的使用量数据。

当汇总资源使用量(所有服务实例)超过 80% 时,Snowflake 会在计算池中部署额外的服务实例。如果汇总资源使用量低于 80%,Snowflake 会通过移除一个正在运行的服务实例来缩小规模。Snowflake 使用五分钟的稳定窗口来防止频繁扩缩。

请注意以下扩缩行为:

  • 服务实例的扩缩受限于为服务配置的 MIN_INSTANCES 和 MAX_INSTANCES 参数。

  • 如果需要扩大规模,而计算池节点缺乏启动另一个服务实例所需的资源能力,则可以触发计算池自动扩缩。有关更多信息,请参阅 计算池节点的自动扩缩

  • 如果您在创建服务时指定了 MAX_INSTANCES 和MIN_INSTANCES 参数,但没有在服务规范文件中指定服务实例的内存和 CPU 要求,则不会发生自动扩缩;Snowflake 会以 MIN_INSTANCES 属性指定的实例数启动,不会自动扩缩。

修改和删除服务

创建服务后,您可以使用 DROP SERVICE 从架构中移除服务(Snowflake 会终止所有服务容器)。

您还可以使用 ALTER SERVICE 命令修改服务。例如,暂停或恢复服务,更改正在运行的实例数量,以及指示 Snowflake 使用新的服务规范重新部署您的服务。

备注

您不能更改作业服务。

更新服务代码,重新部署服务

您可以修改应用程序代码和配置,并在 ALTER SERVICE 命令中提供新的服务规范,指示 Snowflake 使用新规范重新部署正在运行的服务。

您首先要将修改后的应用程序代码上传到镜像仓库,然后调用 ALTER SERVICE,以提供内联服务规范或在 Snowflake 暂存区的文件中指定规范文件路径。例如:

  • 提供内联规范(显示部分规范)。

    ALTER SERVICE echo_service
    FROM SPECIFICATION $$
    spec:
      
      
    $$;
    
    Copy
  • 提供 Snowflake 暂存区文件路径:

    ALTER SERVICE echo_service
    FROM @tutorial_stage
    SPECIFICATION_FILE='echo_spec.yaml';
    
    Copy

收到请求后,Snowflake 会使用新代码重新部署服务。

ALTER SERVICE 始终使用仓库中最新版本的镜像。例如,如果您上传名为“echo_service:latest”或“echo_service:sometag”的多个版本的镜像,Snowflake 将使用最新上传的镜像版本。

您可以使用 DESCRIBE SERVICE 命令查找服务正在运行的镜像版本。如果您是服务所有者,DESCRIBE SERVICE 输出包括服务规范,您可以在其中获取镜像摘要(镜像的 SHA256)。例如:

spec:
 containers:
 - name: "echo"
    image: "orgname-acctname.registry-dev.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest"
    sha256: "@sha256:8d912284f935ecf6c4753f42016777e09e3893eed61218b2960f782ef2b367af"
    env:
       SERVER_PORT: "8000"
       CHARACTER_NAME: "Bob"
    readinessProbe:
       port: 8000
       path: "/healthcheck"
 endpoints:
 - name: "echoendpoint"
    port: 8000
    public: true
Copy

备注

  • 如果暂停和恢复服务,Snowflake 会部署相同的镜像版本;这不是服务更新操作。

  • 您可以更新服务代码,但不能更新作业服务。

获取服务信息

您可以使用 DESCRIBE SERVICE 获取服务(包括作业服务)的属性。您还可以使用 SHOW SERVICES 列出当前数据库和架构中您拥有权限的当前服务(包括作业服务)。您还可以添加筛选器来限定 SHOW SERVICES 输出的范围:

  • 通过对象的 SQL 上下文(账户、数据库或架构)限制输出: 例如,使用 IN ACCOUNT 筛选器列出 Snowflake 账户中的服务,而不管服务属于哪个数据库或架构。如果您在账户中的多个数据库和架构中创建了 Snowflake 服务,这将非常有用。与所有其他命令一样,SHOW SERVICES IN ACCOUNTS 也受权限限制,仅返回所使用角色有查看权限的服务。

    您还可以指定 IN DATABASE 或 IN SCHEMA,以列出当前(或指定)数据库或架构中的服务。

  • 通过计算池限制输出: 例如,使用 IN COMPUTE POOL 筛选器列出计算池中运行的服务。

  • 按服务名称模式限制输出: 您可以应用 LIKE 和 STARTS WITH 筛选器,按名称筛选服务。

  • 限制输出以只返回或排除作业服务: 您可以使用 SHOW JOB SERVICES 或 SHOW SERVICES EXCLUDE JOBS 只列出作业服务或排除作业服务。

您还可以将这些选项组合起来,自定义 SHOW SERVICES 输出。

监控服务

使用 SYSTEM$GET_SERVICE_STATUS 获取服务或作业服务的详细运行时状态。例如,您可以调用此函数来确定服务是否仍在运行,或者服务是否启动失败。如果服务启动失败,该函数会提供有关失败的更多详细信息。

调用此函数时,请输入服务的名称。

示例

CALL SYSTEM$GET_SERVICE_STATUS('echo_service');
Copy

以下是输出示例:

  • 运行一个实例而且拥有一个容器的服务的输出示例:

    [
       {
          "status":"READY",
          "message":"Running",
          "containerName":"echo",
          "instanceId":"0",
          "serviceName":"ECHO_SERVICE",
          "image":"<account>.registry.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_echo_service_image:tutorial",
          "restartCount":0,
          "startTime":"2023-01-01T00:00:00Z"
       }
    ]
    
    Copy

    instanceId 是容器所属的服务实例 ID。 如果运行此服务的两个实例,输出结果将包括两个此类对象,并提供每个服务实例的容器状态。在这种情况下,实例 IDs 是 0 和 1。

    请注意,为方便起见,前面的输出是格式化的。您可以使用 PARSE_JSON 函数获取 SYSTEM$GET_SERVICE_STATUS 的格式化输出。

    SELECT PARSE_JSON(SYSTEM$GET_SERVICE_STATUS('echo_service'));
    
    Copy
  • 运行一个实例而且拥有三个容器的服务的输出示例:

    [
       {
          "status":"READY",
          "message":"Running",
          "containerName":"some-proxy",
          "instanceId":"0",
          "serviceName":"EXAMPLE_SERVICE",
          "image":"<account>.registry.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_service_image_proxy:tutorial",
          "restartCount":0,
          "startTime":"2023-01-01T00:00:00Z"
       },
       {
          "status":"READY",
          "message":"Running",
          "containerName":"some-server",
          "instanceId":"0",
          "serviceName":"EXAMPLE_SERVICE",
          "image":"<account>.registry.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_server:tutorial",
          "restartCount":0,
          "startTime":"2023-01-01T00:00:00Z"
       },
       {
          "status":"READY",
          "message":"Running",
          "containerName":"movies",
          "instanceId":"0",
          "serviceName":"EXAMPLE_SERVICE",
          "image":"<account>.registry.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_movies:tutorial",
          "restartCount":0,
          "startTime":"2023-01-01T00:00:00Z"
       }
    ]
    
    Copy

    输出结果包括每个容器的状态信息。所有这些容器都属于一个服务实例,实例 ID 为 0。

SYSTEM$GET_SERVICE_STATUS 需要一个可选的 timeout_secs 实参。如果未指定 timeout_secs 或指定的值为 0,函数将立即返回当前状态。如果指定了 timeout_secs,Snowflake 会等待服务在指定时间内达到终端状态(READY 或 FAILED),然后才返回服务状态。如果服务在指定时间内未达到终端状态,Snowflake 会在指定的时间间隔结束时返回当前状态。

您指定的超时时间是根据您期望服务就绪的时间得出的近似值,这有助于识别一些情况,例如 Snowflake 正在等待其他服务和作业停止并释放指定计算池中的资源,然后再启动另一项服务。下面的 SYSTEM$GET_SERVICE_STATUS 函数指定了 10 秒的超时时间:

call SYSTEM$GET_SERVICE_STATUS('echo_service', 10);
Copy

访问本地容器日志

Snowflake 会收集应用程序容器输出到标准输出或标准错误的内容。您应确保代码输出有用的信息,以便调试服务。

Snowflake 提供了两种访问这些服务(包括作业服务)容器日志的方法:

  • 使用 SYSTEM$GET_SERVICE_LOGS 系统函数: 访问特定容器中的日志。容器退出后,您可以继续使用系统函数短时间访问日志。系统函数在开发和测试过程中,即在最初授权服务或作业时最有用。有关更多信息,请参阅 SYSTEM$GET_SERVICE_LOGS

  • 使用事件表: 事件表可让您访问所有服务中多个容器的日志。Snowflake 会将日志持久保存在事件表中,以便日后访问。事件表最适合用于服务和作业的回顾分析。有关更多信息,请参阅 使用事件表

使用 SYSTEM$GET_SERVICE_LOGS

您需要提供服务名称、实例 ID、容器名称,还可选择提供要检索的最新日志行数。如果只有一个服务实例正在运行,则服务实例 ID 为 0。例如,以下 GET_SERVICE_LOGS 命令会从名为``echo`` 的容器的日志中检索末尾的 10 行,该容器属于名为 echo_service 的服务的实例 0:

CALL SYSTEM$GET_SERVICE_LOGS('echo_service', '0', 'echo', 10);
Copy

如果不知道服务信息(如实例 ID 或容器名称),可以先运行 GET_SERVICE_STATUS 获取每个实例中运行的服务实例和容器的信息。

输出示例:

+--------------------------------------------------------------------------+
| SYSTEM$GET_SERVICE_LOGS                                                  |
|--------------------------------------------------------------------------|
| 10.16.6.163 - - [11/Apr/2023 21:44:03] "GET /healthcheck HTTP/1.1" 200 - |
| 10.16.6.163 - - [11/Apr/2023 21:44:08] "GET /healthcheck HTTP/1.1" 200 - |
| 10.16.6.163 - - [11/Apr/2023 21:44:13] "GET /healthcheck HTTP/1.1" 200 - |
| 10.16.6.163 - - [11/Apr/2023 21:44:18] "GET /healthcheck HTTP/1.1" 200 - |
+--------------------------------------------------------------------------+
1 Row(s) produced. Time Elapsed: 0.878s

SYSTEM$GET_SERVICE_LOGS 输出有以下限制:

  • 它只是简单地合并了标准输出和标准错误流,从而无法区分常规输出和错误消息。

  • 它在单个服务实例中报告特定容器的捕获数据。

  • 它只报告运行中容器的日志。该函数无法从先前重新启动的容器或者已停止或已删除的服务容器中获取日志。

  • 该函数最多可返回 100 KB 数据。

使用事件表

Snowflake 可以从您的容器中捕获标准输出和标准错误,并将其记录到为您的账户配置的事件表中。有关更多信息,请参阅 日志记录和跟踪概述

例如,以下 SELECT 查询可检索过去一小时内记录的 Snowflake 服务和作业事件:

SELECT TIMESTAMP, RESOURCE_ATTRIBUTES, RECORD_ATTRIBUTES, VALUE
FROM <current-event-table-for-your-account>
WHERE timestamp > dateadd(hour, -1, current_timestamp())
AND RESOURCE_ATTRIBUTES:"snow.executable.type" = 'SnowparkContainers'
ORDER BY timestamp DESC
LIMIT 10;
Copy

Snowflake 建议您在事件表查询的 WHERE 子句中包含时间戳,如本例所示。这一点尤为重要,因为各种 Snowflake 组件可能会生成大量数据。通过应用筛选器,可以检索到较小的数据子集,从而提高查询性能。

您可以通过使用服务规范文件中的 spec.logExporters 字段 来控制希望在事件表中持久化的日志级别(全部、仅错误或无)。

事件表包括以下列,这些列提供了有关 Snowflake 从您的容器收集的日志的有用信息:

  • TIMESTAMP: 显示 Snowflake 收集日志的时间。

  • RESOURCE_ATTRIBUTES: 此列中的每个对象都标识了生成日志的 Snowflake 服务和服务中的容器。例如,它提供了服务运行时指定的服务名称、容器名称和计算池名称等详细信息。

    {
    "snow.containers.compute_pool.id": 549816068,
    "snow.containers.compute_pool.name": "TUTORIAL_COMPUTE_POOL",
    "snow.containers.container.name": "echo",
    "snow.containers.instance.name": "0",
    "snow.containers.restart.id": "cd0cf2",
    "snow.database.id": 549816076,
    "snow.database.name": "TUTORIAL_DB",
    "snow.executable.id": 980991015,
    "snow.executable.name": "ECHO_SERVICE",
    "snow.executable.type": "SnowparkContainers",
    "snow.schema.id": 549816076,
    "snow.schema.name": "DATA_SCHEMA"
    }
    
    Copy
  • RECORD_ATTRIBUTES: 对于 Snowflake 服务,它标识错误源(标准输出或标准错误)。

    { "log.iostream": "stdout" }
    
    Copy
  • VALUE: 标准输出和标准错误被分成若干行,每一行都会在事件表中生成一条记录。

    "echo-service [2023-10-23 17:52:27,429] [DEBUG] Sending response: {'data': [[0, 'Joe said hello!']]}"
    

配置事件表

有关更多信息,请参阅 日志记录和跟踪概述

访问计算池指标

您的服务在您提供的 Snowflake 计算池上运行。每个计算池都会发布一组有关计算池中节点(例如,节点上可供容器使用的空闲内存量)和该计算池中运行的容器(例如,特定容器使用的内存)的指标。

您可以使用发布的指标来了解服务在计算池中的哪个位置运行,以及服务正在使用哪些资源。有关更多信息,请参阅 计算池指标

管理对服务端点的访问

小心

在生产环境中,您可以使用 GRANT <privileges> 命令,为某个角色授予对服务的 USAGE 权限:

GRANT USAGE ON SERVICE <service_name> TO ROLE <role_name>
Copy

但是,如果您的账户已启用 2024_04 行为变更捆绑包,则前面的 GRANT 命令将不起作用。现在必须在特定服务端点上授予角色权限,而不是授予服务级权限。您可以使用服务角色的概念来管理端点级权限,本节将对此进行说明。

服务 所有者 角色(您用来创建服务的角色)对服务本身和服务所公开的端点具有完全访问权限。其他角色需要对端点具有 USAGE 权限才能与服务通信。例如:

  • 客户端的所有者角色需要对端点拥有 USAGE 权限。客户端 指的是向另一个服务的端点发出请求的服务函数或服务。

    • 要创建引用端点的 服务函数,用户需要对端点具有访问权限。也就是说,服务函数的所有者角色需要对在 CREATE FUNCTION 中引用的端点具有 USAGE 权限。

    • 服务到服务通信 中,客户端服务的所有者角色(即调用其他服务的端点)需要对端点具有 USAGE 权限。

  • 从 Snowflake 外部向公共端点发出 入口 请求的用户需要对端点具有 USAGE 权限。

向其他角色授予对服务端点的权限需要执行两个步骤:

  1. 服务规范 中定义一个或多个 服务角色。在定义中,指明该角色对其具有 USAGE 权限的端点。

  2. 创建服务后,使用以下命令向其他角色授予(或撤消)服务角色:GRANT SERVICE ROLEREVOKE SERVICE ROLE。您还可以使用以下命令显示有关授权的信息:SHOW ROLES IN SERVICESHOW GRANTS

在您创建服务时,Snowflake 会创建服务角色;在您删除服务时,会删除服务角色。Snowflake 还定义了一个默认服务角色 (ALL_ENDPOINTS_USAGE),向服务公开的所有端点授予 USAGE 权限,并将此默认服务角色授予服务的所有者角色。因此,所有者角色可以访问服务公开的所有端点。如果使用同一角色创建多个服务,由于所有者角色可以访问所有端点,因此这些服务之间可以无缝通信,无需额外配置。

请注意,如果一个服务有多个容器,它们可以通过本地主机相互通信,这些通信是每个服务实例内部的本地通信,不受基于角色的访问控制的限制。

以下各节提供了详细信息。您也可以尝试学习一个教程(配置和测试服务端点权限),其中提供了分步探索此功能的说明。

使用此功能的前提条件

要使用服务角色管理端点访问,必须在账户中启用 2024_04 行为变更捆绑包

在账户中启用此捆绑包,请执行以下语句:

SELECT SYSTEM$ENABLE_BEHAVIOR_CHANGE_BUNDLE('2024_04');
Copy

为帮助您采用这些变更,Snowflake 已为每个现有服务创建了默认服务角色 (ALL_ENDPOINTS_USAGE)。Snowflake 还将默认服务角色授予了您之前向其授予对该服务的 USAGE 权限的其他角色,因此现有服务应该可以无缝运行。

不过,某些问题可能会阻止 Snowflake 创建默认服务角色。您可以运行以下 SHOW ROLES IN SERVICE 命令来验证是否为您的服务创建了默认服务角色。

SHOW ROLES IN SERVICE <service_name>;
Copy

输出示例:

+-------------------------------+-------------------------+------------+
| created_on                  |   name                       |  comment   |
+-------------------------------+-------------------------+------------+
| 2024-04-29 14:58:50.063 -0700 |   ALL_ENDPOINTS_USAGE   |            |
+-------------------------------+-------------------------+------------+

检查输出;如果输出包含默认服务角色 (ALL_ENDPOINTS_USAGE),则无需进一步操作。否则,请执行以下操作以创建默认服务角色并将其授予其他角色:

  1. 运行 ALTER SERVICE 命令来升级服务 ...

    ALTER SERVICE <name>
    {
       FROM @<stage>
       SPECIFICATION_FILE = '<yaml_file_stage_path>'
       |
       FROM SPECIFICATION <specification_text>
    }
    
    Copy
  2. 使用 GRANT SERVICE ROLE 命令将默认服务角色 (ALL_ENDPOINTS_USAGE) 授予您先前向其授予对服务的 USAGE 权限的角色。

    GRANT SERVICE ROLE <service-name>!ALL_ENDPOINTS_USAGE TO ROLE <another_role>;
    
    Copy

向所有端点授予 USAGE 权限

创建服务(包括作业服务)时,Snowflake 还会创建默认服务角色,名为 ALL_ENDPOINTS_USAGE。此角色对服务公开的所有端点都拥有 USAGE 权限。您可以使用 GRANT SERVICE ROLE 命令将此默认服务角色授予其他角色:

GRANT SERVICE ROLE my_echo_service_image!ALL_ENDPOINTS_USAGE TO ROLE some_other_role;
Copy

使用 some_other_role 的用户对所有服务端点都拥有 USAGE 权限。

删除服务时,Snowflake 会删除与服务相关联的所有服务角色(默认服务角色和服务规范中定义的服务角色),并取消所有服务角色授予。

向特定端点授予 USAGE 权限

使用服务角色管理对服务端点的精细访问。您可以在服务规范中定义服务角色,以及授予其 USAGE 权限的端点列表。

向服务的特定端点授予权限分为两个步骤:

  1. 定义服务角色: 使用服务规范来定义服务角色,方法是提供角色名称,以及包含一个或多个您像向其授予 USAGE 权限的端点的列表。例如,在下面的规范片段中,顶级 serviceRoles 字段定义了两个服务角色,每个角色都对特定端点拥有 USAGE 权限。

    spec:
    ...
    serviceRoles:                 # Optional list of service roles
    - name: <svc_role_name1>
      endpoints:                  # endpoints that role can access
      - <endpoint_name1>
      - <endpoint_name2>
    - name: <svc_role_name2>
      endpoints:
      - <endpoint_name3>
      - <endpoint_name4>
    
    Copy
  2. 将服务角色授予其他角色。 使用 GRANT SERVICE ROLE 命令,可将服务角色授予其他角色(账户角色、应用程序角色或数据库角色)。例如:

    GRANT SERVICE ROLE <service-name>!<svc_role_name1> TO ROLE <another-role>
    
    Copy

使用服务

创建服务后,您可以使用以下三种受支持的方法中的任意一种与之通信:

  • 服务函数:创建与服务相关联的服务函数 (UDF)。然后,使用服务函数与来自 SQL 查询的服务进行通信。您只能将服务函数与 HTTP 或 HTTPS 端点关联。有关示例,请参阅 教程 1

  • 入口:使用服务公开的公共端点从 Snowflake 外部访问服务。在这种情况下,Snowflake 会管理访问控制。只允许通过 HTTP 或 HTTPS 端点使用入口。有关示例,请参阅 教程 1

  • 服务到服务通信:使用 Snowflake 分配的服务 DNS 名称进行服务到服务通信。如果创建端点只是为了实现服务到服务通信,则应使用 TCP 协议。有关示例,请参阅 教程 3

备注

作业服务不支持服务函数和入口。作业服务像作业一样运行,并在完成后终止。不需要服务函数即可与之通信,因此不支持将服务函数与作业服务关联起来。此外,作业服务不支持入口。也就是说,如果规范文件包含公共端点,EXECUTE JOB SERVICE 将失败。

支持服务到服务(包括作业服务)通信。

以下各节提供了详细信息。

服务函数:使用 SQL 查询中的服务

服务函数是使用 CREATE FUNCTION 创建的用户定义函数 (UDF)。不过,您不用直接编写 UDF 代码,而是将 UDF 与您的服务关联起来。例如,在 教程 1 中,您创建了一个名为 echo_service 的服务,它公开了服务规范中定义的一个端点 (echoendoint):

spec:

  endpoints:
  - name: echoendpoint
    port: 8080
Copy

echoendpoint 是一个用户友好型端点名称,代表相应的端口 (8080)。要与此服务端点通信,需要提供 SERVICE 和ENDPOINT 参数,创建一个服务函数,如下所示:

CREATE FUNCTION my_echo_udf (text varchar)
   RETURNS varchar
   SERVICE=echo_service
   ENDPOINT=echoendpoint
   AS '/echo';
Copy

AS 参数提供服务代码的 HTTP 路径。您可以从服务代码中获取该路径值(例如,请参阅 教程 1 中的 service.py)。

@app.post("/echo")
def echo():
...
Copy

调用服务函数时,Snowflake 会将请求定向到相关联的服务端点和路径。

备注

服务函数用于与服务通信,而不是与作业通信。换句话说,您只能将服务(而不是作业)与服务函数关联起来。

当您运行服务的多个实例时,可以通过指定可选的 MAX_BATCH_ROWS 参数来创建服务函数,以限制 批量大小,即 Snowflake 批量发送到服务的最大行数。例如,假设 MAX_BATCH_ROWS 为 10,您调用 my_echo_udf 服务函数时输入 100 行。Snowflake 会将输入的行分成多个批次,每个批次最多有 10 行,然后向服务发送一系列请求,请求正文中包含批次中的行。当处理耗时过长时,配置批次大小会有所帮助,将行分布在所有可用服务器上也会有所帮助。

您可以使用 ALTER FUNCTION 更改服务函数。以下 ALTER FUNCTION 命令更改了与之关联的服务端点和批次大小:

ALTER FUNCTION my_echo_udf(VARCHAR)
   SET SERVICE=other_service
   ENDPOINT=otherendpoint
   MAX_BATCH_ROWS=100
Copy

数据交换格式

对于服务函数和容器之间的数据交换,Snowflake 遵循外部函数使用的相同格式(请参阅 数据格式)。例如,假设有数据行存储在表 (input_table) 中:

"Alex", "2014-01-01 16:00:00"
"Steve", "2015-01-01 16:00:00"

要将这些数据发送到服务,您需要将这些行作为参数传递,从而调用服务函数:

SELECT service_func(col1, col2) FROM input_table;
Copy

Snowflake 会向容器发送一系列请求,请求正文中的数据行批次格式如下:

{
   "data":[
      [
         0,
         "Alex",
         "2014-01-01 16:00:00"
      ],
      [
         1,
         "Steve",
         "2015-01-01 16:00:00"
      ],
      …
      [
         <row_index>,
         "<column1>",
         "<column2>"
      ],
   ]
}
Copy

然后,容器会以以下格式返回输出结果:

{
   "data":[
      [0, "a"],
      [1, "b"],
      …
      [ row_index,  output_column1]
   ]
}
Copy

所示输出示例假定结果是一个单列表,包含行(“a”、“b”...)。

当运行多个服务实例时,可以使用 MAX_BATCH_ROWS 参数创建一个服务函数,将输入行分配到所有可用服务器进行处理。

创建和管理服务函数所需的权限

要创建和管理服务函数,角色需要具备以下权限:

  • 创建服务函数: 当前角色必须对正在引用的服务具有 USAGE 权限。

  • 更改服务函数: 您可以更改服务函数并将其与其他服务关联。当前角色必须对新服务具有 USAGE 权限。

  • 使用服务函数: 当前角色必须对服务函数具有 USAGE 权限,服务函数所有者角色必须对相关服务具有 USAGE 权限。

以下示例脚本显示了如何授予使用服务函数的权限:

USE ROLE service_owner;
GRANT USAGE ON service service_db.my_schema.my_service TO ROLE func_owner;

USE ROLE func_owner;
CREATE OR REPLACE test_udf(v VARCHAR)
  RETURNS VARCHAR
  SERVICE=service_db.my_schema.my_service
  ENDPOINT=endpointname1
  AS '/run';

SELECT test_udf(col1) FROM some_table;

ALTER FUNCTION test_udf(VARCHAR) SET
  SERVICE = service_db.other_schema.other_service
  ENDPOINT=anotherendpoint;

GRANT USAGE ON FUNCTION test_udf(varchar) TO ROLE func_user;
USE ROLE func_user;
SELECT my_test_udf('abcd');
Copy

入口:使用来自 Snowflake 外部的服务

一项服务可以公开一个或多个端点,允许用户从公共网络使用该服务。

备注

对于公开/非公开预览,您的 Snowflake 账户的 ACCOUNTADMIN 必须执行以下命令:

CREATE SECURITY INTEGRATION SNOWSERVICES_INGRESS_OAUTH
TYPE=oauth
OAUTH_CLIENT=snowservices_ingress
ENABLED=true;
Copy

在服务规范文件中将端点标记为公共端点:

spec
  ...
  endpoints
  - name: <endpoint name>
    port: <port number>
    public: true
Copy

有关服务规范的更多信息,请参阅 规范参考

公共端点访问和身份验证

Snowpark Container Services 要求 Snowflake OAuth 对公共端点的请求进行身份验证。例如,您需要使用用户名和密码登录。在后台,您的登录会从 Snowflake 生成 OAuth 令牌。然后使用 OAuth 令牌向服务端点发送请求。

备注

不是每个人都能访问服务公开的公共端点。只有同一 Snowflake 账户中拥有 USAGE 权限角色的用户才能访问服务的公共端点。

您可以使用浏览器或以编程方式访问公共端点:

  • 使用浏览器访问公共端点: 使用浏览器访问公共端点时,会自动重定向用户身份验证。您可以探索 教程 1 来测试这种体验。

  • 以编程方式访问公共端点: 以下 Python 示例代码使用 Snowflake Connector for Python 首先生成一个代表您身份的会话令牌。然后,代码会使用会话令牌登录公共端点。

    import snowflake.connector
    import requests
    
    ctx = snowflake.connector.connect(
       user="<username>",# username
       password="<password>", # insert password here
       account="<orgname>-<acct-name>",
       session_parameters={
          'PYTHON_CONNECTOR_QUERY_RESULT_FORMAT': 'json'
       })
    
    # Obtain a session token.
    token_data = ctx._rest._token_request('ISSUE')
    token_extract = token_data['data']['sessionToken']
    
    # Create a request to the ingress endpoint with authz.
    token = f'\"{token_extract}\"'
    headers = {'Authorization': f'Snowflake Token={token}'}
    # Set this to the ingress endpoint URL for your service
    url = 'http://<ingress_url>'
    
    # Validate the connection.
    response = requests.get(f'{url}', headers=headers)
    print(response.text)
    
    # Insert your code to interact with the application here
    
    Copy

    在代码中:

    • 如果您不知道自己的账户信息 (<orgname>-<acctname>),请参阅教程 常用设置

    • 您可以使用 SHOW ENDPOINTS 获取服务公开的公共端点的 ingress_url

入口请求中的用户特定标头

当入口端点的请求到达时,Snowflake 会自动将以下标头连同 HTTP 请求一起传递给容器。

Sf-Context-Current-User: <user_name>
Copy

您的容器代码可以选择读取标头信息,知道调用者是谁,并为不同用户应用特定于上下文的自定义。此外,Snowflake 还可以选择包含 Sf-Context-Current-User-Email 标头。要包含此标头,请联系 ` Snowflake 支持部门 `_。

服务到服务通信

服务可使用 Snowflake 自动分配给每个服务的 DNS 名称相互通信。有关示例,请参阅 教程 3

DNS 名称格式为:

<service-name>.<schema-name>.<db-name>.snowflakecomputing.internal

使用 SHOW SERVICES(或 DESCRIBE SERVICE)获取服务的 DNS 名称。前面的 DNS 名称是全名。在同一架构下创建的服务可以只使用 <service-name> 进行通信。处于同一数据库但不同架构的服务必须提供架构名称,例如 <service-name>.<schema-name>

Snowflake 允许由同一角色创建的服务之间的网络通信,并阻止由不同角色创建的服务之间的网络通信。如果您想防止服务之间相互通信(出于安全等原因),请使用不同的 Snowflake 角色来创建这些服务。

DNS 名称有以下限制:

  • 您的数据库、架构或服务名称必须是有效的 DNS 标签。(另请参阅 ` <https://www.ietf.org/rfc/rfc1035.html#section-2.3.1 (https://www.ietf.org/rfc/rfc1035.html#section-2.3.1)> `_ )。否则,创建服务将失败。

  • Snowflake 将名称(数据库、架构和服务名称)中的下划线 (_) 替换为 DNS 名称中的短划线 (-)。

  • 创建服务后,请勿更改数据库或架构名称,因为 Snowflake 不会更新服务的 DNS 名称。

  • DNS 名称仅用于在同一账户中运行的服务之间的 Snowflake 内部通信。从互联网访问此名称。

权限

权限

用途

备注

USAGE

要与服务通信,您需要 对服务端点具有 USAGE 权限。创建服务函数、使用公共端点和从其他服务连接时需要。

MONITOR

监控服务并获取运行时状态。

OPERATE

暂停或恢复服务。

OWNERSHIP

完全控制服务。同一时间只有一个角色可以在特定对象上拥有此权限。

ALL [ PRIVILEGES ]

向服务授予所有权限,OWNERSHIP 除外。

语言: 中文