クラウドアシスタントを使用すると、複数の Elastic Compute Service (ECS) インスタンスに個別にログインすることなく、シェル、バッチ、または PowerShell コマンドを同時に実行できます。このトピックでは、ECS SDK を使用してクラウドアシスタントのコマンドを実行し、その実行結果を取得する方法について説明します。
利用シーン
クラウドコンピューティング環境では、O&M (運用保守) 管理システムがビジネスの安定性を維持するために不可欠です。ECS インスタンスを正常な状態に保つには、CPU 使用率、メモリ消費量、ディスク使用率などのリソース使用量を定期的に確認する必要があります。
自動化された O&M 管理システムの構築を考えてみましょう。クラウドアシスタントのログイン不要機能 (インスタンスにログインせずにコマンドを実行する機能) を活用することで、このシステムは ECS インスタンス上でクラウドアシスタントのコマンドをリモート実行し、さまざまな O&M タスクを処理できます。インスタンスに異なるコマンドを渡すことで、リソースのモニタリング、ログ収集、トラブルシューティングなどの機能を実行できます。このアプローチにより、O&M 効率が大幅に向上し、ビジネス運用に信頼性の高いサポートを提供します。
前提条件
開始する前に、以下の要件が満たされていることを確認してください:
対象の ECS インスタンスが 実行中 の状態であり、各インスタンスにクラウドアシスタントエージェントがインストールされていること。インストール手順については、「クラウドアシスタントエージェントのインストール」をご参照ください。
実行環境で環境変数
ALIBABA_CLOUD_ACCESS_KEY_IDとALIBABA_CLOUD_ACCESS_KEY_SECRETが設定されていること。設定手順については、「Linux、macOS、および Windows で環境変数を設定する」をご参照ください。説明Alibaba Cloudアカウントを保護するため、リソースアクセス管理 (RAM) ユーザーを作成し、ECS リソースへのアクセスに必要な権限を付与してから、RAM ユーザーの AccessKey ペアを使用して ECS SDK の操作を呼び出すことを推奨します。詳細については、「RAM ユーザー」をご参照ください。
RAM ユーザーにクラウドアシスタントを使用する権限が付与されていること。詳細については、「RAM ユーザーへのクラウドアシスタントの使用権限の付与」をご参照ください。
実行したいクラウドアシスタントのコマンドが準備されていること。コマンドは、シェル、バッチ、または PowerShell コマンドのいずれかです。
プロジェクトに ECS SDK の依存関係がインストールされていること。詳細については、SDK センターの「Elastic Compute Service」ページをご参照ください。
使用する API 操作
このトピックでは、以下の ECS API 操作を使用します:
| 操作 | 説明 |
|---|---|
| RunCommand | 1 つ以上の ECS インスタンスでコマンドを実行します。コマンドタスクを識別する InvokeId を返します。 |
| DescribeInvocations | InvokeId に基づいて、クラウドアシスタントコマンドの実行結果をクエリします。 |
コマンドタイプ
クラウドアシスタントは、RunCommand リクエストの Type パラメーターで指定される 3 種類のコマンドタイプをサポートしています:
| Type の値 | 説明 |
|---|---|
RunShellScript | シェルコマンド。Linux インスタンスに適用されます。 |
RunBatScript | バッチコマンド。Windows インスタンスに適用されます。 |
RunPowerShellScript | PowerShell コマンド。Windows インスタンスに適用されます。 |
呼び出しステータスの値
コマンドの実行結果をポーリングすると、InvocationStatus フィールドにコマンドの現在の状態が表示されます:
| ステータス | 説明 |
|---|---|
Pending | システムがコマンドを検証または送信中です。 |
Running | コマンドが ECS インスタンスで実行中です。 |
Stopping | コマンドが停止処理中です。 |
Finished | コマンドは正常に完了しました。 |
Failed | コマンドの実行に失敗しました。 |
Stopped | コマンドは停止されました。 |
サンプルコード
以下のサンプルコードは、ECS SDK を使用して ECS インスタンスでコマンドを実行し、実行結果をポーリングする方法を示しています。Java と Python の両方の例が提供されています。
Java
この Java の例では、CloudAssistantService クラスを作成します。このクラスは、ECS クライアントを初期化し、指定されたインスタンスでコマンドを実行し、コマンドが完了するかタイムアウトするまで結果をポーリングします。
主な動作:
double-checked locking パターンを使用して、スレッドセーフなシングルトンの ECS クライアントを作成します。
指定されたインスタンスでシェルコマンド (
cat /proc/meminfo) を実行します。最大
commandTimeOut / delay回のリトライまで、2 秒間隔で実行結果をポーリングします。
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 Secret を読み取ります。
*/
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 ECS クライアントが接続するリージョン ID。
* @return 初期化された ECS クライアント。
* <p>
* このメソッドは、double-checked locking パターンを使用して、スレッドセーフな
* ECS クライアントのシングルトン作成を保証します。まずクライアントが
* 既に存在するかどうかを確認し、次に synchronized ブロック内で再確認してから作成します。
*/
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("The command execution result:" + 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 (InvokeId)。
*/
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);
}
}
/**
* クラウドアシスタントコマンドの実行結果をクエリします。
*
* @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("The command execution result was not found.");
}
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("Max retries exceeded for command execution result.");
} catch (Exception e) {
throw new RuntimeException("describeInvocationResults failed", e);
}
}
public static class Constants {
// コマンドタイプ。
public static final class COMMAND_TYPE {
// シェルコマンド。Linux インスタンスに適用されます。
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";
}
// クラウドアシスタントコマンドの呼び出しステータス値。
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";
}
}
}Python
この Python の例では、クライアントの初期化、コマンドの実行、呼び出しのクエリ、完了のポーリングをそれぞれ別の関数で行うモジュール式のアプローチを提供します。
主な動作:
実行前に、コマンドタイプが 3 つの有効な値 (
RunShellScript、RunBatScript、RunPowerShellScript) のいずれかであることを検証します。インスタンス ID のリストが空でないことを検証します。
結果をポーリングする際に指数バックオフを使用します。待機時間はリトライごとに倍増します (
2 ^ retry_count秒)。最大リトライ回数はデフォルトで
max(command_timeout // 2, 1)に設定されます。これは、60 秒のタイムアウトの場合、30 回のリトライに相当します。最終状態を処理します:
Finished(成功)、Failed、およびStopped。
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(
"Missing required environment variables: ALIBABA_CLOUD_ACCESS_KEY_ID and 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("Instance IDs list cannot be empty.")
valid_command_types = ["RunShellScript", "RunBatScript", "RunPowerShellScript"]
if command_type not in valid_command_types:
raise ValueError(f"Invalid command type: {command_type}. Valid types are {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"Failed to execute command: {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"Failed to query invocations: {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("The command execution result was not found.")
invocation_result = invocation_list[0]
invocation_status = invocation_result.get('InvocationStatus')
logging.info(f"Current invocation status: {invocation_status}")
if invocation_status == "Finished":
print("query_invocations result:", results)
break
elif invocation_status in ["Failed", "Stopped"]:
raise RuntimeError(f"Command execution failed with status: {invocation_status}")
else:
retry_count += 1
else:
raise TimeoutError("Command execution timed out.")
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()