Snowpark Container Services:使用服务¶
Snowpark Container Services 使您能够轻松部署、管理和扩展容器化应用程序。创建应用程序并将应用程序映像上传到 Snowflake 账户中的存储库后,您就可以将应用程序容器作为服务运行。
服务表示 Snowflake 在 计算池 上运行您的容器化应用程序,计算池是虚拟机 (VM) 节点的集合。服务分为两类:
长期运行的服务。 长期运行的服务就像不会自动结束的 Web 服务。创建服务后,Snowflake 会管理正在运行的服务。例如,如果某个服务容器出于某种原因停止,Snowflake 会重新启动该容器,以便服务不间断地运行。
作业服务。 作业服务会在代码退出时终止,与存储过程类似。当所有容器都退出时,作业服务便已完成。
Snowpark Container Services 提供了一组 SQL 命令,您可以使用这些命令创建并管理服务。这些对象包括:
更改服务。 ALTER SERVICE、DROP SERVICE
获取服务信息。 SHOW SERVICES、DESCRIBE SERVICE
启动服务¶
启动服务所需的最低限度信息包括:
创建长期服务¶
使用 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 $$;
使用暂存区信息创建服务。在生产环境中部署服务时,建议应用关注点分离设计原则,并将规范上传到一个暂存区,提供暂存区信息 CREATE SERVICE 命令,如下所示:
CREATE SERVICE echo_service IN COMPUTE POOL tutorial_compute_pool FROM @tutorial_stage SPECIFICATION_FILE='echo_spec.yaml';
执行作业服务¶
使用 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" $$;
使用暂存区信息执行作业服务:
EXECUTE JOB SERVICE IN COMPUTE POOL tutorial_compute_pool NAME = example_job_service FROM @tutorial_stage SPECIFICATION_FILE='my_job_spec.yaml';
使用规范模板¶
有时,您可能希望使用相同的规范创建多项服务,但采用不同的配置。例如,假设您以一种服务规范定义了一个 环境变量,并且您希望使用相同的规范创建多项服务,但环境变量的值不同。
借助规范模板,您可以为规范中的字段值定义变量。在创建服务时,您需要为这些变量提供值。
使用规范模板的流程分为两个步骤:
使用变量作为各种规范字段的值来创建规范。使用
{{ variable_name }}
语法来指定这些变量。例如,以下规范为镜像标签名称使用名为“tag_name”的变量,以便您可以为每项服务指定不同的镜像标签。spec: containers: - name: echo image: myorg-myacct.registry.snowflakecomputing.cn/tutorial_db/data_schema/tutorial_repository/my_echo_service_image:{{ tag_name }} ... endpoints: …
在 CREATE SERVICE 命令中提供规范模板,以创建服务。您使用 SPECIFICATION_TEMPLATE 或 SPECIFICATION_TEMPLATE_FILE 指定模板。使用 USING 参数指定变量的值。 例如,以下语句使用来自 Snowflake 暂存区的规范模板。USING 参数将
tag_name
变量设置为值'latest'
。CREATE SERVICE echo_service IN COMPUTE POOL tutorial_compute_pool FROM @STAGE SPECIFICATION_TEMPLATE_FILE='echo.yaml' USING (tag_name=>'latest');
在规范中定义变量的准则¶
使用
{{ variable_name }}
语法将变量定义为规范中的字段值。这些变量可以具有默认值。要指定默认值,请在变量声明中使用
default
函数。例如,以下规范定义具有默认值的两个变量(character_name
和endpoint_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
此外,您还可以为
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
在规范中:
对于
character_name
变量,布尔参数设置为false
。因此,如果变量设置为此参数的空字符串值 (''),则该值将保留空值;不使用默认值(“Bob”)。对于
echo_endpoint
变量,布尔参数设置为true
。因此,如果向此参数传递空值,则使用默认值(“echo-endpoint”)。
默认情况下,
default
函数的布尔参数为false
。
为规范变量传递值的准则¶
在 CREATE SERVICE 命令中指定 USING 参数,为变量提供值。USING 的一般语法是:
USING( var_name=>var_value, [var_name=>var_value, ... ] );
其中
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]' );
CREATE SERVICE 中的 USING 参数必须为规范变量提供值(规范为其提供默认值的变量除外)。否则,返回错误。
示例¶
以下示例显示如何使用规范模板创建服务。这些示例中的 CREATE SERVICE 命令使用内联规范。
示例 1:提供简单值¶
在 教程 1 中,您可以通过提供内联规范来创建服务。以下示例是该示例的修改版本,其中规范定义了两个变量:image_url
和 SERVER_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 );
在此 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
示例 2:提供 JSON 值¶
在教程 1 中,规范定义了两个环境变量(SERVER_PORT
和 CHARACTER_NAME
),如下所示:
spec:
containers:
- name: echo
image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest
env:
SERVER_PORT: 8000
CHARACTER_NAME: Bob
…
您可以为 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"}' );
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"
在规范的模板版本中,您可以以 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"]$$ );
扩展服务¶
默认情况下,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;
当多个服务实例正在运行时,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;
有关更多信息,请参阅 CREATE SERVICE。
备注
您不能运行一项作业服务的多个实例。
启用自动扩缩¶
要配置 Snowflake 以自动扩缩正在运行的服务实例数量,请按照以下步骤操作:
在服务规范文件中指定服务实例的 CPU 和内存要求。有关更多信息,请参阅 container.resources 字段。
示例
resources: requests: cpu: <cpu-units>
运行 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 属性指定的实例数启动,不会自动扩缩。
长期运行的服务会消耗计算池资源并产生成本,但您可以在服务不执行有意义的工作时将其暂停(请参见 ALTER SERVICE ...SUSPEND)。当任何计算池节点上没有服务或作业处于活动状态时,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:
…
…
$$;
收到请求后,Snowflake 会使用新代码重新部署服务。
当您运行 CREATE SERVICE ...<from-Specification> 命令,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
ALTER SERVICE 可能会影响与服务的通信(请参阅 使用服务)。
如果 ALTER SERVICE ...<fromSpecification> 移除了一个端点或移除了使用端点时所需的相关权限(请参阅 规范参考中的 serviceRoles),因此对服务的访问将失败。有关更多信息,请参见 使用服务。
在升级过程中,新连接可能会路由到新版本。如果新服务版本不向后兼容,它将中断任何正在进行的服务使用。例如,使用服务函数的持续查询可能会失败。
备注
当更新的服务代码是带容器的原生应用程序的一部分时,您可以使用 SYSTEM$WAIT_FOR_SERVICES 系统函数暂停原生应用程序设置脚本,以允许服务完全升级。有关更多信息,请参阅 升级应用程序。
监控滚动升级¶
当多个服务实例运行时,Snowflake 会根据服务实例的 ID 降序执行滚动升级。使用以下命令监控服务升级:
DESCRIBE SERVICE 和 SHOW SERVICES:
如果服务正在升级,则输出中的
is_upgrading
列显示为 TRUE。输出中的
spec_digest
列表示当前服务规范的规范摘要。您可以定期执行此命令;spec_digest
值中的更改表示已触发服务升级。使用 SHOW SERVICE INSTANCES IN SERVICE 命令检查是否所有实例都已升级到最新版本,如下所述。
SHOW SERVICE INSTANCES IN SERVICE:
输出中的
status
列提供滚动升级过程中每个服务实例的状态。在升级过程中,您将观察每个服务实例的转换状态,例如 TERMINATING 至 PENDING,以及 PENDING 至 READY。在服务升级期间,SHOW SERVICE INSTANCES IN SERVICE 命令可能会在 SHOW SERVICES(始终返回最新的服务规范)的
spec_digest
输出中返回不同的值。它只是表示服务升级正在进行中,并且服务实例仍在运行旧版本的服务。
获取服务信息¶
您可以使用以下命令:
使用 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:监控服务。
管理对服务端点的访问¶
服务 所有者 角色(您用来创建服务的角色)对服务本身和服务所公开的端点具有完全访问权限。其他角色需要对端点具有 USAGE 权限才能与服务通信。例如:
客户端的所有者角色需要对端点拥有 USAGE 权限。客户端 指的是向另一个服务的端点发出请求的服务函数或服务。
从 Snowflake 外部向公共端点发出 入口 请求的用户需要对端点具有 USAGE 权限。
要允许某个角色访问服务端点,需要向该角色授予以下权限:
创建服务的数据库和架构的 USAGE 权限。
有权访问端点的 服务角色*(请参见 :doc:`/sql-reference/sql/grant-service-role`)。*服务角色 是一种机制,用于为其他角色授予对服务端点的权限。您有以下选项:
使用默认服务角色 Snowflake 定义了一个默认服务角色 (
ALL_ENDPOINTS_USAGE
),授予对服务公开的所有端点的 USAGE 权限,并向服务的所有者角色授予此默认服务角色。因此,所有者角色可以访问服务公开的所有端点。您可以向其他角色授予此默认服务角色。示例: 假设您创建了一项带有公共端点 (
echoendpoint
) 的服务,如下所示:use database my_db; use schema my_schema; create service my_service in compute pool tutorial_pool from specification $$ spec: containers: - name: echo image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest endpoints: - name: echoendpoint port: 8000 public: true $$
要授予某个角色 (
custom_role
) 访问端点的权限,请运行以下命令:grant usage on database my_db to role custom_role; grant usage on schema my_schema to role custom_role; grant service role my_service!all_endpoints_usage to role custom_role;
创建服务角色: 您可以在 服务规范 中定义一个或多个服务角色,而不是使用默认服务角色授予对所有端点的权限。在定义中,指明角色获得的 USAGE 权限所针对的特定端点。您可以使用 GRANT SERVICE ROLE 和 REVOKE SERVICE ROLE 命令,向其他角色授予(或撤销)服务角色。您还可以使用 SHOW ROLES IN SERVICE、SHOW GRANTS 命令,显示有关授权的信息。
在您创建服务时,Snowflake 会创建服务角色;在您删除服务时,会删除服务角色。
创建自定义服务角色,允许您针对不同的场景授予不同的访问权限。例如,您可以向服务角色授予对端点的权限,以用于服务函数。您可以创建另一个服务角色,并赋予其对用于 Web UI 的公共端点的权限。
示例: 假设您创建了一项服务,其中有两个公共端点(
ep1
和ep2
)和一个可访问端点ep1
的服务角色 (ep1_role
),如下所示:use database my_db; use schema my_schema; create service my_service in compute pool tutorial_pool from specification $$ spec: containers: - name: echo image: /tutorial_db/data_schema/tutorial_repository/my_echo_service_image:latest endpoints: - name: ep1 port: 8000 public: true - name: ep2 port: 8082 public: true serviceRoles: - name: ep1_role endpoints: - ep1 $$
要仅授予角色 (
custom_role
) 对端点ep1
的访问权限,请运行以下命令:grant usage on database my_db to role custom_role; grant usage on schema my_schema to role custom_role; grant service role my_service!ep1_role to role custom_role;
请注意以下事项:
如果使用同一角色创建多项服务,由于所有者角色可以访问所有端点,因此这些服务之间可以无缝通信,无需任何额外配置更改。
如果一项服务有多个容器,这些容器可以通过本地主机相互通信,这些通信是每个服务实例内部的本地通信,不受基于角色的访问控制的限制。
以下各节提供了详细信息。您也可以尝试学习一个教程(配置和测试服务端点权限),其中提供了分步探索此功能的说明。
使用默认服务角色授予对所有端点的 USAGE 权限¶
创建服务(包括作业服务)时,Snowflake 还会创建默认服务角色,名为 ALL_ENDPOINTS_USAGE
。此角色对服务公开的所有端点都拥有 USAGE 权限。您可以使用 GRANT SERVICE ROLE 命令将此默认服务角色授予其他角色:
GRANT SERVICE ROLE my_echo_service_image!ALL_ENDPOINTS_USAGE TO ROLE some_other_role;
使用 some_other_role
的用户对所有服务端点都拥有 USAGE 权限。
删除服务时,Snowflake 会删除与服务相关联的所有服务角色(默认服务角色和服务规范中定义的服务角色),并取消所有服务角色授予。
使用规范中定义的服务角色授予对特定端点的 USAGE 权限¶
使用服务角色管理对服务端点的精细访问。您可以在服务规范中定义服务角色,以及授予其 USAGE 权限的端点列表。
向服务的特定端点授予权限分为两个步骤:
定义服务角色: 使用服务规范来定义服务角色,方法是提供角色名称,以及包含一个或多个您像向其授予 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>
将服务角色授予其他角色。 使用 GRANT SERVICE ROLE 命令,可将服务角色授予其他角色(账户角色、应用程序角色或数据库角色)。例如:
GRANT SERVICE ROLE <service-name>!<svc_role_name1> TO ROLE <another-role>
使用服务¶
创建服务后,同一账户(创建服务的账户)中的用户可以使用以下三种受支持方法中的任何一种来使用该服务。用户需要访问具有必要权限的角色。
**通过 SQL 查询使用服务**(服务函数):您创建服务函数、与服务关联的用户定义的函数 (UDF),然后在 SQL 查询中使用函数,以与服务进行通信。有关示例,请参阅 教程 1。
**从 Snowflake 外部使用服务**(入口):您可以将一个或多个服务端点声明为公共端点,以允许对服务进行网络入口访问。有关示例,请参阅 教程 1。
**通过其他服务使用服务**(服务到服务通信):使用 Snowflake 针对服务到服务通信分配的服务 DNS 名称,服务可以相互进行通信。有关示例,请参阅 教程 3。
备注
作业服务像作业一样运行,并在完成后终止。不支持使用服务函数或入口与作业服务通信。
您不能将服务函数与作业服务的任何端点相关联。
您不能使用定义公共端点的规范来创建作业服务。
支持与作业服务进行服务到服务通信。也就是说,服务与作业服务可以相互通信。
以下各节提供了详细信息。
服务函数:使用 SQL 查询中的服务¶
服务函数是使用 CREATE FUNCTION (Snowpark Container Services) 创建的用户定义函数 (UDF)。不过,您不用直接编写 UDF 代码,而可将 UDF 与您的服务关联起来。请注意,您只能将服务函数与支持 HTTP 或 HTTPS 协议的服务端点关联。
例如,在 教程 1 中,您创建了一个名为 echo_service
的服务,它公开了服务规范中定义的一个端点 (echoendoint):
spec:
…
endpoints:
- name: echoendpoint
port: 8080
echoendpoint
是一个用户友好型端点名称,代表相应的端口 (8080)。要与此服务端点通信,需要提供 SERVICE 和ENDPOINT 参数,创建一个服务函数,如下所示:
CREATE FUNCTION my_echo_udf (text varchar)
RETURNS varchar
SERVICE=echo_service
ENDPOINT=echoendpoint
AS '/echo';
AS
参数提供服务代码的 HTTP 路径。您可以从服务代码中获取该路径值。例如,以下代码行来自 教程 1 中的 service.py
。
@app.post("/echo")
def echo():
...
调用服务函数时,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
数据交换格式¶
对于服务函数和应用程序容器之间的数据交换,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;
Snowflake 会向容器发送一系列请求,请求正文中的数据行批次格式如下:
{
"data":[
[
0,
"Alex",
"2014-01-01 16:00:00"
],
[
1,
"Steve",
"2015-01-01 16:00:00"
],
…
[
<row_index>,
"<column1>",
"<column2>"
],
]
}
然后,容器会以以下格式返回输出结果:
{
"data":[
[0, "a"],
[1, "b"],
…
[ row_index, output_column1]
]
}
所示输出示例假定结果是一个单列表,包含行(“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');
入口:使用来自 Snowflake 外部的服务¶
一项服务可以 公开 一个或多个端点,允许用户从公共网络使用该服务。 在这种情况下,Snowflake 会管理访问控制。请注意,仅允许通过 HTTP 或 HTTPS 端点使用入口。
在服务规范文件中将端点标记为公共端点:
spec
...
endpoints
- name: <endpoint name>
port: <port number>
public: true
来自 Snowflake 外部的公共端点访问及身份验证¶
不是每个人都能访问服务公开的公共端点。只有与服务处于同一 Snowflake 账户且拥有公共端点 USAGE 权限的用户才能访问公共端点。您可以使用 服务角色 来授予此权限。
这些用户可以使用浏览器或以编程方式访问公共端点:Snowflake 使用 OAuth 对这些请求进行身份验证:
使用浏览器访问公共端点: 当用户使用浏览器访问公共端点时,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在代码中:
如果您不知道自己的账户信息 (
<orgname>-<acctname>
),请参阅教程 常用设置。您可以使用 SHOW ENDPOINTS 获取服务公开的公共端点的
ingress_url
。
入口请求中的用户特定标头¶
当入口端点的请求到达时,Snowflake 会自动将以下标头连同 HTTP 请求一起传递给容器。
Sf-Context-Current-User: <user_name>
您的容器代码可以选择读取标头信息,知道调用者是谁,并为不同用户应用特定于上下文的自定义。此外,Snowflake 还可以选择包含 Sf-Context-Current-User-Email
标头。要包含此标头,请联系 ` Snowflake 支持部门 `_。
服务到服务通信¶
服务实例可通过 TCP(包括 HTTP/HTTPS)直接相互通信。这既适用于属于同一服务的实例,也适用于属于不同服务的实例。
实例只能接收 已声明端点 上的通信(请求)。客户端(发送请求的服务)必须具备连接该端点所需的角色和权限。
默认情况下,服务实例可以在已声明端点上连接到同一服务的其他实例。从广义上讲,服务的 所有者角色 拥有连接到具有相同所有者角色的服务端点的权限。
为了让客户端服务连接到拥有不同所有者角色的服务端点,必须将与端点相关的 :ref:` 服务角色 <label-snowpark_containers_service_endpoint_access>` 授予客户端服务的 所有者角色。例如:
GRANT SERVICE ROLE my_service!all_endpoints_usage TO ROLE custom_role;
如果您想防止服务之间相互通信(出于安全等原因),请使用不同的 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 地址作为身份。
权限¶
权限 |
用途 |
备注 |
---|---|---|
USAGE |
要与服务通信,您需要 对服务端点具有 USAGE 权限。创建服务函数、使用公共端点和从其他服务连接时需要。 |
|
MONITOR |
监控服务并获取运行时状态。 |
|
OPERATE |
暂停或恢复服务。 |
|
OWNERSHIP |
完全控制服务。同一时间只有一个角色可以在特定对象上拥有此权限。 |
|
ALL [ PRIVILEGES ] |
向服务授予所有权限,OWNERSHIP 除外。 |
准则和限制¶
有关更多信息,请参阅 准则和限制。