All Products
Search
Document Center

Elastic Compute Service:Create a custom image from a preemptible instance

Last Updated:Mar 29, 2024

Preemptible instances may be forcefully reclaimed due to price fluctuations or changes in market supply and demand. To avoid loss of data stored on a preemptible instance, you can create a custom image from the preemptible instance and then create a preemptible instance from the custom image to restore the data. This topic describes how to use Java code to configure the system to create a custom image from a preemptible instance when an interruption event of the instance is detected before the reclamation of the instance. Then, you can create a preemptible instance from the custom image to restore the data that is stored on the source preemptible instance. In the example of this topic, Alibaba Cloud Elastic Compute Service (ECS) SDK for Java is used.

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 preemptible instance is reclaimed, it enters the Locked state and an interruption event is triggered for the instance.

You can set a monitoring mechanism based on the interruption event. After you receive an interruption event of the preemptible instance, you can use Java code to create a custom image from the instance and then create a preemptible instance by using the custom image. This way, data stored on the source preemptible instance can be restored.

The following figure shows the O&M workflow in this example.

image

Prerequisites

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

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

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

    You must add the following dependencies to the Maven project. For more information, see Install and use ECS SDKs.

    <dependencies>
           <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.83</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-ecs</artifactId>
                <version>4.23.10</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.0.8</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.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.ecs.model.v20140526.RunInstancesRequest;
import com.aliyuncs.ecs.model.v20140526.RunInstancesResponse;
import com.aliyuncs.profile.DefaultProfile;

/**
 * Call the RunInstances operation to create a preemptible instance. 
 */
public class CreateSpotInstance {

    static IAcsClient 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.s6-c1m1.small";
    // Specify the ID of the image to use to create the preemptible instance. 
    static String imagesId = "centos_7_6_x64_20G_alibase_20211130.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) {
        client = initialization();
        createInstance();
    }

    private static IAcsClient initialization() {
        /**
         * Initialize request parameters. 
         * Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. 
         * 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 shows how to use environment variables to obtain an AccessKey pair and use the AccessKey pair to call API operations. We recommend that you use Security Token Service (STS) tokens, which provide higher security. 
         */
        DefaultProfile profile = DefaultProfile.getProfile(regionId, System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        return new DefaultAcsClient(profile);
    }

    // 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 InstanceChargeType is set to PostPaid. 
            request.setInstanceChargeType("PostPaid");
            request.setPassword(password);
            request.setInternetMaxBandwidthOut(1);
            // Obtain the response and the instance ID. 
            RunInstancesResponse response = client.getAcsResponse(request);
            if (null == response.getInstanceIdSets() || response.getInstanceIdSets().isEmpty()) {
                return null;
            }
            String instanceId = response.getInstanceIdSets().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 state 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 state of the custom image. After the custom image enters the Available state, a message is returned.

import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.ecs.model.v20140526.*;
import com.aliyuncs.profile.DefaultProfile;
import java.util.ArrayList;
import java.util.List;

/**
 * Monitor the interruption events of the preemptible instance. A custom image is automatically created from the preemptible instance when an interruption event occurs. 
 * The following operations are called in the Java code:
 * DescribeInstances: queries the details of the instance.
 * CreateImage: creates a custom image.
 * DescribeImages: queries the state of the custom image.
 */
public class CreateSpotImage {
    static IAcsClient 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) {
        client = initialization();
        // 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 IAcsClient initialization() {
        /**
         * Initialize request parameters. 
         * Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. 
         * 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 shows how to use environment variables to obtain an AccessKey pair and use the AccessKey pair to call API operations. We recommend that you use Security Token Service (STS) tokens, which provide higher security. 
         */
        DefaultProfile profile = DefaultProfile.getProfile(regionId, System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        return new DefaultAcsClient(profile);
    }

    // Monitor the state 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. 
        ArrayList<String> instanceIds = new ArrayList();
        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.getAcsResponse(request);
                // Obtain the response data of the preemptible instance. 
                List<DescribeInstancesResponse.Instance> instanceList = response.getInstances();
                // If the details of the instance are not found, break out of the loop. 
                if (instanceList == null || instanceList.isEmpty()) {
                    break;
                }
                DescribeInstancesResponse.Instance instance = instanceList.get(0);
                // If the instance is not interrupted, restart the loop. 
                if (instance.getOperationLocks() == null || instance.getOperationLocks().size() == 0) {
                    continue;
                }
                for (DescribeInstancesResponse.Instance.LockReason lockReason : instance.getOperationLocks()) {
                    // 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.getAcsResponse(request);
            System.out.println("imageID:" + response.getImageId());
            return response.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) {
            DescribeImagesResponse.Image 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 state of the custom image. 
    public static DescribeImagesResponse.Image 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.getAcsResponse(imagesRequest);
            if (null == imagesResponse.getImages() || imagesResponse.getImages().isEmpty()) {
                return null;
            }
            return imagesResponse.getImages().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 the custom image to create a preemptible instance by calling the RunInstances operation:

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.ecs.model.v20140526.RunInstancesRequest;
import com.aliyuncs.ecs.model.v20140526.RunInstancesResponse;
import com.aliyuncs.profile.DefaultProfile;

/**
 *Call the RunInstances operation to create a preemptible instance. 
 */
public class CreateSpotInstanceFromImage {

    static IAcsClient 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-shanghai-l";
    // 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-passwd>";

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

    private static IAcsClient initialization() {
        /**
         * Initialize request parameters. 
         * Make sure that the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET environment variables are configured. 
         * 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 shows how to use environment variables to obtain an AccessKey pair and use the AccessKey pair to call API operations. We recommend that you use Security Token Service (STS) tokens, which provide higher security. 
         */
        DefaultProfile profile = DefaultProfile.getProfile(regionId, System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        return new DefaultAcsClient(profile);
    }

    //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.getAcsResponse(request);
            if (null == response.getInstanceIdSets() || response.getInstanceIdSets().isEmpty()) {
                return null;
            }
            String instanceId = response.getInstanceIdSets().get(0);
            System.out.println("Instance ID: " + instanceId);
            return instanceId;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}