阿里云竞价实例OpenApi入门实践
目的
本文目标是介绍开发者如何通过阿里云ECS SDK,合理快速的创建需要的SPOT竞价实例
准备工作:
- 首先要熟悉了解阿里云SDK的基础知识和调用方法
- Spot竞价实例代码需要依赖的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>
- 目前竞价实例灰度开放部分用户白名单开放,您可以用您的账号登陆购买页面,确认你的账号是否具备spot购买权限
- 建议您提前通过自定义镜像来保证SOPT实例可以快速开始自己的业务功能。
哪些地域与规格可以购买竞价实例:
典型一个场景,我需要创建一批价格便宜量又足的计算资源,做业务数据处理。
但我不知道去哪个地域去创建需要的计算资源。
我们需要规划下对计算资源的大致需求:
- 地域,创建的竞价实例哪个地域创建。
- 规格 根据你的业务需求确定什么样规格实例
但是由于目前aliyun 每个地域因为售卖比率不同,每个地域可用对外开放售卖的规格存在很大差异。
所以建议通过DesribeZones接口来获取您的账户可创建的地域与规格信息,帮助您选择地域和规格。
- API名称: DesribeZones
- API说明:https://help.aliyun.com/document_detail/25610.html
代码示例:
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"
]
}
]
}
通过查询spot历史价格接口来获得最佳性价比的地域,规格信息
通过上一步我们可以基本确定哪个可用区可以生产的哪些具体规格,有了这些信息就可以通过
DescribeSpotPriceHistory接口查询获取价格历史变化,最多允许获取30天内的价格变化数据。
- API名称:DescribeSpotPriceHistory
- API文档:https://help.aliyun.com/document_detail/60400.html
- 代码实例: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");//是否Io优化类型,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
- 重复以上步骤,您可以判断出该规格资源在可用区的价格变化趋势和最近价格。
- 简单的策略通过平均价格和最高价格来决定是否可以接受购买该spot实例。
- 当然从最小成本出发,可以通过更加合理的数据模型来分析价历史价格数据,随时调整创建资源的规格和可用区,到达最佳性价比。
创建Spot实例
通过以上过程,我们最终得到可以创建的资源的地域信息,规格属性,磁盘,网络等信息。
现在就可以开始真正创建竞价实例了。
- API名称:CreateInstance
- 建议您提前在控制台或者openapi方式 在需要创建vm资源的地区创建好vpc专有网络,获取创建参数vswitchId
建议您提前在控制台或者openapi方式 在需要创建vm资源的地区创建安全组,获取创建参数:SecurityGroupId
- 示例代码: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-bp11nhf94ivkdxwb2gd4");//提前创建的安全组Id
request.setImageId("centos_7_03_64_20G_alibase_20170818.vhd");//建议选择您自己在该地域准备的自定义镜像
request.setVSwitchId("vsw-bp164cyonthfudn9kj5br");//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());
}
}
spot实例中断
- 竞价实例可能会因为价格因素或者其他ecs内部库存因素会强制回收竞价资源,此时会触发spot的中断
- 在真正开始释放vm前 vm 会进入锁定状态, 提示vm 将会被自动回收,您可以针对vm 回收状态来自动化处理实例的退出逻辑
- 目前spot的中断锁定状态可以通过2种方式来感知
vpc 实例可以通过metadata 来获取
metadata访问示例:
curl 'http://100.100.100.200/latest/meta-data/instance/spot/termination-time'
返回格式示例:2015-01-05T18:02:00Z,时间为UTC时间,如果返回为空,说明可持续使用
通过查询 describeInstances 返回的OperationLocks 感知是否vm进入准备回收状态
openapi获取锁定状态代码示例: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-bp18hgfai8ekoqwo0y2n", "i-bp1ecbyds24ij63w146c"));
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-bp1ecbyds24ij63w146c
启动,关机,释放 SPOT竞价实例
启动,停止,释放实例Spot 实例和其他实例没有任何区别,本文不再介绍,请参看相关api文档即可。