Snowpark Container Services:使用服务

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

服务表示 Snowflake 在 计算池 上运行您的容器化应用程序,计算池是虚拟机 (VM) 节点的集合。服务分为两类:

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

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

下图显示了服务的架构:

Snowpark Container Services 服务架构

该图表的亮点如下:

  • 用户将他们的应用程序代码上传到其 Snowflake 账户中的仓库。镜像注册表服务提供 OCIv2 API,用于在仓库中存储兼容 OCI 的镜像。例如,您可以使用 Docker API 将镜像上传到仓库。创建服务时,您需要指定要使用的镜像。

  • 计算池是 Snowflake 运行您的服务的地方。该图表显示了一个具有两个计算节点(节点 0 和节点 1)的计算池。Snowflake 在节点上运行您的服务实例。运行多个服务实例时,根据资源需求,Snowflake 可能会在同一个节点上运行它们,也可能将它们分布在多个节点上。例如:

    • 节点 0 正在运行服务 A(该服务总的三个实例中的两个实例)和一个作业(包含一个实例)。

    • 节点 1 正在运行服务 A 的第三个实例。该节点还运行服务 B 的一个实例。

  • 根据您的应用程序代码,服务实例可以由多个容器组成。虽然 Snowflake 可能会在多个计算池节点上分配一项服务的多个实例,但单个服务实例中的所有容器始终在同一个计算池节点上运行。

  • 服务可以选择与公共互联网通信。

  • 服务可以使用包括瞬态存储(例如,内存和本地磁盘)和永久卷(例如,块卷)在内的存储。

  • Snowflake 可以将服务中的日志、跟踪和指标记录到 Snowflake 账户中的事件表。

Snowflake 提供 APIs,以便您创建和管理仓库、计算池和服务。本主题介绍如何使用服务。用于管理服务的 APIs 包括以下内容:

启动服务

将应用程序代码上传到 Snowflake 账户中的 仓库 后,即可启动服务。启动服务所需的最低限度信息包括:

  • 名称: 服务的名称。

  • 服务规范:规范 为 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
  • 通过引用 Snowflake 暂存区上存储的服务规范来创建服务。在生产环境中部署服务时,您可以应用关注点分离设计原则,并将规范上传到一个暂存区,在 CREATE SERVICE 命令中提供暂存区信息,如下所示:

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

执行作业服务

使用 EXECUTE JOB SERVICE 创建作业服务。默认情况下,此命令同步运行,并在任务服务的所有容器退出后返回响应。您可以选择指定 ASYNC 参数以异步运行作业服务。

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

    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

    您可以选择使用该 ASYNC 属性异步执行此作业。

    EXECUTE JOB SERVICE
       IN COMPUTE POOL tutorial_compute_pool
       NAME = example_job_service
       ASYNC = TRUE
       FROM SPECIFICATION $$
       ...
       $$;
    
    Copy
  • 使用暂存区信息执行作业服务:

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

使用规范模板

有时,您可能希望使用相同的规范创建多项服务,但采用不同的配置。例如,假设您以一种服务规范定义了一个 环境变量,并且您希望使用相同的规范创建多项服务,但环境变量的值不同。

借助规范模板,您可以为规范中的字段值定义变量。在创建服务时,您需要为这些变量提供值。

在规范模板中,您可以将变量指定为各种规范字段的值。使用 {{ variable_name }} 语法来指定这些变量。然后,在 CREATE SERVICE 命令中,指定 USING 参数来设置这些变量的值。

例如,以下 CREATE SERVICE 命令中的内联规范模板为镜像标签名称使用名为 tag_name 的变量。您可以使用此变量为每项服务指定不同的镜像标签。在此示例中,USING 参数将 tag_name 变量设置为值 latest

CREATE SERVICE echo_service
  IN COMPUTE POOL tutorial_compute_pool
  FROM SPECIFICATION $$
  spec:
    containers:
    - name: echo
      image: myorg-myacct.registry.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_echo_service_image:{{ tag_name }}
        ...
    endpoints:
    - name: ...
      ...
  $$
  USING (tag_name=>'latest');
Copy

如果您选择将规范模板保存到账户中的 Snowflake 暂存区,您可以在 CREATE SERVICE 命令中指向模板的位置:

CREATE SERVICE echo_service
    IN COMPUTE POOL tutorial_compute_pool
    FROM @STAGE SPECIFICATION_TEMPLATE_FILE='echo.yaml'
    USING (tag_name=>'latest');
Copy

在规范中定义变量的准则

  • 使用 {{ variable_name }} 语法将变量定义为规范中的字段值。

  • 这些变量可以具有默认值。要指定默认值,请在变量声明中使用 default 函数。例如,以下规范定义具有默认值的两个变量(character_nameendpoint_name)。

    spec:
      containers:
      - name: echo
        image: <image_name>
        env:
          CHARACTER_NAME: {{ character_name | default('Bob') }}
          SERVER_PORT: 8085
      endpoints:
      - name: {{ endpoint_name | default('echo-endpoint') }}
        port: 8085
    
    Copy

    此外,您还可以为 default 函数指定可选布尔参数,以指示是否要在为变量传递空白值时使用默认值。请考虑以下规范:

    spec:
      containers:
      - name: echo
        image: <image_name>
        env:
          CHARACTER_NAME: {{ character_name | default('Bob', false) }}
          SERVER_PORT: 8085
      endpoints:
      - name: {{ endpoint_name | default('echo-endpoint', true) }}
        port: 8085
    
    Copy

    在规范中:

    • 对于 character_name 变量,布尔参数设置为 false。因此,如果变量设置为此参数的空字符串值 (''),则该值将保留空值;不使用默认值(“Bob”)。

    • 对于 echo_endpoint 变量,布尔参数设置为 true。因此,如果向此参数传递空值,则使用默认值(“echo-endpoint”)。

    默认情况下,default 函数的布尔参数为 false

为规范变量传递值的准则

在 CREATE SERVICE 命令中指定 USING 参数,为变量提供值。USING 的一般语法是:

USING( var_name=>var_value, [var_name=>var_value, ... ] );
Copy

其中

  • var_name 区分大小写,并且应该是有效的 Snowflake 标识符(请参阅 标识符要求)。

  • var_value 可以是字母数字值或有效 JSON 值。

    示例:

    -- Alphanumeric string and literal values
    USING(some_alphanumeric_var=>'blah123',
          some_int_var=>111,
          some_bool_var=>true,
          some_float_var=>-1.2)
    
    -- JSON string
    USING(some_json_var=>' "/path/file.txt" ')
    
    -- JSON map
    USING(env_values=>'{"SERVER_PORT": 8000, "CHARACTER_NAME": "Bob"}' );
    
    -- JSON list
    USING (ARGS='["-n", 2]' );
    
    Copy
  • CREATE SERVICE 中的 USING 参数必须为规范变量提供值(规范为其提供默认值的变量除外)。否则,返回错误。

示例

以下示例显示如何使用规范模板创建服务。这些示例中的 CREATE SERVICE 命令使用内联规范。

示例 1:提供简单值

教程 1 中,您可以通过提供内联规范来创建服务。以下示例是该示例的修改版本,其中规范定义了两个变量:image_urlSERVER_PORT。请注意,SERVER_PORT 变量在三个地方重复使用。这样可以实现使用变量的额外优势,即确保所有这些字段像预期一样具有相同的值。

CREATE SERVICE echo_service
   IN COMPUTE POOL tutorial_compute_pool
   MIN_INSTANCES=1
   MAX_INSTANCES=1
   FROM SPECIFICATION_TEMPLATE $$
      spec:
         containers:
         - name: echo
           image: {{ image_url }}
           env:
             SERVER_PORT: {{SERVER_PORT}}
             CHARACTER_NAME: Bob
           readinessProbe:
             port: {{SERVER_PORT}}
             path: /healthcheck
         endpoints:
         - name: echoendpoint
           port: {{SERVER_PORT}}
           public: true
         $$
      USING (image_url=>' "/tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest" ', SERVER_PORT=>8000 );
Copy

在此 CREATE SERVICE 命令中,USING 参数为两个规范变量提供值。image_url 值包括斜杠和冒号。这些不是字母数字字符。因此,该示例将值引在双引号中,使其成为有效的 JSON 字符串值。模板规范扩展了以下规范:

spec:
  containers:
  - name: echo
    image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest
    env:
      SERVER_PORT: 8000
      CHARACTER_NAME: Bob
    readinessProbe:
      port: 8000
      path: /healthcheck
    endpoints:
    - name: echoendpoint
      port: 8000
      public: true
Copy

示例 2:提供 JSON 值

在教程 1 中,规范定义了两个环境变量(SERVER_PORTCHARACTER_NAME),如下所示:

spec:
 containers:
 - name: echo
   image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest
   env:
     SERVER_PORT: 8000
     CHARACTER_NAME: Bob
   
Copy

您可以为 env 字段使用变量,以此模板化此规范。这允许您创建多项服务,为环境变量使用不同的值。以下 CREATE SERVICE 命令为 env 字段使用变量 (env_values)。

CREATE SERVICE echo_service
  IN COMPUTE POOL tutorial_compute_pool
  MIN_INSTANCES=1
  MAX_INSTANCES=1
  FROM SPECIFICATION_TEMPLATE $$
     spec:
       containers:
       - name: echo
         image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest
         env: {{env_values}}
         readinessProbe:
           port: {{SERVER_PORT}}    #this and next tell SF to connect to port 8000
           path: /healthcheck
       endpoints:
       - name: echoendpoint
         port: {{SERVER_PORT}}
         public: true
        $$
     USING (env_values=>'{"SERVER_PORT": 8000, "CHARACTER_NAME": "Bob"}' );
Copy

CREATE SERVICE 中的 USING 参数为 env_values 变量提供值。该值是 JSON 映射,为两个环境变量提供值。

示例 3:提供列表作为变量值

教程 2 中,该规范包括 args 字段,其中包含两个实参。

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

在规范的模板版本中,您可以以 JSON 列表的形式提供这些实参,如下所示:

spec:
  container:
  - name: main
    image: /tutorial_db/data_schema/tutorial_repository/my_job_image:latest
    env:
      SNOWFLAKE_WAREHOUSE: tutorial_warehouse
    args: {{ARGS}}
  $$
  USING (ARGS=$$["--query=select current_time() as time,'hello'", "--result_table=results"]$$ );
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 不会将服务视为 READY,直到至少有两个实例可用。当服务尚未准备就绪时,Snowflake 会阻止对它的访问,这意味着在确认准备就绪之前,关联的服务函数或入口请求会遭据。

在某些情况下,您可能希望 Snowflake 将服务视为就绪(并转发传入请求),即使可用的实例数少于指定的最小实例数也是如此。您可以通过设置 MIN_READY_INSTANCES 属性来实现此目标。

考虑以下情况:在维护或滚动服务升级期间,Snowflake 可能会终止一个或多个服务实例。这可能会导致可用实例少于指定的 MIN_INSTANCES,从而阻止服务进入 READY 状态。在这些情况下,您可以将 MIN_READY_INSTANCES 设置为小于 MIN_INSTANCES 的值,以确保服务可以继续接受请求。

示例

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

有关更多信息,请参阅 CREATE SERVICE

备注

您不能运行一项作业服务的多个实例。

启用自动扩缩

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

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

    示例

    resources:
      requests:
       cpu: <cpu-units>
    
    Copy
  2. 运行 CREATE SERVICE 命令时,设置 MIN_INSTANCES 和 MAX_INSTANCES 参数。您还可以使用 ALTER SERVICE 更改这些值。当指定的 MAX_INSTANCES 大于 MIN_INSTANCES 时,会发生自动缩放。

Snowflake 首先在指定的计算池上创建最小数量的服务实例。然后,Snowflake 会根据 80% 的 CPU 使用率阈值来增加或减少服务实例的数量。Snowflake 可持续监控计算池内的 CPU 利用率,汇总当前运行的所有服务实例的使用量数据。

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

请注意以下扩缩行为:

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

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

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

暂停服务

长期运行的服务会消耗计算池资源并产生成本,但您可以在服务不执行有意义的工作时将其暂停。当任何计算池节点上没有服务或作业处于活动状态时,Snowflake 的计算池自动暂停机制会暂停计算池,以降低成本。

要暂停服务,您可以明确调用 :doc:` ALTER SERVICE ...SUSPEND </sql-reference/sql/alter-service>` 以暂停服务或使用 CREATE SERVICEALTER SERVICE 设置 AUTO_SUSPEND_SECS 属性来定义空闲时长,在此之后 Snowflake 将自动暂停服务。

设置 AUTO_SUSPEND_SECS 属性后,如果服务尚未暂停且空闲时间超过 AUTO_SUSPEND_SECS 秒钟,Snowflake 会自动暂停该服务。当满足以下两个条件时,服务处于空闲状态:

  • 当前没有正在运行的查询包含对该服务的 服务函数 调用。

  • 服务状态为 RUNNING。

小心

自动暂停功能不会追踪由服务函数调用发起的数据处理(即使该处理在服务函数返回后仍持续运行)。在当前的实现中,自动暂停也不跟踪入口和服务间通信。因此,对于提供此类功能的服务,您不应启用自动暂停功能,因为这可能会干扰这些可能仍在进行的处理流程。

当 Snowflake 暂停服务时,它会关闭计算池上的所有服务实例。如果计算池上没有运行其他服务,且为该计算池配置了自动暂停功能,则 Snowflake 也会暂停该计算池节点。因此,您无需为非活动计算池付费。

同时请注意以下事项:

  • 作业服务不支持自动暂停。

  • 对于具有公共端点的服务,系统不支持自动暂停功能,因为 Snowflake 目前仅通过服务函数流量(而非入口流量)来判断服务是否处于空闲状态。

修改和删除服务

创建服务后:

  • 使用 DROP SERVICE 命令从架构中移除服务(Snowflake 终止所有服务容器)。

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

    备注

    您不能更改作业服务。

服务终止

当您暂停服务 (ALTER SERVICE ...SUSPEND) 或放弃服务 (DROP SERVICE) 时,Snowflake 会终止所有服务实例。同样,当您升级服务代码 (ALTER SERVICE...<fromSpecification>) 时,Snowflake 会通过每次终止和重新部署一个服务实例来应用 滚动升级

在终止服务实例时,Snowflake 会首先将 SIGTERM 信号发送到每个服务容器。容器可以选择处理信号并在 30 秒的窗口内正常关闭。否则,宽限期过后,Snowflake 会终止容器中的所有进程。

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

创建服务后,使用 ALTER SERVICE … <fromSpecification> 命令更新服务代码并重新部署服务。

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

ALTER SERVICE echo_service
FROM SPECIFICATION $$
spec:
  
  
$$;
Copy

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

备注

当您运行 CREATE SERVICE ... <fromSpecification> 命令时,Snowflake 会记录所提供镜像的特定版本。在以下情况下,Snowflake 会部署相同的镜像版本,即使仓库中的镜像已更新也是如此:

  • 当恢复暂停的服务时(使用 ALTER SERVICE ...RESUME)。

  • 当自动扩缩添加更多服务实例时。

  • 当在集群维护期间重新启动服务实例时。

但是,当您调用 ALTER SERVICE ... <fromSpecification> 时,Snowflake 会使用仓库中该镜像的最新版本。

如果您是服务所有者,则 DESCRIBE SERVICE 命令的输出包括服务规范,其中包括镜像摘要(规范中 sha256 字段的值),如下所示:

spec:
containers:
- name: "echo"
    image: "/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

ALTER SERVICE 可能会影响与服务的通信(请参阅 使用服务)。

  • 如果 ALTER SERVICE ...<fromSpecification> 移除了一个端点或移除了使用端点时所需的相关权限(请参阅 规范参考中的 serviceRoles),因此对服务的访问将失败。有关更多信息,请参见 使用服务

  • 在升级过程中,新连接可能会路由到新版本。如果新服务版本不向后兼容,它将中断任何正在进行的服务使用。例如,使用服务函数的持续查询可能会失败。

备注

当更新的服务代码是带容器的原生应用程序的一部分时,您可以使用 SYSTEM$WAIT_FOR_SERVICES 系统函数暂停原生应用程序设置脚本,以允许服务完全升级。有关更多信息,请参阅 升级应用程序

监控滚动更新

当多个服务实例运行时,Snowflake 会根据服务实例的 ID 降序执行滚动更新。使用以下命令监控服务更新:

  • DESCRIBE SERVICESHOW SERVICES

    • 如果服务正在升级,则输出中的 is_upgrading 列显示 TRUE。

    • 输出中的 spec_digest 列表示当前服务规范的规范摘要。您可以定期执行此命令;spec_digest 值中的更改表示已触发服务升级。仅在 is_upgrading 为 FALSE 后,spec_digest 才在使用中;否则,服务升级仍在进行中。

      使用 SHOW SERVICE INSTANCES IN SERVICE 命令检查是否所有实例都已更新到最新版本,如下所述。

  • SHOW SERVICE INSTANCES IN SERVICE

    • 输出中的 status 列提供滚动升级过程中每个服务实例的状态。在升级过程中,您将观察每个服务实例的转换状态,例如 TERMINATING 至 PENDING,以及 PENDING 至 READY。

    • 在服务升级期间,此命令输出中的 spec_digest 列可能显示与 SHOW SERVICES(始终返回最新的规范摘要)不同的值。 此区别只是表示服务升级正在进行中,并且服务实例仍在运行旧版本的服务。

获取服务信息

您可以使用以下命令:

  • 使用 DESCRIBE SERVICE 命令检索服务的属性和状态。输出返回所有服务属性。

  • 使用 SHOW SERVICES 命令列出您具有权限的当前服务(包括作业服务)。输出提供了这些服务的一些属性和状态。

    默认情况下,输出会列出当前数据库和架构中的服务。您还可以指定以下任何范围。例如:

    • 列出账户、特定数据库或特定架构中的服务: 例如,使用 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 输出。

  • 使用 SHOW SERVICE INSTANCES IN SERVICE 命令检索服务实例的属性。

  • 使用 SHOW SERVICE CONTAINERS IN SERVICE 命令检索服务实例的属性和状态。

监控服务

Snowpark Container Services 提供一些工具,用于监控账户中的计算池及其上运行的服务。有关更多信息,请参阅 Snowpark Container Services:监控服务

使用服务

创建服务后,同一账户(创建服务的账户)中的用户可以使用该服务。如图所示,有三种使用服务的方法。用户需要访问具有必要权限的角色。

使用服务的方法

该图表突出显示了使用服务的方法,而为清楚起见,其他与服务相关的组件显示为灰色。有关服务组件的详细说明,请参阅本页开头的图表。

  • **通过 SQL 查询使用服务**(服务函数):您可以创建服务函数、与服务关联的用户定义函数 (UDF),然后将其用于 SQL 查询并利用您的服务提供的自定义数据处理。有关示例,请参阅 教程 1

  • **从 Snowflake 外部使用服务**(入口):您可以将一个或多个服务端点声明为公共端点,以允许网络入口访问该服务。这可用于构建 Web 应用程序,也可以通过您的 Snowflake 数据公开 APIs。有关示例,请参阅 教程 1

  • **通过其他服务使用服务**(服务到服务通信):使用 Snowflake 针对服务到服务通信分配的服务 DNS 名称,服务可以相互进行通信。 有关示例,请参阅 教程 3

如图所示,当使用这些方法中的任何一种与服务进行通信时,您会向该服务公开的端点发送请求并获得结果。

备注

  • 不支持使用服务函数或入口与作业服务通信。

    • 您不能将服务函数与作业服务的任何端点相关联。

    • 您不能使用定义公共端点的规范来创建作业服务。

  • 支持与作业服务进行服务到服务通信。也就是说,服务与作业服务可以相互通信。

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

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

服务函数是使用 CREATE FUNCTION (Snowpark Container Services) 创建的用户定义函数 (UDF)。不过,您不用直接编写 UDF 代码,而可将 UDF 与您的服务关联起来。请注意,您只能将服务函数与支持 HTTP 协议的服务端点关联(请参阅 spec.endpoints 字段(可选))。

例如,在 教程 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

您可以在 SELECT 语句中调用服务函数,如下所示:

SELECT service_function_name(<parameter-list>);
Copy

Snowflake 将请求定向到相关的服务端点和路径。

备注

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

数据交换格式

对于服务函数和应用程序容器之间的数据交换,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”...)。

配置批处理

CREATE FUNCTIONALTER FUNCTION 命令支持用于配置 Snowflake 如何处理服务所处理的数据批次的参数。

  • 配置批次大小

    您可以使用 MAX_BATCH_ROWS 参数来限制批次大小,即 Snowflake 在单个请求中向您的服务发送的最大行数。这有助于控制传输的数据量。如果您的服务支持多个实例或并发请求,这也可能导致更多、更小的批次可以并行处理。

  • 处理错误

    您可以使用以下参数进行批次错误处理:ON_BATCH_FAILUREMAX_BATCH_RETRIESBATCH_TIMEOUT_SECS

例如,以下 ALTER FUNCTION 命令配置 my_echo_udf 服务函数的 MAX_BATCH_ROWS 和 MAX_BATCH_RETRIES 参数:

ALTER FUNCTION my_echo_udf(VARCHAR) SET
   MAX_BATCH_ROWS = 15
   MAX_BATCH_RETRIES = 5;
Copy

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

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

  • 当前角色必须具有为 CREATE FUNCTIONALTER FUNCTION 命令中引用的端点授予的服务角色。

  • 要在 SQL 查询中使用服务函数,当前会话必须具有对该服务函数拥有使用权限的角色,并且必须向该服务函数的所有者角色授予关联服务端点的服务角色。

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

USE ROLE service_owner;
GRANT USAGE ON DATABASE service_db TO ROLE func_owner;
GRANT USAGE ON SCHEMA my_schema TO ROLE func_owner;
GRANT SERVICE ROLE ON service service_db.my_schema.my_service!all_endpoints_usage 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 DATABASE service_db TO ROLE func_user;
GRANT USAGE ON SCHEMA my_schema TO ROLE func_user;
GRANT USAGE ON FUNCTION test_udf(varchar) TO ROLE func_user;
USE ROLE func_user;
SELECT my_test_udf('abcd');
Copy

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

您可以在服务规范中将一个或多个端点声明为公共端点,以允许用户从公共场所使用该服务。请注意,用户必须是创建该服务的同一 Snowflake 账户中的 Snowflake 用户。

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

请注意,仅允许通过 HTTP 端点使用入口(请参阅 spec.endpoints 字段(可选))。

入口身份验证

当用户被授予允许访问公共端点的服务角色时,即可访问该端点。(请参阅 访问服务端点所需的权限(服务角色))。

然后,这些用户可以使用浏览器或以编程方式访问公共端点:

  • 使用浏览器访问公共端点: 当用户使用浏览器访问公共端点时,Snowflake 会自动将用户重定向到登录页面。用户必须提供他们的 Snowflake 凭据才能登录。成功登录后,用户可以访问该端点。在后台,用户登录会从 Snowflake 生成一个 OAuth 令牌。然后,使用该 OAuth 令牌向服务端点发送请求。

  • 以编程方式访问公共端点: 应用程序可以使用 密钥对身份验证,对公有端点的请求进行身份验证。在代码中,您通过密钥对生成 JSON Web 令牌 (JWT)、与 Snowflake 交换 JWT 令牌以获得 OAuth 令牌,然后使用 OAuth 令牌对服务公共端点的请求进行身份验证。

教程 1 提供分步说明,供您测试公共端点访问。

教程 1 中所示的密钥对身份验证是推荐方式,用于在访问公共端点时对请求进行身份验证。以下代码可用于身份验证,作为使用键值对的替代方法;不过,无法保证代码能与 Snowflake Connector for Python 的未来版本结合使用。这段 Python 代码会使用 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 支持部门 `_。

服务到服务通信

服务实例可通过 TCP(包括 HTTP)直接相互通信。这既适用于属于同一服务的实例,也适用于属于不同服务的实例。

实例只能在服务规范中声明的 端点 上接收通信(请求)。客户端(发送请求的服务)必须具备连接该端点所需的角色和权限。(请参阅 访问服务端点所需的权限(服务角色)

  • 默认情况下,服务实例可以在已声明端点上连接到同一服务的其他实例。从广义上讲,服务的 所有者角色 拥有连接到具有相同所有者角色的服务端点的权限。

  • 为了使客户端服务连接到具有不同所有者角色的服务的端点,该客户端服务的所有者角色需要使用 服务角色 授予对其他服务的端点的访问权限,以调用该端点。有关更多信息,请参阅 访问服务端点所需的权限(服务角色)

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

可以使用服务 IP 地址或服务实例 IP 地址访问服务实例。

  • 使用 IP 服务地址的请求会被路由到负载平衡器,而负载平衡器又会将请求路由到随机选择的服务实例。

  • 使用服务实例 IP 地址的请求会直接路由到特定的服务实例。连接到使用 portRange ` 字段定义的端点时,必须使用服务实例 IP(请参见 :ref:`label-spcs_spec_ref_spec_endpoints)。

这两个 IP 地址都可使用 Snowflake 自动分配给每项服务的 DNS 名称进行查找。请注意,无法使用 DNS 连接到特定实例。例如,使用服务实例 DNS 名称构建 URL 没有意义,因为无法使用服务实例 DNS 名称来引用特定的服务实例。

启用 2025_01 行为变更捆绑包 时,服务实例 IP 的地址会显示在 SHOW SERVICE INSTANCES IN SERVICE 命令的输出中。

有关服务到服务通信的示例,请参见 教程 3

请注意,如果创建服务端点仅为了允许服务到服务通信,则应该使用 TCP 协议(请参见 spec.endpoints 字段(可选))。

服务 DNS 名称

DNS 名称格式为:

<service-name>.<hash>.svc.spcs.internal

使用 /sql-reference/sql/show-services`(或 :doc:/sql-reference/sql/desc-service`)获取服务的 DNS 名称。前面的 DNS 名称是完全限定名称。在同一架构下创建的服务可以只使用 <service-name> 进行通信。不同架构或数据库中的服务必须提供哈希值,如 <service-name>.<hash> 或提供完全限定名称 (:code:`<service-name>.<hash>.svc.spcs.internal `)。

使用 SYSTEM$GET_SERVICE_DNS_DOMAIN 函数查找给定架构的 DNS 域。DNS 哈希域特定于当前版本的架构。请注意以下事项:

  • 如果该架构或其数据库重命名,哈希值不会变更。

  • 如果架构被删除,然后重新创建(例如使用 CREATE OR REPLACE SCHEMA),则新架构将有一个新的哈希值。 如果您 UNDROP 架构,则哈希值保持不变。

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 名称中的短划线 (-)。

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

服务实例 DNS 名称

服务实例 DNS 名称格式为:

instances.<service-name>.<hash>.svc.spcs.internal

解析到服务实例 IP 地址列表,每个服务实例对应一个地址。请注意,DNS 返回的 IP 地址列表顺序没有保证。DNS 名称只应与 DNS APIs 一起使用,不应用作 URL 中的主机名。我们希望应用程序将此主机名与 DNS APIs 结合使用来收集服务实例集 IPs,然后以编程方式直接连接到这些实例 IPs。

可以通过此 IP 地址列表创建一个网状网络,用于在特定服务实例之间进行直接通信。

要选择的 DNS 名称

在服务到服务通信中连接服务时,选择使用哪个 DNS 名称需要考虑以下因素。

当出现以下情况时,请使用服务 DNS 名称:

  • 您需要以尽可能简单的方式访问特定目标端口。

  • 您希望将每个请求发送到随机选择的服务实例。

  • 您不知道应用程序框架如何执行和缓存 DNS 响应。

出现以下情况时,请使用服务实例 DNS 名称或服务实例 IP:

  • 您希望找到所有服务实例的 IP 地址。

  • 您想跳过中间负载平衡器。

  • 您使用的分布式框架或数据库(如 Ray 或 Cassandra)以服务实例 IP 地址作为身份。

准则和限制

有关更多信息,请参阅 准则和限制

语言: 中文