全部产品
Search
文档中心

云服务器 ECS:创建抢占式实例

更新时间:Jul 13, 2023

本文介绍了如何通过阿里云ECS Java SDK调用DescribeAvailableResource和DescribeSpotPriceHistory查询抢占式实例的供应情况和历史价格,再调用CreateInstance创建抢占式ECS实例。

前提条件

  • 您使用的aliyun-java-sdk-ecs版本必须大于等于4.2.0。

  • 创建抢占式ECS实例的镜像必须包含了实例运行的所有环境依赖。建议您使用自定义镜像创建实例,保证抢占式ECS实例能快速处理业务数据。假设镜像ID为m-bp146shijn7hujkui9***

  • 调用DescribeRegions查询ECS实例启动的阿里云地域,假设为cn-hangzhou

  • 调用DescribeInstanceTypes查询ECS实例使用的实例规格,假设为ecs.g5.large

背景信息

抢占式实例能够帮助您建立低价处理业务数据的方案。创建抢占式实例时,您需要指定出价模式,当指定的实例规格的当前市场价格低于您的出价时,您可以成功创建抢占式实例,并按当前市场价格计费。抢占式实例常被运用于控制计算成本、适用于无状态的应用场景。更多信息,请参见抢占式实例概述

操作步骤

  1. 封装一个ApiCaller.java类,完成初始化profile和client,以及错误处理等功能。

    public class ApiCaller {
        IClientProfile profile;
        IAcsClient client;
    
        public ApiCaller() {
            // 请确保代码运行环境设置了环境变量ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
            // 工程代码泄露可能会导致AccessKey泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取AccessKey的方式进行调用,建议使用更安全的STS方式。
            profile = DefaultProfile.getProfile("cn-hangzhou", System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
            client = new DefaultAcsClient(profile);
        }
    
        public  <T extends AcsResponse> T doAction(AcsRequest<T> var1) {
            try {
                return  client.getAcsResponse(var1);
            } catch (ServerException e) {
                e.printStackTrace();
            } catch (ClientException e) {
                System.out.println("ErrCode:" + e.getErrCode());
                System.out.println("ErrMsg:" + e.getErrMsg());
                System.out.println("RequestId:" + e.getRequestId());
            }
            return null;
        }
     }
  2. 调用DescribeAvailableResource查询指定地域中的抢占式实例提供的实例规格。

    public class DescribeAvailableResourceSample {
    
        public static void main(String[] args) {
    
            ApiCaller caller = new ApiCaller();
            DescribeAvailableResourceRequest request = new DescribeAvailableResourceRequest();
            // 通过DescribeRegionsRequest获取地域ID。
            request.setRegionId("cn-hangzhou");
            request.setDestinationResource("InstanceType");
            // 设置为按量付费,必填。
            request.setInstanceChargeType("PostPaid");
            // 设定抢占式实例的出价策略,必填。
            request.setSpotStrategy("SpotAsPriceGo");
            // 不设置可用区ID则默认查询所有可以创建抢占式实例的可用区。
            request.setZoneId("cn-hangzhou-h");
            // 不设置实例规格则默认查询所有可以创建抢占式实例的实例规格。
            request.setInstanceType("ecs.g5.large");
            request.setSystemDiskCategory("cloud_ssd");
            request.setNetworkCategory("vpc");
    
            DescribeAvailableResourceResponse response = caller.doAction(request);
            System.out.println(JSON.toJSONString(response));
        }
    }

    输出结果示例:

    {
        "RequestId": "D8491D5E-AB8A-4E22-BDB4-EEEE1F1C8241",
        "AvailableZones": {
            "AvailableZone": [
                {
                    "Status": "Available",
                    "RegionId": "cn-hangzhou",
                    "AvailableResources": {
                        "AvailableResource": [
                            {
                                "Type": "InstanceType",
                                "SupportedResources": {
                                    "SupportedResource": [
                                        {
                                            "Status": "Available",
                                            "Value": "ecs.g5.large",
                                            "StatusCategory": "WithStock"
                                        }
                                    ]
                                }
                            }
                        ]
                    },
                    "ZoneId": "cn-hangzhou-h",
                    "StatusCategory": "WithStock"
                }
            ]
        }
    }
  3. (可选)调用DescribeSpotPriceHistory查询抢占式实例历史价格变化。

    说明

    DescribeSpotPriceHistory最多允许获取30天内的价格变化。

    public class DescribeSpotPriceHistorySample {
    
        public static void main(String[] args) {
    
            ApiCaller caller = new ApiCaller();
            List<DescribeSpotPriceHistoryResponse.SpotPriceType> result = new ArrayList<DescribeSpotPriceHistoryResponse.SpotPriceType>();
            int offset = 0;
            while (true) {
                DescribeSpotPriceHistoryRequest request = new DescribeSpotPriceHistoryRequest();
                // 地域必填。
                request.setRegionId("cn-hangzhou");
                // 可用区必填。
                request.setZoneId("cn-hangzhou-b");
                // 实例规格必填。
                request.setInstanceType("ecs.g5.large");
                // 网络类型必填。
                request.setNetworkType("vpc");
                // 查询历史价格的起始时间,选填。默认获取结束时间前3小时内的数据。
                // request.setStartTime("2017-09-20T08:45:08Z");
                // 查询历史价格的结束时间,选填。如果没填结束时间,默认结束时间是当前时间。
                // 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
  4. 调用CreateInstance创建一台抢占式实例。

    如果您需要批量创建多台抢占式实例,可以调用RunInstances。具体操作,请参见批量创建ECS实例

    public class CreateInstanceSample {
    
        public static void main(String[] args) {
    
            ApiCaller caller = new ApiCaller();
    
            CreateInstanceRequest request = new CreateInstanceRequest();
            // 地域ID。
            request.setRegionId("cn-hangzhou");
            // 可用区ID。
            request.setZoneId("cn-hangzhou-h"); 
            // 安全组ID。
            request.setSecurityGroupId("sg-bp11nhf94ivkdxwb2***");
            // 建议您使用自定义镜像,可以快速承载业务。
            request.setImageId("m-bp146shijn7hujkui9***");
            // 与安全组同属于一个专有网络VPC的虚拟交换机ID。
            request.setVSwitchId("vsw-bp164cyonthfudn9kj***");
    
            // 填入您询价后需要购买的规格。
            request.setInstanceType("ecs.g5.large"); 
    
            // 系统盘类型,支持cloud_essd,cloud_ssd,cloud_efficiency,cloud。
            request.setSystemDiskCategory("cloud_ssd");
            request.setSystemDiskSize(40);
    
            // 设置为按量付费,必填。
            request.setInstanceChargeType("PostPaid");
            // SpotWithPriceLimit表示出价模式。您还可以设置为SpotAsPriceGo,表示不用出价,价格上限为按量付费价格,则无需设置SpotPriceLimit。
            request.setSpotStrategy("SpotWithPriceLimit");
            // 在SpotStrategy设置的SpotWithPriceLimit出价模式中,您能接受的最高价格,必须高于市场成交价才能运行实例。
            request.setSpotPriceLimit(0.25F);
    
            // 创建两台抢占式实例,否则默认创建一台实例。
            // request.setAmount(2);
    
            CreateInstanceResponse response = caller.doAction(request);
            System.out.println(response.getInstanceId());
    
        }
    }
  5. 查询抢占式实例的回收时间。

    重要

    由于抢占式实例存在回收机制,建议您在创建抢占式实例的同时建立服务中断处理程序或者负载均衡机制,避免抢占式实例被释放给您业务带来影响。

    • 方式一:在ECS实例操作系统内运行以下命令,通过实例元数据查询抢占式实例的释放时间。

      curl http://100.100.100.200/latest/meta-data/instance/spot/termination-time
      说明

      如果返回信息为空,说明抢占式实例可以继续被使用。如果返回类似2023-07-05T18:02:00Z格式的信息(UTC+0时间),说明抢占式实例将于该时间点被回收。

    • 方式二:调用DescribeInstances根据返回的OperationLocks判断实例是否进入待回收状态。

      public class DescribeInstancesSample {
        public static void main(String[] args) throws InterruptedException {
      
            ApiCaller caller = new ApiCaller();
            JSONArray allInstances = new JSONArray();
            allInstances.addAll(Arrays.asList("i-bp18hgfai8ekoqwo0***", "i-bp1ecbyds24ij63w1***"));
      
            while (!allInstances.isEmpty()) {
      
                DescribeInstancesRequest request = new DescribeInstancesRequest();
                request.setRegionId("cn-hangzhou");
                // 设置具体的实例ID可以提高效率。
                request.setInstanceIds(allInstances.toJSONString());
                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() + ",was created in zone:" + instance.getZoneId());
                        if (instance.getOperationLocks() != null) {
                            for (DescribeInstancesResponse.Instance.LockReason lockReason : instance.getOperationLocks()) {
                                System.out.println("instance: " + instance.getInstanceId() + "-->lockReason:" + lockReason.getLockReason() + ",instanceStatus:" + instance.getStatus());
                                if ("Recycling".equals(lockReason.getLockReason())) {
                                    // 输出回收信息并从实例ID列表中删除被回收的实例ID。
                                    System.out.println("Preemptible instance will be recycled immediately, instance id: " + instance.getInstanceId());
                                    allInstances.remove(instance.getInstanceId());
                                }
                            }
                        }
                    }
                    System.out.print("Try describeInstancesRequest again later...");
                    Thread.sleep(2 * 60 * 1000);
                } else {
                    break;
                }
            }
        }
      }

      触发回收抢占式实例输出结果如下:

      instance: i-bp1ecbyds24ij63w1***-->lockReason:Recycling,instanceStatus:Stopped
      Preemptible instance will be recycled immediately, instance id: i-bp1ecbyds24ij63w1***
    • 方式三:通过云监控API DescribeSystemEventAttribute查询。

      public class DescribeSystemEventAttribute {
      
        public static void main(String[] args) {
              // 请确保代码运行环境设置了环境变量ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
              // 工程代码泄露可能会导致AccessKey泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取AccessKey的方式进行调用,建议使用更安全的STS方式。
          DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
          IAcsClient client = new DefaultAcsClient(profile);
      
          DescribeSystemEventAttributeRequest request = new DescribeSystemEventAttributeRequest();
          // 地域ID。
          request.setRegionId("cn-hangzhou");
          // 云产品取值,即Product。
          request.setProduct("ecs");
          // 查询的开始时间点。
          request.setStartTime("1574064240000");
      
          try {
            DescribeSystemEventAttributeResponse response = client.getAcsResponse(request);
            System.out.println(new Gson().toJson(response));
          } catch (ServerException e) {
            e.printStackTrace();
          } catch (ClientException e) {
            System.out.println("ErrCode:" + e.getErrCode());
            System.out.println("ErrMsg:" + e.getErrMsg());
            System.out.println("RequestId:" + e.getRequestId());
          }
      
        }
      }

      触发回收抢占式实例输出结果如下:

      {
       "SystemEvents": {
        "SystemEvent": [
         {
          "Name": "Instance:PreemptibleInstanceInterruption",
          "Status": "Normal",
          "Time": 1574064244000,
          "Product": "ECS",
          "ResourceId": "acs:ecs:cn-hangzhou:133160284996****:instance/i-bp19w6o2tqa9jvoh8***",
          "RegionId": "cn-hangzhou",
          "Level": "WARN",
          "Content": "{\"action\":\"delete\",\"instanceId\":\"i-bp19w6o2tqa9jvoh8***\"}",
          "GroupId": "0",
          "InstanceName": "i-bp19w6o2tqa9jvoh8***"
         }
        ]
       },
       "Message": "200",
       "RequestId": "D4730844-EF73-4856-BFF3-1BDAE8E54C81",
       "Success": true,
       "Code": "200"
      }

相关文档