All Products
Search
Document Center

Elastic Compute Service:Create a custom image from a preemptible instance to be reclaimed and then create a new instance from the custom image to restore the data of the preemptible instance

Last Updated:Jul 08, 2024

Preemptible instances may be forcefully reclaimed due to price fluctuations or changes in market supply and demand. To prevent loss of data stored on a preemptible instance, you can create a custom image from the preemptible instance and configure a monitoring mechanism based on the interruption events of preemptible instances. After an interruption event of the preemptible instance is received, the system can create a new preemptible instance by using the custom image to restore the data of the source preemptible instance.

Working mechanism

When you use a preemptible instance, the instance may be forcefully reclaimed due to price fluctuations or changes in market supply and demand. Before the instance is fully reclaimed, it enters the Locked state and an interruption event is triggered for the instance.

You can configure a monitoring mechanism based on interruption events. After an interruption event of the preemptible instance is received, the system can use the SDK for Java 2.0 code that you compile to create a custom image from the preemptible instance and then create a new preemptible instance from the custom image. This way, the data that is stored on the source preemptible instance can be restored.

The following figure shows the O&M workflow in the preceding scenario.

image

Prerequisites

  • An Alibaba Cloud account is created and the AccessKey pair of the account is obtained.

    You must configure the AccessKey pair of your Alibaba Cloud account to use Alibaba Cloud Elastic Compute Service (ECS) SDK for Java. For information about how to obtain the AccessKey pair, see Obtain an AccessKey pair.

  • The ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. For more information, see the Step 2: Configure environment variables section of the "Get started with Alibaba Cloud Darabonba SDK for Java" topic.

  • ECS SDK for Java 2.0 is installed in the development environment.

    You must add the following dependencies to the Maven project. For more information, see Integrate Alibaba Cloud Darabonba SDK for Java.

    <dependencies>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>ecs20140526</artifactId>
            <version>5.1.8</version>
        </dependency>
    
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>
    
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
    
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
    
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
    
    </dependencies>

Precautions

Important

The sample code in this topic is used for reference only. The custom image creation process and data restoration process may require more than 5 minutes to complete.

After an interruption event is triggered for a preemptible instance, notifications are sent at least 5 minutes before the instance is fully claimed. However, the amount of time required to restore data depends on the image type of the instance and the size of the system disk. The larger the size of the system disk, the longer it takes to restore data. Before you use the sample code, evaluate and verify the effect of data restoration.

Step 1: Create a preemptible instance

In this step, the CreateSpotInstance Java class is used. The following sample code provides an example on how to create a preemptible instance by calling the RunInstances operation:

import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.RunInstancesRequest;
import com.aliyun.ecs20140526.models.RunInstancesResponse;

public class CreateSpotInstance {
    static Client client;
    // Specify the ID of the region in which to create the preemptible instance.  
    static String regionId = "cn-hangzhou";
    // Specify the ID of the zone in which to create the preemptible instance.  
    static String zoneId = "cn-hangzhou-i";
    // Specify the instance type of the preemptible instance. 
    static String instanceType = "ecs.e-c1m1.large";
    // Specify the ID of the image to use to create the preemptible instance. 
    static String imagesId = "aliyun_3_9_x64_20G_alibase_20231219.vhd";
    // Specify the ID of the vSwitch to which to connect the preemptible instance. 
    static String vSwitchId = "<your-vSwitchId>";
    // Specify the ID of the security group to which to assign the preemptible instance. 
    static String securityGroupId = "<your-securityGroupId>";
    // Specify a preemption policy. 
    static String spotStrategy = "SpotAsPriceGo";
    // Specify the protection period of the preemptible instance. If you cannot determine the protection period, set the value to 0. 
    static Integer spotDuration = 0;
    // Specify the logon password of the preemptible instance. 
    static String password = "<your-password>";

    public static void main(String[] args) throws Exception {
        client = createClient();
        createInstance();
    }

    private static Client createClient() throws Exception {
        // If the project code is leaked, the AccessKey pair may be leaked and the security of all resources in your account may be compromised. The following sample code is provided for reference only. 
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
            // Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured in the code runtime environment. 
            .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
            // Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured in the code runtime environment. 
            .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        // Specify an endpoint. For information about endpoints, see the topic at the following URL: https://api.alibabacloud.com/product/Ecs.
        config.endpoint = "ecs-cn-hangzhou.aliyuncs.com";
        return new Client(config);
    }

    // Create a preemptible instance. 
    public static String createInstance() {
        try {
            // Configure the parameters of the RunInstances operation and send the request. 
            RunInstancesRequest request = new RunInstancesRequest();
            request.setRegionId(regionId);
            request.setZoneId(zoneId);
            request.setInstanceType(instanceType);
            request.setSpotDuration(spotDuration);
            request.setSpotStrategy(spotStrategy);
            request.setImageId(imagesId);
            request.setVSwitchId(vSwitchId);
            request.setSecurityGroupId(securityGroupId);
            // The preemption policy takes effect only when the InstanceChargeType parameter is set to PostPaid. 
            request.setInstanceChargeType("PostPaid");
            request.setPassword(password);
            request.setInternetMaxBandwidthOut(1);
            // Obtain the response and the instance ID. 
            RunInstancesResponse response = client.runInstances(request);
            if (null == response.getBody().getInstanceIdSets() || response.getBody().getInstanceIdSets().getInstanceIdSet().isEmpty()) {
                return null;
            }
            String instanceId = response.getBody().getInstanceIdSets().getInstanceIdSet().get(0);
            System.out.println("<Instance ID>:" + instanceId);
            return instanceId;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Step 2: Have a custom image automatically created after an interruption event is detected

In this step, the CreateSpotImage Java class is used. The following operations are called in sequence:

  • Call the DescribeInstances operation to monitor the status of the preemptible instance.

  • After an interruption event of the preemptible instance is detected, call the CreateImage operation to create a custom image from the preemptible instance.

  • After the custom image is created, call the DescribeImages operation to monitor the status of the custom image. After the custom image enters the Available state, a message is returned.

import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.CreateImageRequest;
import com.aliyun.ecs20140526.models.CreateImageResponse;
import com.aliyun.ecs20140526.models.DescribeImagesRequest;
import com.aliyun.ecs20140526.models.DescribeImagesResponse;
import com.aliyun.ecs20140526.models.DescribeImagesResponseBody.DescribeImagesResponseBodyImagesImage;
import com.aliyun.ecs20140526.models.DescribeInstancesRequest;
import com.aliyun.ecs20140526.models.DescribeInstancesResponse;
import com.aliyun.ecs20140526.models.DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstances;
import com.aliyun.ecs20140526.models.DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance;
import com.aliyun.ecs20140526.models.DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstanceOperationLocksLockReason;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;

import com.alibaba.fastjson.JSON;

public class CreateSpotImage {
    static Client client;
    // Specify the region ID of the preemptible instance. 
    static String regionId = "cn-hangzhou";
    // Specify the ID of the preemptible instance. 
    static String instanceId = "<your-instanceId>";

    public static void main(String[] args) throws Exception {
        client = createClient();
        // Step 1: Wait for the preemptible instance to enter the To Be Released state and an interruption event to be generated. 
        waitForInstanceMarked();
        System.out.println("spot instance will be recycled immediately, instance id:" + instanceId);
        // Step 2: Create a custom image from the preemptible instance when an interruption event is triggered for the preemptible instance. 
        String image1 = createImage();
        // Step 3: Wait until the custom image is created. 
        waitCreateImageSuccess(image1);
    }

    private static Client createClient() throws Exception {
        // If the project code is leaked, the AccessKey pair may be leaked and the security of all resources in your account may be compromised. The following sample code is provided for reference only. 
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
            // Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured in the code runtime environment. 
            .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
            // Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured in the code runtime environment. 
            .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        // Specify an endpoint. For information about endpoints, see the topic at the following URL: https://api.alibabacloud.com/product/Ecs.
        config.endpoint = "ecs-cn-hangzhou.aliyuncs.com";
        return new Client(config);
    }

    // Monitor the status of the preemptible instance and obtain the details of the instance when an interruption event occurs. 
    public static void waitForInstanceMarked() {
        // Convert the instance ID to a JSON string. 
        List<String> instanceIds = Lists.newArrayList();
        instanceIds.add(instanceId);
        String instanceIdStr = JSON.toJSONString(instanceIds);
        boolean isMarked = false;
        // Determine whether an interruption event occurs on the preemptible instance. 
        while (!isMarked) {
            try {
                // Configure the parameters of the DescribeInstances operation and send the request. 
                DescribeInstancesRequest request = new DescribeInstancesRequest();
                // Specify the region ID of the preemptible instance. 
                request.setRegionId(regionId);
                // Specify the ID of the preemptible instance to query the details of the instance. 
                request.setInstanceIds(instanceIdStr);
                // Obtain the response. 
                DescribeInstancesResponse response = client.describeInstances(request);
                // Obtain the response data of the preemptible instance. 
                DescribeInstancesResponseBodyInstances instances = response.getBody().getInstances();
                // If the details of the instance are not found, break out of the loop. 
                if (CollectionUtils.isEmpty(instances.getInstance())) {
                    break;
                }
                DescribeInstancesResponseBodyInstancesInstance instance = instances.getInstance().get(0);
                // If the instance is not interrupted, restart the loop. 
                if (instance.getOperationLocks() == null || instance.getOperationLocks().getLockReason().size() == 0) {
                    continue;
                }
                for (DescribeInstancesResponseBodyInstancesInstanceOperationLocksLockReason lockReason : instance
                    .getOperationLocks().getLockReason()) {
                    // If the instance is interrupted, the ID of the instance and the cause of the interruption are displayed. 
                    System.out.println("instance:" + instance.getInstanceId() + "-->lockReason:" + lockReason.getLockReason() + ",vmStatus:" + instance.getStatus());
                    if ("Recycling".equals(lockReason.getLockReason())) {
                        isMarked = true;
                    }
                }

                Thread.sleep(2 * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // Create a custom image. 
    public static String createImage() {
        try {
            // Configure the parameters of the CreateImage operation and send the request. 
            CreateImageRequest request = new CreateImageRequest();
            request.setRegionId(regionId);
            request.setInstanceId(instanceId);
            // Obtain the response and the image ID. 
            CreateImageResponse response = client.createImage(request);
            System.out.println("imageID:" + response.getBody().getImageId());
            return response.getBody().getImageId();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // Check whether the custom image is created. 
    public static void waitCreateImageSuccess(String imageId) {
        boolean isSuccess = false;
        while (!isSuccess) {
            DescribeImagesResponseBodyImagesImage image = describeImage(imageId);
            if (null == image) {
                System.err.println("image not exist. imageId: " + imageId);
                break;
            }
            if ("Available".equals(image.getStatus())) {
                System.out.println("Image created successfully.");
                isSuccess = true;
            }
        }
    }

    // Call the DescribeImages operation to query the status of the custom image. 
    public static DescribeImagesResponseBodyImagesImage describeImage(String imageId) {
        try {
            Thread.sleep(6 * 60 * 1000);
            DescribeImagesRequest imagesRequest = new DescribeImagesRequest();
            imagesRequest.setRegionId(regionId);
            imagesRequest.setImageId(imageId);
            imagesRequest.setPageSize(100);
            DescribeImagesResponse imagesResponse = client.describeImages(imagesRequest);
            if (null == imagesResponse.getBody().getImages() || CollectionUtils.isEmpty(imagesResponse.getBody().getImages().getImage())) {
                return null;
            }
            return imagesResponse.getBody().getImages().getImage().get(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Step 3: Use the custom image to create a preemptible instance to restore data

In this step, the CreateSpotInstanceFromImage Java class is used. The following sample code provides an example on how to use a custom image to create a preemptible instance by calling the RunInstances operation:

import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.RunInstancesRequest;
import com.aliyun.ecs20140526.models.RunInstancesResponse;

public class CreateSpotInstanceFromImage {
    static Client client;
    // Specify the ID of the region in which to create the instance. We recommend that you create the instance in the same region as the source preemptible instance. 
    static String regionId = "cn-hangzhou";
    // Specify the ID of the zone in which to create the instance. We recommend that you create the instance in the same zone as the source preemptible instance. 
    static String zoneId = "cn-hangzhou-i";
    // Specify the instance type of the preemptible instance. 
    static String instanceType = "ecs.s6-c1m1.small";
    // Specify the ID of the custom image. 
    static String imagesId = "<your-imagesId>";
    // Specify the ID of the vSwitch to which to connect the preemptible instance. 
    static String vSwitchId = "<your-vSwitchId>";
    // Specify the ID of the security group to which to assign the preemptible instance. 
    static String securityGroupId = "<your-securityGroupId>";
    // Specify a preemption policy. 
    static String spotStrategy = "SpotAsPriceGo";
    // Specify the protection period of the preemptible instance. If you cannot determine the protection period, set the value to 0. 
    static Integer spotDuration = 0;
    // Specify the logon password of the preemptible instance. 
    static String password = "<your-password>";


    public static void main(String[] args) throws Exception {
        client = createClient();
        createInstance();
    }

    private static Client createClient() throws Exception {
        // If the project code is leaked, the AccessKey pair may be leaked and the security of all resources in your account may be compromised. The following sample code is provided for reference only. 
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
            // Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID environment variable is configured in the code runtime environment. 
            .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
            // Required. Make sure that the ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variable is configured in the code runtime environment. 
            .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        // Specify an endpoint. For information about endpoints, see the topic at the following URL: https://api.alibabacloud.com/product/Ecs.
        config.endpoint = "ecs-cn-hangzhou.aliyuncs.com";
        return new Client(config);
    }

    //Call the RunInstances operation to create an instance. 
    public static String createInstance() {
        try {
            RunInstancesRequest request = new RunInstancesRequest();
            request.setRegionId(regionId);
            request.setZoneId(zoneId);
            request.setInstanceType(instanceType);
            request.setSpotDuration(spotDuration);
            request.setSpotStrategy(spotStrategy);
            request.setImageId(imagesId);
            request.setVSwitchId(vSwitchId);
            request.setSecurityGroupId(securityGroupId);
            request.setInstanceChargeType("PostPaid");
            request.setPassword(password);
            request.setInternetMaxBandwidthOut(1);
            RunInstancesResponse response = client.runInstances(request);
            if (null == response.getBody().getInstanceIdSets() || response.getBody().getInstanceIdSets().getInstanceIdSet().isEmpty()) {
                return null;
            }
            String instanceId = response.getBody().getInstanceIdSets().getInstanceIdSet().get(0);
            System.out.println("<Instance ID>: " + instanceId);;
            return instanceId;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}