すべてのプロダクト
Search
ドキュメントセンター

Elastic Compute Service:SDK を使用してコマンドを実行する

最終更新日:Mar 07, 2025

Cloud Assistant を使用すると、複数の Elastic Compute Service (ECS) インスタンスでコマンドを同時に実行できます。コマンドは、シェル、バッチ、または PowerShell コマンドです。このトピックでは、ECS SDK を使用して Cloud Assistant コマンドを実行し、コマンドの実行結果をクエリする方法について説明します。

前提条件

  • Cloud Assistant コマンドを実行する ECS インスタンスが 実行中 状態であり、インスタンスに クラウドアシスタントクライアント がインストールされていること。Cloud Assistant Agent のインストール方法については、「Cloud Assistant Agent をインストールする」をご参照ください。

  • ALIBABA_CLOUD_ACCESS_KEY_ID および ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境変数が、コードランタイム環境で設定されていること。環境変数の設定方法については、「Linux、macOS、および Windows で環境変数を設定する」をご参照ください。

    説明

    Alibaba Cloud アカウントの AccessKey ペアを保護するために、Resource Access Management (RAM) ユーザーを作成し、RAM ユーザーに ECS リソースへのアクセス権限を付与してから、RAM ユーザーの AccessKey ペアを使用して ECS SDK を呼び出すことをお勧めします。詳細については、「RAM ユーザー」をご参照ください。

  • RAM ユーザーに Cloud Assistant を使用する権限が付与されていること。詳細については、「RAM ユーザーに Cloud Assistant を使用する権限を付与する」をご参照ください。

  • 実行する Cloud Assistant コマンドが準備されていること。コマンドは、シェル、バッチ、または PowerShell コマンドです。

  • プロジェクトに ECS SDK の依存関係がインストールされていること。詳細については、SDK センターの Elastic Compute Service ページをご覧ください。

サンプルシナリオ

最新のクラウドコンピューティング環境では、O&M 管理システムはビジネスの安定性を確保するために不可欠です。ECS インスタンスのパフォーマンスと安定性を確保するために、CPU 使用率、メモリ使用量、ディスク使用量などのリソース使用量を定期的に確認してください。たとえば、自動化された O&M 管理システムを開発するとします。Cloud Assistant のログイン不要機能に基づいて、自動化された O&M 管理システムでは、ECS インスタンスで Cloud Assistant コマンドをリモートで実行して、さまざまな O&M 要件を満たすことができます。自動化された O&M 管理システムは、さまざまなコマンドを ECS インスタンスに渡し、リソース監視、ログ収集、トラブルシューティングなどの複数の機能を実装できます。これにより、O&M 効率が大幅に向上し、効率的なビジネス運用のための信頼性の高いサポートが提供されます。

import com.aliyun.ecs20140526.Client;
import com.aliyun.ecs20140526.models.*;
import com.aliyun.teaopenapi.models.Config;
import com.google.gson.Gson;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;


public class CloudAssistantService {

    /**
     * 環境変数から AccessKey ID と AccessKey シークレットを取得します。
     */
    private static final String ACCESS_KEY_ID = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    private static final String ACCESS_KEY_SECRET = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
    private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
    private static volatile Client ecsClient;

    private CloudAssistantService() {
    }

    /**
     * ECS クライアントを初期化します。
     *
     * @param regionId リージョン ID。ECS クライアントが存在するリージョンを指定するために使用されます。
     * @return 初期化された ECS クライアントを返します。
     * <p>
     * サンプルコードでは、二重チェックロックパターンを使用して、スレッドが安全な方法で単一の ECS クライアントを作成することを保証しています。
     * ECS クライアントが既に存在するかどうかを確認します。ECS クライアントが存在しない場合は、同期ブロックでもう一度確認し、ECS クライアントを作成します。
     */
    public static Client getEcsClient(String regionId) throws Exception {
        if (ecsClient == null) {
            synchronized (CloudAssistantService.class) {
                if (ecsClient == null) {
                    Config config = new Config().setAccessKeyId(ACCESS_KEY_ID).setAccessKeySecret(ACCESS_KEY_SECRET).setRegionId(regionId);
                    ecsClient = new Client(config);
                }
            }
        }
        return ecsClient;
    }

    public static void main(String[] args_) {
        try {
            // リージョン ID。
            String regionId = "cn-chengdu";
            getEcsClient(regionId);
            // コマンドを実行する ECS インスタンスの ID。
            List<String> instanceIds = Arrays.asList("i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot");
            // コマンドの内容。
            String commandContent = "#!/bin/bash\n cat /proc/meminfo";
            // コマンドの実行タイムアウト期間。
            long commandTimeOut = 60;


            // コマンドを実行します。
            String invokeId = runCommand(commandContent, regionId, instanceIds, commandTimeOut);
            // コマンドの実行結果をクエリします。
            DescribeInvocationsResponse invocationResult = describeInvocations(regionId, invokeId, commandTimeOut);
            System.out.println("コマンドの実行結果: " + new Gson().toJson(invocationResult));
            // サンプルコードにはログ設定が含まれていません。

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            SCHEDULER.shutdown();
        }
    }

    /**
     * 指定された ECS インスタンスでコマンドを実行します。
     *
     * @param commandContent コマンドの内容。
     * @param regionId       ECS インスタンスが存在するリージョンの ID。
     * @param instanceIds    コマンドを実行する ECS インスタンスの ID。
     * @param commandTimeOut コマンドの実行タイムアウト期間。
     * @return コマンドのタスク ID を返します。
     */
    public static String runCommand(String commandContent, String regionId, List<String> instanceIds, long commandTimeOut) {
        try {
            System.out.println("runCommand start...");
            RunCommandRequest request = new RunCommandRequest();
            request.setRegionId(regionId);
            request.setType(Constants.COMMAND_TYPE.RUN_SHELL_SCRIPT);
            request.setCommandContent(commandContent);
            request.setInstanceId(instanceIds);
            request.setTimeout(commandTimeOut);
            RunCommandResponse runCommandResponse = ecsClient.runCommand(request);
            return runCommandResponse.body.invokeId;
        } catch (Exception e) {
            throw new RuntimeException("runCommand failed", e);
        }
    }

    /**
     * Cloud Assistant コマンドの実行結果をクエリします。
     *
     * @param regionId       リージョン ID。実行結果が属するリージョンを指定するために使用されます。
     * @param invokeId       タスク ID。タスクを一意に識別します。
     * @param commandTimeOut コマンドの実行タイムアウト期間。
     */
    public static DescribeInvocationsResponse describeInvocations(String regionId, String invokeId, long commandTimeOut) {
        DescribeInvocationsRequest describeInvocationsRequest = new DescribeInvocationsRequest()
                .setRegionId(regionId)
                .setInvokeId(invokeId);

        long delay = 2;
        // 最大再試行回数を指定します。
        int maxRetries = (int) (commandTimeOut / delay);
        int retryCount = 0;

        try {
            while (retryCount < maxRetries) {
                ScheduledFuture<DescribeInvocationsResponse> future = SCHEDULER.schedule(() ->
                        ecsClient.describeInvocations(describeInvocationsRequest), delay, TimeUnit.SECONDS);
                DescribeInvocationsResponse results = future.get();
                List<DescribeInvocationsResponseBody.DescribeInvocationsResponseBodyInvocationsInvocation> invocationList = results.body.invocations.invocation;
                if (invocationList.isEmpty()) {
                    throw new RuntimeException("コマンドの実行結果が見つかりませんでした。");
                }
                DescribeInvocationsResponseBody.DescribeInvocationsResponseBodyInvocationsInvocation invocationResult = results.body.invocations.invocation.get(0);
                String invocationStatus = invocationResult.invocationStatus;
                switch (invocationStatus) {
                    case Constants.INVOCATION_STATUS.PENDING:
                    case Constants.INVOCATION_STATUS.RUNNING:
                    case Constants.INVOCATION_STATUS.STOPPING:
                        retryCount++;
                        continue;
                    default:
                        return results;
                }
            }
            throw new RuntimeException("コマンドの実行結果の最大再試行回数を超えました。");
        } catch (Exception e) {
            throw new RuntimeException("describeInvocationResults failed", e);
        }
    }


    public static class Constants {
        // コマンドのタイプ。
        public static final class COMMAND_TYPE {
            // シェルコマンド。Windows インスタンスに適用されます。
            public static final String RUN_SHELL_SCRIPT = "RunShellScript";
            // バッチコマンド。Windows インスタンスに適用されます。
            public static final String RUN_BAT_SCRIPT = "RunBatScript";
            // PowerShell コマンド。Windows インスタンスに適用されます。
            public static final String RUN_POWERSHELL_SCRIPT = "RunPowerShellScript";
        }

        // Cloud Assistant コマンドの実行結果。
        public static final class INVOCATION_STATUS {
            // システムはコマンドを確認または送信しています。
            public static final String PENDING = "Pending";
            // コマンドは ECS インスタンスで実行されています。
            public static final String RUNNING = "Running";
            // コマンドは停止されています。
            public static final String STOPPING = "Stopping";
        }
    }
}
import os
import time
import logging
from alibabacloud_ecs20140526 import models as ecs_20140526_models
from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
from alibabacloud_tea_openapi import models as open_api_models

# ログを設定します。
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

ACCESS_KEY_ID = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
ACCESS_KEY_SECRET = os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")

if not ACCESS_KEY_ID or not ACCESS_KEY_SECRET:
    raise EnvironmentError(
        "必須の環境変数がありません: ALIBABA_CLOUD_ACCESS_KEY_ID と ALIBABA_CLOUD_ACCESS_KEY_SECRET")


def get_ecs_client(region_id):
    config = open_api_models.Config(
        access_key_id=ACCESS_KEY_ID,
        access_key_secret=ACCESS_KEY_SECRET,
        region_id=region_id
    )
    return Ecs20140526Client(config)


def execute_command(client, command_content, region_id, instance_ids, command_timeout, command_type):
    if not instance_ids:
        raise ValueError("インスタンス ID リストは空にできません。")

    valid_command_types = ["RunShellScript", "RunBatScript", "RunPowerShellScript"]
    if command_type not in valid_command_types:
        raise ValueError(f"無効なコマンドタイプ: {command_type}。有効なタイプは {valid_command_types} です。")

    request = ecs_20140526_models.RunCommandRequest()
    request.region_id = region_id
    request.type = command_type
    request.command_content = command_content
    request.instance_ids = instance_ids
    request.timeout = command_timeout

    try:
        run_command_response = client.run_command(request)
        return run_command_response.to_map()['body']['InvokeId']
    except Exception as e:
        logging.error(f"コマンドの実行に失敗しました: {e}")
        raise


def query_invocations(client, region_id, invoke_id):
    request = ecs_20140526_models.DescribeInvocationsRequest()
    request.region_id = region_id
    request.invoke_ids = [invoke_id]

    try:
        describe_invocations_response = client.describe_invocations(request)
        return describe_invocations_response.to_map()['body']
    except Exception as e:
        logging.error(f"呼び出しのクエリに失敗しました: {e}")
        raise


def wait_for_command_completion(client, region_id, invoke_id, max_retries, backoff_factor=2):
    retry_count = 0
    while retry_count < max_retries:
        time.sleep(backoff_factor ** retry_count)
        results = query_invocations(client, region_id, invoke_id)
        invocation_list = results.get('Invocations', {}).get('Invocation', [])
        if not invocation_list:
            raise RuntimeError("コマンドの実行結果が見つかりませんでした。")

        invocation_result = invocation_list[0]
        invocation_status = invocation_result.get('InvocationStatus')
        logging.info(f"現在の呼び出しステータス: {invocation_status}")

        if invocation_status == "Finished":
            print("query_invocations result:", results)
            break
        elif invocation_status in ["Failed", "Stopped"]:
            raise RuntimeError(f"コマンドの実行はステータス {invocation_status} で失敗しました")
        else:
            retry_count += 1
    else:
        raise TimeoutError("コマンドの実行がタイムアウトしました。")


def main():
    # リージョン ID。
    region_id = "cn-chengdu"
    # コマンドを実行する ECS インスタンスの ID。
    instance_ids = ["i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot"]
    # コマンドの内容。
    command_content = "#!/bin/bash\n cat /proc/meminfo"
    # コマンドの実行タイムアウト期間。
    command_timeout = 60
    # コマンドのタイプ。有効な値: RunShellScript、RunBatScript、RunPowerShellScript。
    command_type = "RunShellScript"

    client = get_ecs_client(region_id)
    invoke_id = execute_command(client, command_content, region_id, instance_ids, command_timeout, command_type)

    max_retries = max(int(command_timeout // 2), 1)
    wait_for_command_completion(client, region_id, invoke_id, max_retries)


if __name__ == "__main__":
    main()