在Knative中使用抢占式实例

如果您的业务为短时服务类型(例如短时Web服务请求等),对业务中断容忍度较高,可以用完即释放,不会长时间占用资源,您可以配置Knative Service使用抢占式实例,其价格相较于按量计费的ECS实例更为优惠。通过将Knative与抢占式实例结合使用,您可以在保证服务弹性和响应速度的同时,最大化地利用云计算资源的成本效益,实现更加经济高效的Serverless架构。

前提条件

已在集群中部署Knative,请参见部署Knative

名词解释

  • 抢占式实例

    是一种低成本竞价型实例,您可以对阿里云当前闲置的资源出价,获得资源后运行容器,直到出价低于市场价格或者库存不足等原因导致资源回收。关于抢占式实例的更多信息,请参见什么是抢占式实例

  • Knative

    一款基于Kubernetes的开源Serverless应用编排框架,其目标是制定云原生、跨平台的Serverless应用编排标准。Knative主要功能包括基于请求的自动弹性、缩容到0、多版本管理、基于流量的灰度发布、函数部署以及事件驱动等。关于Knative的更多信息,请参见ACK Knative概述ACK Serverless Knative概述

实现原理及功能优势

在Knative中,Serverless工作负载的管理主要通过Knative Service实现。Knative Service可以基于请求自动扩缩容Pod。如果需要使用抢占式实例,只需要配置相应的Pod注解即可。虚拟节点(Virtual Node)会根据Pod的注解自动申请对应的ECI资源规格。当前虚拟节点提供了抢占式实例自动替换能力,可以更加自动化地使用抢占式实例。

image

Knative结合抢占式实例的优势:

  • Serverless场景:短时Web服务请求,资源随时使用,用完即释放,不会长时间占用资源。

  • 优雅下线的天然适配:在虚拟节点实现自动替换过程中,需要先删除Pod,然后工作负载控制器再创建新的抢占式实例,这就要求业务容器具备优雅下线的能力。而在Knative中会为每个Pod设置1个queue-proxy Sidecar容器,在删除Pod时,会先触发queue-proxy容器等待请求处理完成,然后再删除业务容器。

  • 成本敏感:对于使用Knative且关心成本的用户,使用抢占式实例更具吸引力。

结合Knative配置抢占式实例及示例

配置抢占式实例

重要

仅支持在创建ECI Pod时添加以下Annotation来使ECI功能生效,更新ECI Pod时添加或者修改ECI相关Annotation均不会生效。

在Service中添加Annotation来配置抢占式实例的相关Annotation如下:

Annotation

示例值

是否必选

说明

k8s.aliyun.com/eci-spot-strategy

SpotAsPriceGo

抢占式实例的出价策略。可根据需要配置为:

  • SpotWithPriceLimit:自定义设置抢占实例价格上限。此时必须通过k8s.aliyun.com/eci-spot-price-limit来指定每小时价格上限。

  • SpotAsPriceGo:系统自动出价,跟随当前市场实际价格。

    重要

    使用SpotAsPriceGo策略时,如果对应可用区规格资源紧张,最高价格可能会达到按量价格。

k8s.aliyun.com/eci-spot-price-limit

"0.5"

抢占式实例的每小时价格上限,最多支持精确到小数点后三位。

仅当k8s.aliyun.com/eci-spot-strategy设置为SpotWithPriceLimit时有效。

k8s.aliyun.com/eci-spot-duration

"1"

抢占式实例的保护期。默认值为1,可设置为0。0表示无保护期。

k8s.aliyun.com/eci-spot-fallback

"true"

抢占式实例没有库存时,是否自动转为按量付费,以保证实例创建成功。默认为false

示例一:指定ECS规格,采用SpotWithPriceLimit策略

以下YAML示例可创建一个ecs.c6规格的抢占式实例。

  • 创建时,如果没有满足规格和价格上限要求的库存,则创建失败。

  • 创建后,可以稳定使用1小时,超出1小时保护期后,如果某一时刻的市场价格高于出价或实例规格库存不足,抢占式实例会被释放。

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-go
spec:
  template:
    metadata:
      labels:
        alibabacloud.com/eci: "true"
      annotations:
        k8s.aliyun.com/eci-use-specs : "ecs.c6.large"           # 指定ECS实例规格。
        k8s.aliyun.com/eci-spot-strategy: "SpotWithPriceLimit"  # 采用自定义设置价格上限的策略。
        k8s.aliyun.com/eci-spot-price-limit: "0.25"            # 设置每小时价格上限。
    spec:         
      containers:
      - env:
        - name: TARGET
          value: "Knative"
        image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56

示例二:设置没有库存时自动转为按量付费

以下YAML示例可创建一个ecs.c6规格的抢占式实例。

  • 创建时,如果有满足规格和价格上限要求的库存,则会创建一个抢占式实例。创建后,可以稳定使用1小时,超出1小时保护期后,如果某一时刻的市场价格高于出价或实例规格库存不足,抢占式实例会被释放。

  • 创建时,如果没有满足规格和价格上限要求的库存,则会创建一个按量付费的实例。创建后,系统不会主动释放实例。实例创建成功后,您可以通过kubectl describe pod命令查看对应Pod的事件来确认是否转为按量付费实例,如果看到SpotDegraded事件,则表明已转为按量付费实例。

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-go
spec:
  template:
    metadata:
      labels:
        alibabacloud.com/eci: "true" 
      annotations:
        k8s.aliyun.com/eci-use-specs : "ecs.c6.large"           # 指定ECS实例规格。
        k8s.aliyun.com/eci-spot-strategy: "SpotWithPriceLimit"  # 采用自定义设置价格上限的策略。
        k8s.aliyun.com/eci-spot-price-limit: "0.05"             # 设置每小时价格上限。
        k8s.aliyun.com/eci-spot-fallback: "true"                # 当抢占式实例没有库存时,自动转为按量付费。
    spec:         
      containers:
      - env:
        - name: TARGET
          value: "Knative"
        image: registry.cn-hangzhou.aliyuncs.com/knative-sample/helloworld-go:73fbdd56

步骤一:为Knative Service配置抢占式实例

为尽可能降低计算资源成本,您可以为Knative Service配置k8s.aliyun.com/eci-spot-strategy注解,这样服务会优先使用抢占式实例(ECI Spot实例),这类实例的价格相较于按需实例更为优惠。

然而,实例在市场价格高于出价或实例规格库存不足时会被回收,这可能导致服务中断。为了避免这种情况影响业务连续性,您可以同时设置k8s.aliyun.com/eci-spot-fallback: "true"注解,在抢占式实例因资源不足无法继续使用时,配置了该注解的Knative Service会自动回退到使用按需实例,从而确保服务的稳定运行和连续性。这样既能有效节约成本,又能兼顾服务可靠性。

  1. 登录容器服务管理控制台,在左侧导航栏选择集群

  2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择应用 > Knative

  3. Knative页面的服务管理页签下,选择命名空间default,然后单击使用模板创建,单击示例模板右侧的下拉列表,选择自定义,将以下示例代码粘贴至模板,最后单击创建,创建一个名为helloworld-go的Knative服务。

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: helloworld-go
    spec:
      template:
        metadata:
          labels:
            alibabacloud.com/eci: "true" 
          annotations:
            k8s.aliyun.com/eci-use-specs : "ecs.c6.large"           # 指定ECS实例规格。
            k8s.aliyun.com/eci-spot-strategy: "SpotAsPriceGo"       # 采用自定义设置价格上限的策略。
            k8s.aliyun.com/eci-spot-duration: "1"                   # 设置无保护期。默认为1,单位:h。您可以在创建实例时根据实际需求指定保护期时长。
            k8s.aliyun.com/eci-spot-fallback: "true"                # 当抢占式实例没有库存时,自动转为按量付费。
        spec:         
          containers:
          - env:
            - name: TARGET
              value: "Knative"
            image: registry-vpc.{REGION-ID}.aliyuncs.com/knative-samples/helloworld-go:160e4dc8 # {REGION-ID}为对应集群所在的地域,如cn-hangzhou。

    以上YAML示例可创建一个ecs.c6规格的抢占式实例。

    • 创建时,如果没有满足规格要求的库存,则创建失败。

    • 创建后,可以稳定使用1小时,在此期间内,即使市场价格高于出价,实例也不会被释放。超出1小时保护期后,如果某一时刻的市场价格高于出价或者因为资源库存不足,抢占式实例有可能会被阿里云自动释放。此时,通过k8s.aliyun.com/eci-spot-fallback: "true"设置,当抢占式实例因任何原因无法获取或被释放时,系统会自动转为按量付费实例以确保服务可用性。

(可选)步骤二:配置抢占式实例的优雅下线

使用限制

虚拟节点组件ACK Virtual Node需要升级到v2.11.0及以上,才支持通过Pod Conditions来进行抢占实例中断通知以及配置Eviction API来驱逐抢占式实例。关于ACK Virtual Node的更多信息,请参见ACK Virtual Node

抢占式实例中断通知

抢占式实例会在中断前3分钟发出SpotToBeReleased Event,同时会更新Pod Conditions的ContainerInstanceExpired字段为true

Pod的Conditions字段和Events字段显示如下。图片 1.png

配置抢占式实例到期的优雅处理方式

为了尽量避免ECI抢占式实例回收导致的业务中断,虚拟节点提供了可配置的ECI抢占式实例优雅下线的功能。您可以为抢占型Pod配置annotationsk8s.aliyun.com/eci-spot-release-strategy: api-evict。那么当虚拟节点接收到SpotToBeReleased Event时,则会调用Eviction API来驱逐该抢占式实例。API发起的驱逐将遵从您的PodDisruptionBudgetsterminationGracePeriodSeconds配置。使用API创建Eviction对象,类似于对Pod执行策略控制的DELETE操作。

  1. 调用API请求:虚拟节点接收到SpotToBeReleased Event,调用Eviction API。

  2. PDB检查:API服务器验证与目标Pod关联的PodDisruptionBudget。

  3. 驱逐执行:如果API服务器允许驱逐,Pod将按照如下方式删除。

    1. API服务器中的Pod资源会更新删除时间戳,之后API服务器会认为此Pod资源将被终止。 此Pod资源还会标记上配置的宽限期。

    2. 本地运行状态的Pod所处的节点上的kubelet注意到Pod资源被标记为终止,并开始优雅停止本地Pod。

    3. 当kubelet停止Pod时,控制面从EndpointEndpointSlice对象中移除该Pod。因此,控制器不再将此Pod视为有用对象。

    4. Pod的宽限期到期后,kubelet强制终止本地Pod。

    5. kubelet告诉API服务器删除Pod资源。

    6. API服务器删除Pod资源。

  4. 在Knative场景下,每个Service的Pod中包含一个queue-proxy Sidecar容器。在删除Pod时,queue-proxy会首先等待所有正在进行的请求处理完成,以保证服务的连续性和数据完整性,最后才会停止业务容器,从而实现平滑的Pod缩容或更新操作。

释放说明

抢占式实例创建成功后,在保护期内可以正常运行。超出保护期后,如果市场价格高于出价或者资源库存不足,抢占式实例会被释放。您可以通过以下信息了解抢占式实例的释放情况。

  • 预释放事件

    抢占式实例在释放前约5分钟,会产生SpotToBeReleased事件。

    重要

    ECI会通过Kubernetes Events事件通知的方式告知您抢占式实例将被释放,在此期间,您可以做一定的处理来确保业务不受实例释放所影响。

    • 通过kubectl describe命令查看Pod详细信息,在返回信息的Events中可以看到预释放事件。示例如下:

      Events:
        Type     Reason            Age    From          Message
        ----     ------            ----   ----          -------
        Warning  SpotToBeReleased  3m32s  kubelet, eci  Spot ECI will be released in 3 minutes
    • 通过kukubectl get events命令查看事件信息,在返回信息中可以看到预释放事件。示例如下:

      LAST SEEN   TYPE      REASON             OBJECT         MESSAGE
      3m39s       Warning   SpotToBeReleased   pod/pi-frmr8   Spot ECI will be released in 3 minutes
  • 释放后Pod状态

    抢占式实例释放后,实例信息仍会保留,状态变更为Failed,Failed原因为BidFailed。

    • 通过kubectl get pod命令查看Pod信息,在返回信息中可以看到Pod状态已变更。示例如下:

      NAME       READY   STATUS      RESTARTS   AGE
      pi-frmr8   1/1     BidFailed   0          3h5m
    • 通过kubectl describe命令查看Pod详细信息,在返回信息中可以看到Pod状态信息。示例如下:

      Status:             Failed
      Reason:             BidFailed
      Message:            The pod is spot instance, and have been released at 2020-04-08T12:36Z

相关文档

如果您的应用对冷启动延时较为敏感,推荐您使用保留实例功能,保留一个低规格的突发性能实例,平衡好使用成本和启动时长,请参见配置保留实例