本文介绍了如何使用阿里云ECS SDK合理快速地创建并管理抢占式实例。

前提条件

在执行操作之前,您需要:
  • 了解能满足您业务要求的实例规格和地域。
  • 熟悉了解阿里云ECS SDK的基础知识和调用方法。详细信息,请参见SDK使用说明
  • 抢占式实例代码需要依赖的ECS SDK版本4.2.0以上。以Java POM依赖为例,修改引入pom依赖:
    <dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>aliyun-java-sdk-core</artifactId>
      <version>3.2.8</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>aliyun-java-sdk-ecs</artifactId>
      <version>4.2.0</version>
    </dependency>

查询地域及可用的实例规格

使用DescribeZones可以查询创建抢占式实例的地域以及可用的实例规格。示例代码如下所示。

  • OpenApiCaller.java
    public class OpenApiCaller {
      IClientProfile profile;
      IAcsClient client;
      public OpenApiCaller() {
          profile = DefaultProfile.getProfile("cn-hangzhou", AKSUtil.accessKeyId, AKSUtil.accessKeySecret);
          client = new DefaultAcsClient(profile);
      }
      public  <T extends AcsResponse> T doAction(AcsRequest<T> var1) {
          try {
              return  client.getAcsResponse(var1);
          } catch (Throwable e) {
              e.printStackTrace();
              return null;
          }
      }
    }
  • DescribeZonesSample.java
    public class DescribeZonesSample {
      public static void main(String[] args) {
          OpenApiCaller caller = new OpenApiCaller();
          DescribeZonesRequest request = new DescribeZonesRequest();
          request.setRegionId("cn-zhangjiakou");//可以通过DescribeRegionsRequest获取每个地域的RegionId
          request.setSpotStrategy("SpotWithPriceLimit");//对于查询是否可购买抢占式实例此项必填
          request.setInstanceChargeType("PostPaid");//后付费模式,抢占式实例必须是后付费模式
          DescribeZonesResponse response = caller.doAction(request);
          System.out.println(JSON.toJSONString(response));
      }
    }
以下为输出结果,可以查看每个地域可供选择的实例规格、磁盘类型、网络类型等信息。
{
  "requestId": "388D6321-E587-470C-8CFA-8985E2963DAE",
  "zones": [
      {
          "localName": "华北 3 可用区 A",
          "zoneId": "cn-zhangjiakou-a",
          "availableDiskCategories": [
              "cloud_ssd",
              "cloud_efficiency"
          ],
          "availableInstanceTypes": [
              "ecs.e4.large",
              "ecs.n4.4xlarge",
              "ecs.sn2.medium",
              "ecs.i1.2xlarge",
              "ecs.se1.2xlarge",
              "ecs.n4.xlarge",
              "ecs.se1ne.2xlarge",
              "ecs.se1.large",
              "ecs.sn2.xlarge",
              "ecs.se1ne.xlarge",
              "ecs.xn4.small",
              "ecs.sn2ne.4xlarge",
              "ecs.se1ne.4xlarge",
              "ecs.sn1.medium",
              "ecs.n4.8xlarge",
              "ecs.mn4.large",
              "ecs.e4.2xlarge",
              "ecs.mn4.2xlarge",
              "ecs.mn4.8xlarge",
              "ecs.n4.2xlarge",
              "ecs.e4.xlarge",
              "ecs.sn2ne.large",
              "ecs.sn2ne.xlarge",
              "ecs.sn1ne.large",
              "ecs.n4.large",
              "ecs.sn1.3xlarge",
              "ecs.e4.4xlarge",
              "ecs.sn1ne.2xlarge",
              "ecs.e4.small",
              "ecs.i1.4xlarge",
              "ecs.se1.4xlarge",
              "ecs.sn2ne.2xlarge",
              "ecs.sn2.3xlarge",
              "ecs.i1.xlarge",
              "ecs.n4.small",
              "ecs.sn1ne.4xlarge",
              "ecs.mn4.4xlarge",
              "ecs.sn1ne.xlarge",
              "ecs.se1ne.large",
              "ecs.sn2.large",
              "ecs.i1-c5d1.4xlarge",
              "ecs.sn1.xlarge",
              "ecs.sn1.large",
              "ecs.mn4.small",
              "ecs.mn4.xlarge",
              "ecs.se1.xlarge"
          ],
          "availableResourceCreation": [
              "VSwitch",
              "IoOptimized",
              "Instance",
              "Disk"
          ],
          "availableResources": [
              {
                  "dataDiskCategories": [
                      "cloud_ssd",
                      "cloud_efficiency"
                  ],
                  "instanceGenerations": [
                      "ecs-3",
                      "ecs-2"
                  ],
                  "instanceTypeFamilies": [
                      "ecs.mn4",
                      "ecs.sn1",
                      "ecs.sn2",
                      "ecs.sn1ne",
                      "ecs.xn4",
                      "ecs.i1",
                      "ecs.se1",
                      "ecs.e4",
                      "ecs.n4",
                      "ecs.se1ne",
                      "ecs.sn2ne"
                  ],
                  "instanceTypes": [
                      "ecs.n4.4xlarge",
                      "ecs.sn2.medium",
                      "ecs.i1.2xlarge",
                      "ecs.se1.2xlarge",
                      "ecs.n4.xlarge",
                      "ecs.se1ne.2xlarge",
                      "ecs.se1.large",
                      "ecs.sn2.xlarge",
                      "ecs.se1ne.xlarge",
                      "ecs.xn4.small",
                      "ecs.sn2ne.4xlarge",
                      "ecs.se1ne.4xlarge",
                      "ecs.sn1.medium",
                      "ecs.n4.8xlarge",
                      "ecs.mn4.large",
                      "ecs.mn4.2xlarge",
                      "ecs.mn4.8xlarge",
                      "ecs.n4.2xlarge",
                      "ecs.sn2ne.large",
                      "ecs.sn2ne.xlarge",
                      "ecs.sn1ne.large",
                      "ecs.n4.large",
                      "ecs.sn1.3xlarge",
                      "ecs.sn1ne.2xlarge",
                      "ecs.e4.small",
                      "ecs.i1.4xlarge",
                      "ecs.se1.4xlarge",
                      "ecs.sn2ne.2xlarge",
                      "ecs.sn2.3xlarge",
                      "ecs.i1.xlarge",
                      "ecs.n4.small",
                      "ecs.sn1ne.4xlarge",
                      "ecs.mn4.4xlarge",
                      "ecs.sn1ne.xlarge",
                      "ecs.se1ne.large",
                      "ecs.sn2.large",
                      "ecs.i1-c5d1.4xlarge",
                      "ecs.sn1.xlarge",
                      "ecs.sn1.large",
                      "ecs.mn4.small",
                      "ecs.mn4.xlarge",
                      "ecs.se1.xlarge"
                  ],
                  "ioOptimized": true,
                  "networkTypes": [
                      "vpc"
                  ],
                  "systemDiskCategories": [
                      "cloud_ssd",
                      "cloud_efficiency"
                  ]
              }
          ],
          "availableVolumeCategories": [
              "san_ssd",
              "san_efficiency"
          ]
      }
  ]
}

查询抢占式实例的历史价格

使用DescribeSpotPriceHistory查询抢占式实例最近30天的价格变化数据,获得最佳性价比的地域和规格信息,示例代码(DescribeSpotPriceHistorySample.java)如下。

public class DescribeSpotPriceHistorySample {
    public static void main(String[] args) {
        OpenApiCaller caller = new OpenApiCaller();
        List<DescribeSpotPriceHistoryResponse.SpotPriceType> result = new ArrayList<DescribeSpotPriceHistoryResponse.SpotPriceType>();
        int offset = 0;
        while (true) {
            DescribeSpotPriceHistoryRequest request = new DescribeSpotPriceHistoryRequest();
            request.setRegionId("cn-hangzhou");//可以通过DescribeRegionsRequest获取可购买的每个地域的RegionId
            request.setZoneId("cn-hangzhou-b");//可用区必填
            request.setInstanceType("ecs.sn2.medium");//参考DescribeZones返回的实例类型,必填
            request.setNetworkType("vpc");//参考DescribeZones返回的网络类型,必填
//            request.setIoOptimized("optimized");//是否I/O优化类型,DescribeZones返回的IoOptimized,选填
//            request.setStartTime("2017-09-20T08:45:08Z");//价格开始时间,选填,默认3天内数据
//            request.setEndTime("2017-09-28T08:45:08Z");//价格结束时间,选填
            request.setOffset(offset);
            DescribeSpotPriceHistoryResponse response = caller.doAction(request);
            if (response != null && response.getSpotPrices() != null) {
                result.addAll(response.getSpotPrices());
            }
            if (response.getNextOffset() == null || response.getNextOffset() == 0) {
                break;
            } else {
                offset = response.getNextOffset();
            }
        }
        if (!result.isEmpty()) {
            for (DescribeSpotPriceHistoryResponse.SpotPriceType spotPriceType : result) {
                System.out.println(spotPriceType.getTimestamp() + "--->spotPrice:" + spotPriceType.getSpotPrice() + "---->originPrice:" + spotPriceType.getOriginPrice());
            }
            System.out.println(result.size());
        } else {
        }
    }
}
以下为返回结果示例。
2017-09-26T06:28:55Z--->spotPrice:0.24---->originPrice:1.2
2017-09-26T14:00:00Z--->spotPrice:0.36---->originPrice:1.2
2017-09-26T15:00:00Z--->spotPrice:0.24---->originPrice:1.2
2017-09-27T14:00:00Z--->spotPrice:0.36---->originPrice:1.2
2017-09-27T15:00:00Z--->spotPrice:0.24---->originPrice:1.2
2017-09-28T14:00:00Z--->spotPrice:0.36---->originPrice:1.2
2017-09-28T15:00:00Z--->spotPrice:0.24---->originPrice:1.2
2017-09-29T06:28:55Z--->spotPrice:0.24---->originPrice:1.2

重复以上步骤,您可以判断出该规格资源在可用区的价格变化趋势和最近价格。

说明 您可以通过平均价格和最高价格来决定是否可以接受购买该抢占式实例,也可以通过更加合理的数据模型来分析历史价格数据,随时调整创建资源的规格和可用区,达到最佳性价比。

创建抢占式实例

在创建抢占式实例之前,您需要先完成以下工作:

  • 如果您使用自定义镜像创建抢占式实例,必须已经创建自定义镜像。详情请参见CreateImage
  • 在控制台创建安全组,或者使用CreateSecurityGroup创建安全组,并获取安全组ID(SecurityGroupId)。
  • 在控制台创建VPC和交换机,或者使用CreateVpcCreateVSwitch创建,并获取交换机ID(VSwitchId)。

使用CreateInstance创建抢占式实例。示例代码(CreateInstaneSample.java)如下。

public class CreateInstaneSample {
    public static void main(String[] args) {
        OpenApiCaller caller = new OpenApiCaller();
        CreateInstanceRequest request = new CreateInstanceRequest();
        request.setRegionId("cn-hangzhou");//地域ID
        request.setZoneId("cn-hangzhou-b"); //可用区ID
        request.setSecurityGroupId("sg-bp11nhf94ivkdxwb2***");//提前创建的安全组ID
        request.setImageId("centos_7_03_64_20G_alibase_20170818.vhd");//建议选择您自己在该地域准备的自定义镜像
        request.setVSwitchId("vsw-bp164cyonthfudn9kj***");//VPC类型需要交换机ID
        request.setInstanceType("ecs.sn2.medium"); //填入您询价后需要购买的规格
        request.setIoOptimized("optimized");//参见DescirbeZones返回参数
        request.setSystemDiskCategory("cloud_ssd");//参见DescirbeZones返回参数,多选一cloud_ssd, cloud_efficiency, cloud
        request.setSystemDiskSize(40);
        request.setInstanceChargeType("PostPaid");//抢占式实例必须后付费
        request.setSpotStrategy("SpotWithPriceLimit");//SpotWithPriceLimit出价模式,SpotAsPriceGo不用出价,最高按量付费价格
        request.setSpotPriceLimit(0.25F);//SpotWithPriceLimit出价模式生效,您能接受的最高价格,单位为元每小时,必须高于当前的市场成交价才能成功
        CreateInstanceResponse response = caller.doAction(request);
        System.out.println(response.getInstanceId());
    }
}

回收抢占式实例

抢占式实例可能会因为价格因素或者市场供需变化而被强制回收,此时会触发抢占式实例的中断。释放前,抢占式实例会进入锁定状态,提示实例将会被自动回收。您可以针对实例回收状态自动化处理实例的退出逻辑。

目前,您可以通过以下任一种方式来获取抢占式实例的中断锁定状态:

  • 通过实例元数据获取。运行以下命令:
    curl 'http://100.100.100.200/latest/meta-data/instance/spot/termination-time'
    • 如果返回为空,说明实例可持续使用。
    • 如果返回类似2015-01-05T18:02:00Z格式的信息(UTC时间),说明实例将于这个时间释放。
  • 使用DescribeInstances,根据返回的OperationLocks判断实例是否进入待回收状态。代码示例(DescribeInstancesSample.java)如下。
    public class DescribeInstancesSample {
      public static void main(String[] args) throws InterruptedException {
          OpenApiCaller caller = new OpenApiCaller();
          JSONArray allInstances = new JSONArray();
          allInstances.addAll(Arrays.asList("i-bp18hgfai8ekoqwo0***", "i-bp1ecbyds24ij63w1***"));
          while (!allInstances.isEmpty()) {
              DescribeInstancesRequest request = new DescribeInstancesRequest();
              request.setRegionId("cn-hangzhou");
              request.setInstanceIds(allInstances.toJSONString());//指定实例ID,效率最高
              DescribeInstancesResponse response = caller.doAction(request);
              List<DescribeInstancesResponse.Instance> instanceList = response.getInstances();
              if (instanceList != null && !instanceList.isEmpty()) {
                  for (DescribeInstancesResponse.Instance instance : instanceList) {
                      System.out.println("result:instance:" + instance.getInstanceId() + ",az:" + instance.getZoneId());
                      if (instance.getOperationLocks() != null) {
                          for (DescribeInstancesResponse.Instance.LockReason lockReason : instance.getOperationLocks()) {
                              System.out.println("instance:" + instance.getInstanceId() + "-->lockReason:" + lockReason.getLockReason() + ",vmStatus:" + instance.getStatus());
                              if ("Recycling".equals(lockReason.getLockReason())) {
                                  //do your action
                                  System.out.println("spot instance will be recycled immediately, instance id:" + instance.getInstanceId());
                                  allInstances.remove(instance.getInstanceId());
                              }
                          }
                      }
                  }
                  System.out.print("try describeInstances again later ...");
                  Thread.sleep(2 * 60 * 1000);
              } else {
                  break;
              }
          }
      }
    }
触发回收时输出结果如下:
instance:i-bp1ecbyds24ij63w146c-->lockReason:Recycling,vmStatus:Stopped
spot instance will be recycled immediately, instance id:i-bp1ecbyds24ij63w1***

其他操作

您还可以启动、停止、释放抢占式实例。具体的操作与按量付费实例没有区别。可以参见以下API文档: