ECS SDK を使用して、複数の ECS インスタンスでクラウドアシスタントコマンドを実行し、ログインせずに実行結果を取得できます。
利用シーン
ビジネスの安定性を確保するには、O&M(運用・保守)システムが不可欠です。ECS インスタンスの健全性を維持するために、CPU 使用率、メモリ消費量、ディスク使用率などのリソース使用量を定期的に確認してください。
クラウドアシスタントは、ログインせずに ECS インスタンス上でコマンドを実行できるため、リソースモニタリング、ログ収集、トラブルシューティングなどの自動化された O&M タスクを実現します。
前提条件
以下の要件を満たしていることを確認してください。
-
対象の ECS インスタンスが Running 状態であり、クラウドアシスタントエージェントがインストール済みである必要があります。詳細については、「クラウドアシスタントエージェントのインストール」をご参照ください。
-
ALIBABA_CLOUD_ACCESS_KEY_IDおよびALIBABA_CLOUD_ACCESS_KEY_SECRETの環境変数が設定されている必要があります。詳細については、「Linux、macOS、および Windows での環境変数の設定」をご参照ください。説明Alibaba Cloud アカウントを保護するには、RAM ユーザーを作成し、そのユーザーに ECS 権限を付与して、その AccessKey ペアを使用して ECS SDK の操作を呼び出します。「RAM ユーザー」をご参照ください。
-
RAM ユーザーにクラウドアシスタントの権限が付与されている必要があります。詳細については、「RAM ユーザーによるクラウドアシスタントへのアクセス」をご参照ください。
-
クラウドアシスタントコマンド(シェル、バッチ、または PowerShell)が準備されている必要があります。
-
ECS SDK の依存関係がインストールされている必要があります。SDK Center の Elastic Compute Service ページをご確認ください。
使用する API オペレーション
以下の ECS API オペレーションを使用します。
|
オペレーション |
説明 |
|
1 つまたは複数の ECS インスタンスでコマンドを実行し、コマンドタスクの |
|
|
|
コマンドタイプ
クラウドアシスタントは、RunCommand の Type パラメーターで指定される 3 種類のコマンドタイプをサポートしています。
|
Type 値 |
説明 |
|
|
Linux インスタンス用のシェルコマンド |
|
|
Windows インスタンス用のバッチコマンド |
|
|
Windows インスタンス用の PowerShell コマンド |
呼び出しステータス値
結果をポーリングする際、InvocationStatus フィールドでコマンドの状態を確認できます。
|
ステータス |
説明 |
|
|
コマンドが検証中または送信中です。 |
|
|
コマンドが実行中です。 |
|
|
コマンドが停止処理中です。 |
|
|
コマンドが完了しました。 |
|
|
コマンドが失敗しました。 |
|
|
コマンドが停止されました。 |
サンプルコード
以下の Java および Python の例では、ECS SDK を使用して ECS インスタンスでコマンドを実行し、実行結果をポーリングする方法を示します。
Java
この例では、ECS クライアントを初期化し、コマンドを実行して、コマンドが完了またはタイムアウトするまで結果をポーリングする CloudAssistantService クラスを作成します。
主な動作:
-
ダブルチェックロックを使用して、スレッドセーフなシングルトン ECS クライアントを作成します。
-
指定されたインスタンスで
cat /proc/meminfoを実行します。 -
2 秒間隔でポーリングを行い、最大
commandTimeOut / delay回リトライします。
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 {
/**
* Read the AccessKey ID and AccessKey secret from environment variables.
*/
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() {
}
/**
* Initialize the ECS client.
*
* @param regionId The region ID that specifies where the ECS client connects.
* @return The initialized ECS client.
* <p>
* This method uses the double-checked locking pattern to ensure thread-safe
* singleton creation of the ECS client. It first checks whether a client
* already exists, then re-checks within a synchronized block before creating one.
*/
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 {
// The region ID.
String regionId = "cn-chengdu";
getEcsClient(regionId);
// The IDs of the ECS instances on which to run the command.
List<String> instanceIds = Arrays.asList("i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot");
// The command to run.
String commandContent = "#!/bin/bash\n cat /proc/meminfo";
// The command execution timeout, in seconds.
long commandTimeOut = 60;
// Run the command.
String invokeId = runCommand(commandContent, regionId, instanceIds, commandTimeOut);
// Query the command execution results.
DescribeInvocationsResponse invocationResult = describeInvocations(regionId, invokeId, commandTimeOut);
System.out.println("The command execution result:" + new Gson().toJson(invocationResult));
// Note: This sample does not include logging configuration.
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
SCHEDULER.shutdown();
}
}
/**
* Run a command on the specified ECS instances.
*
* @param commandContent The command to run.
* @param regionId The region ID of the target ECS instances.
* @param instanceIds The IDs of the target ECS instances.
* @param commandTimeOut The command execution timeout, in seconds.
* @return The invocation ID (InvokeId) of the command task.
*/
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);
}
}
/**
* Query the execution results of a Cloud Assistant command.
*
* @param regionId The region ID of the target instances.
* @param invokeId The invocation ID that uniquely identifies the command task.
* @param commandTimeOut The command execution timeout, in seconds.
*/
public static DescribeInvocationsResponse describeInvocations(String regionId, String invokeId, long commandTimeOut) {
DescribeInvocationsRequest describeInvocationsRequest = new DescribeInvocationsRequest()
.setRegionId(regionId)
.setInvokeId(invokeId);
long delay = 2;
// Maximum number of polling retries.
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 {
// Command types.
public static final class COMMAND_TYPE {
// Shell command, applicable to Linux instances.
public static final String RUN_SHELL_SCRIPT = "RunShellScript";
// Batch command, applicable to Windows instances.
public static final String RUN_BAT_SCRIPT = "RunBatScript";
// PowerShell command, applicable to Windows instances.
public static final String RUN_POWERSHELL_SCRIPT = "RunPowerShellScript";
}
// Invocation status values for Cloud Assistant commands.
public static final class INVOCATION_STATUS {
// The system is verifying or sending the command.
public static final String PENDING = "Pending";
// The command is being run on the ECS instances.
public static final String RUNNING = "Running";
// The command is being stopped.
public static final String STOPPING = "Stopping";
}
}
}
Python
この例では、クライアントの初期化、コマンドの実行、呼び出しのクエリ、完了のポーリングをそれぞれ別の関数で実装しています。
主な動作:
-
実行前にコマンドタイプを
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
# Configure logging.
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():
# The region ID.
region_id = "cn-chengdu"
# The IDs of the ECS instances on which to run the command.
instance_ids = ["i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot"]
# The command to run.
command_content = "#!/bin/bash\n cat /proc/meminfo"
# The command execution timeout, in seconds.
command_timeout = 60
# The command type. Valid values: RunShellScript, RunBatScript, and 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()