创建抢占式实例

抢占式实例是阿里云提供的一种低成本计算资源,适用于对成本敏感且允许中断的任务场景,如数据分析、测试开发等。抢占式实例的价格通常远低于按量付费实例,但可能会因市场价格波动或资源供需变化而被系统自动释放。本文将指导您如何通过ECS控制台、OpenAPITerraform创建抢占式实例。

建议

在创建抢占式实例时,建议您参考以下内容:

  • 选择合理的出价模式:出价要充分考虑到市场价格的波动,合理的出价可以提升抢占式实例创建成功的概率,并降低后续使用中由于出价低于市场价格导致实例被释放的概率。另外,出价还必须符合您根据自身业务评估后的预期。

    说明

    如果您暂时不能决定出价,建议使用自动出价,即接受实时的市场价格作为实例规格的计费价格 。

  • 数据持久性:建议您使用不受抢占式实例释放影响的存储介质来保存您的重要数据。例如,您可以使用独立创建的云盘(不能设置为随实例一起释放)、OSS、NAS等存储数据。

  • 监控实例状态:您可以通过云监控、实例元数据等方式监控抢占式实例的状态,以判断是否被中断回收。详情请参见查询抢占式实例中断事件

  • 处理中断:抢占式实例可能因市场价格超过您的最高出价或库存不足而被释放,请测试您的应用程序,确保它能很好地处理实例的意外释放。

操作步骤

通过ECS控制台创建

  1. 前往实例创建页,选择自定义购买页签。

  2. 根据实际需求和页面提示,设置ECS资源配置信息。

    需注意以下参数(其他配置项说明,请参考自定义购买实例):

    • 付费类型:选择抢占式实例

    • 实例使用时长

      • 设定实例使用1小时:实例稳定运行1小时不会被自动释放,超过1小时后,系统实时监测库存、出价的变化,进而判断您是否能够继续使用资源。

      • 无确定使用时长:基于实际业务需求,不进行资源使用时长的设定,优势在于相较设定实例使用时长可获得更优惠的价格。

    • 单台实例上限价格

      • 使用自动出价:选择跟随当前市场价格的模式,即表示始终接受实时的市场价格作为实例规格的计费价格 。

      • 设置单台上限价:您必须为指定的实例规格设置一个价格上限,即您愿意为这个实例规格支付的最高价格 。

    • 实例中断模式

      • 直接释放触发中断回收时,您的实例将被直接释放,包括计算资源(vCPU、GPU和内存)、固定公网IP、固定带宽以及云盘(系统盘和数据盘)。

      • 节省停机触发中断回收时,实例进入节省停机模式,计算资源(vCPU、GPU和内存)、固定公网IP和固定带宽被回收,云盘(系统盘和数据盘)、弹性公网IP、快照等资源保留并继续收费。

        说明

        抢占式实例进入节省停机模式后,可能会因为库存不足或者价格浮动超过出价而重启失败。

通过SDK创建

准备工作

  1. 创建AccessKey

    由于阿里云账号(主账号)拥有资源的所有权限,其AccessKey一旦泄露风险巨大,所以建议您使用满足最小化权限需求的RAM用户的AccessKey。获取方法请参见创建AccessKey

  2. RAM用户授予ECS相关权限

    RAM用户授予操作云服务器ECS相关资源的权限。本文提供的示例代码需要创建实例,建议授予以下权限:

    云产品

    授予权限

    云服务器ECS

    AliyunECSFullAccess

  3. 配置访问凭证

    本文示例代码会在系统环境变量中读取AccessKey作为访问云服务的凭证,具体操作步骤请参见Linux、macOSWindows系统配置环境变量

  4. 安装ECS SDK

    获取ECS SDK,本文通过添加Maven依赖的方式来安装。更多安装方式,请参见安装ECS Java SDK

    添加Maven依赖的示例如下:

    <dependencies>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>ecs20140526</artifactId>
            <version>5.3.0</version>
        </dependency>
    </dependencies>

初始化客户端

阿里云SDK支持多种访问凭据用于初始化客户端,例如AccessKeySTS Token等,更多方式请参见管理访问凭据。本示例以通过AccessKey初始化客户端为例。

import com.aliyun.ecs20140526.Client;
import com.aliyun.teaopenapi.models.Config;

public class Sample {
    private static Client createClient() throws Exception {
        Config config = new Config()
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
                // Endpoint 请参考 https://api.aliyun.com/product/Ecs
                .setEndpoint("ecs.cn-hangzhou.aliyuncs.com");
        return new Client(config);
    }
}

构建接口的请求对象

在构建请求对象之前,请查看该接口的API文档获取参数信息。

// 构造请求对象
RunInstancesRequest request = new RunInstancesRequest()
    //设置地域
    .setRegionId("cn-hangzhou")
    //设置付费方式为后付费
    .setInstanceChargeType("PostPaid")
    //设置出价模式为自动出价
    .setSpotStrategy("SpotAsPriceGo")
    //设置实例使用时长为1小时:保证实例运行1小时不被释放
    .setSpotDuration(1)
    //设置实例规格
    .setInstanceType("instanceType")
    //设置镜像
    .setImageId("imageId")
    //设置安全组ID
    .setSecurityGroupId("securityGroupId")
    //设置虚拟交换机ID
    .setVSwitchId("vSwitchId");

发起调用

通过客户端调用OpenAPI时,支持设置运行时参数,例如超时配置、代理配置等,更多信息请查看进阶配置

// 设置运行时参数
RuntimeOptions runtime = new RuntimeOptions();
// 调用 DescribeInstances 接口
RunInstancesResponse response = client.runInstancessWithOptions(request, runtime);
System.out.println(response.body.toMap());

异常处理

Java SDK将异常进行了细致的分类,主要划分为TeaUnretryableExceptionTeaException。

  • TeaUnretryableException:主要是因为网络问题造成,一般是网络问题达到最大重试次数后抛出。

  • TeaException:主要以业务报错为主的异常。

建议采取合理的措施来处理异常,比如合理地传播异常、记录日志、尝试恢复等,以确保系统的健壮性和稳定性。

完整示例

import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.RunInstancesRequest;
import com.aliyun.ecs20140526.models.RunInstancesResponse;
import com.aliyun.ecs20140526.models.RunInstancesResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.tea.TeaUnretryableException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.alibaba.fastjson.JSONArray;

import java.util.Arrays;

public class Sample {
    private static Client createClient() throws Exception {
        Config config = new Config()
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
                // Endpoint 请参考 https://api.aliyun.com/product/Ecs
                .setEndpoint("ecs.cn-hangzhou.aliyuncs.com");
        return new Client(config);
    }

    public static void main(String[] args) {
        try {
            Client client = Sample.createClient();
            // 构造请求对象
            RunInstancesRequest request = new RunInstancesRequest()
                    //设置地域
                    .setRegionId("cn-hangzhou")
                    //设置付费方式为后付费
                    .setInstanceChargeType("PostPaid")
                    //设置出价模式为自动出价
                    .setSpotStrategy("SpotAsPriceGo")
                    //设置实例使用时长为1小时:保证实例运行1小时不被释放
                    .setSpotDuration(1)
                    //设置实例规格
                    .setInstanceType("instanceType")
                    //设置镜像
                    .setImageId("imageId")
                    //设置安全组ID
                    .setSecurityGroupId("securityGroupId")
                    //设置虚拟交换机ID
                    .setVSwitchId("vSwitchId");;
            // 设置运行时参数
            RuntimeOptions runtime = new RuntimeOptions();
            // 调用 RunInstances 接口
            RunInstancesResponse response = client.runInstancesWithOptions(request, runtime);
            System.out.println(response.body.toMap());
        } catch (TeaUnretryableException ue) {
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            ue.printStackTrace();
            // 打印错误信息
            System.out.println(ue.getMessage());
            // 打印请求记录,查询错误发生时的请求信息
            System.out.println(ue.getLastRequest());
        } catch (TeaException e) {
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            e.printStackTrace();
            // 打印错误码
            System.out.println(e.getCode());
            // 打印错误信息,错误信息中包含 RequestId
            System.out.println(e.getMessage());
            // 打印服务端返回的具体错误内容
            System.out.println(e.getData());
        } catch (Exception e) {
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            e.printStackTrace();
        }
    }
}

通过Terraform创建

说明

以下操作步骤示例代码支持一键运行,您可以直接运行代码。一键运行

前提条件

  • 创建AccessKey

    由于阿里云账号(主账号)具有资源的所有权限,防止AK泄露后被任意利用。建议您使用拥有最小化权限的RAM用户的AccessKey进行操作,具体操作方式请参见创建RAM用户创建AccessKey

  • 创建自定义权限策略

    请参考以下示例为RAM用户创建自定义权限策略:允许启动ECS实例、查看ECS实例的详细信息以及查看ECS现货实例的价格历史。具体操作方式请参见创建自定义权限策略

    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ecs:RunInstances",
            "ecs:DescribeInstances",
            "ecs:DescribeSpotPriceHistory"
          ],
          "Resource": "*"
        }
      ]
    }

使用的资源

操作步骤

  1. 打开浏览器,访问Cloud Shell的地址https://shell.aliyun.com,登录Cloud Shell。

    更多Cloud Shell入口及使用,请参见使用云命令行

  2. 创建一个用于存放Terraform资源的项目文件夹,命名为terraform,进入项目目录

    mkdir terraform
    cd terraform
  3. 创建名为main.tf的配置文件,文件内容如下:

    main.tf

    provider "alicloud" {
      region = var.region
    }
    
    # 区域
    variable "region" {
      type    = string
      default = "cn-beijing"
    }
    
    # VPC 名称
    variable "vpc_name" {
      type    = string
      default = "tf_test_fofo"
    }
    
    # VPC CIDR 块
    variable "vpc_cidr_block" {
      type    = string
      default = "172.16.0.0/12"
    }
    
    # vSwitch CIDR 块
    variable "vswitch_cidr_block" {
      type    = string
      default = "172.16.0.0/21"
    }
    
    # 可用区
    variable "availability_zone" {
      type    = string
      default = "cn-beijing-b"
    }
    
    # 安全组名称
    variable "security_group_name" {
      type    = string
      default = "default"
    }
    
    # 实例规格
    variable "instance_type" {
      type    = string
      default = "ecs.n2.small"
    }
    
    # 系统盘类型
    variable "system_disk_category" {
      type    = string
      default = "cloud_efficiency"
    }
    
    # 操作系统镜像
    variable "image_id" {
      type    = string
      default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
    }
    
    # 实例名称
    variable "instance_name" {
      type    = string
      default = "test_fofo"
    }
    
    # 公网带宽
    variable "internet_max_bandwidth_out" {
      type    = number
      default = 10
    }
    
    # 付费类型
    variable "instance_charge_type" {
      type    = string
      default = "PostPaid"
    }
    
    # 抢占式实例出价策略
    variable "spot_strategy" {
      type    = string
      default = "SpotAsPriceGo"
    }
    
    # 抢占式实例的保留时长
    variable "spot_duration" {
      type    = number
      default = 0
    }
    
    # 入站规则端口范围
    variable "port_range" {
      type    = string
      default = "1/65535"
    }
    
    # 入站规则优先级
    variable "priority" {
      type    = number
      default = 1
    }
    
    # 入站规则CIDR
    variable "cidr_ip" {
      type    = string
      default = "0.0.0.0/0"
    }
    
    resource "alicloud_vpc" "vpc" {
      vpc_name       = var.vpc_name
      cidr_block = var.vpc_cidr_block
    }
    
    resource "alicloud_vswitch" "vsw" {
      vpc_id            = alicloud_vpc.vpc.id
      cidr_block        = var.vswitch_cidr_block
      zone_id = var.availability_zone
    }
    
    resource "alicloud_security_group" "default" {
      security_group_name   = var.security_group_name
      vpc_id = alicloud_vpc.vpc.id
    }
    
    resource "alicloud_instance" "instance" {
      availability_zone          = var.availability_zone
      security_groups = [alicloud_security_group.default.id]
      instance_type              = var.instance_type
      system_disk_category       = var.system_disk_category
      image_id                   = var.image_id
      instance_name              = var.instance_name
      vswitch_id                 = alicloud_vswitch.vsw.id
      internet_max_bandwidth_out = var.internet_max_bandwidth_out
      instance_charge_type       = var.instance_charge_type
      spot_strategy              = var.spot_strategy
      spot_duration              = var.spot_duration
    }
    
    resource "alicloud_security_group_rule" "allow_all_tcp" {
      type              = "ingress"
      ip_protocol       = "tcp"
      nic_type          = "intranet"
      policy            = "accept"
      port_range        = var.port_range
      priority          = var.priority
      security_group_id = alicloud_security_group.default.id
      cidr_ip           = var.cidr_ip
    }
  4. 执行terraform init命令,初始化配置。

    image

  5. 执行terraform apply命令,在执行过程中,根据提示输入yes并按下Enter键,等待命令执行完成,若出现以下信息,则表示创建抢占式实例完成。

    image

  6. 执行terraform show命令查询Terraform已创建的资源详细信息。

    image

清理资源

当您不再需要上述通过Terraform创建或管理的资源时,请运行以下命令以释放资源。更多信息,请参见Terraform常用命令

terraform destroy

相关文档

通过其他方式创建抢占式实例

考虑到实际的资源使用场景,阿里云提供了弹性伸缩、容器服务ACK、弹性供应组等产品服务帮助您提高资源创建和资源自动运维效率。

弹性伸缩ESS

适用于需要动态响应业务负载波动,自动混合按量和抢占式实例(比例可调),实例释放时自动补充新实例。更多信息,请参见在伸缩组使用抢占式实例降低成本

容器服务ACK

适用于无状态、容错性较好的应用,Kubernetes集群需要低成本节点池,可以创建抢占式实例节点池。更多信息,请参见抢占式实例节点池最佳实践

弹性供应组

弹性供应是一种快速交付ECS实例集群的方案,简单配置后即可自动在多个可用区内交付不同计费方式(按量付费和抢占式实例)、多种实例规格的实例集合,提升批量交付大量实例的效率。更多信息,请参见弹性供应组配置示例