Gunakan ECS SDK untuk menjalankan perintah Cloud Assistant pada beberapa instance ECS dan mengambil hasil eksekusinya tanpa perlu login.
Skenario contoh
Sistem O&M sangat penting untuk stabilitas bisnis. Untuk menjaga kesehatan instance ECS, lakukan pemeriksaan berkala terhadap penggunaan sumber daya seperti utilisasi CPU, konsumsi memori, dan penggunaan disk.
Cloud Assistant menjalankan perintah pada instance ECS tanpa login, sehingga memungkinkan otomatisasi tugas O&M seperti pemantauan sumber daya, pengumpulan log, dan troubleshooting.
Prasyarat
Pastikan persyaratan berikut terpenuhi:
-
Instance ECS target berada dalam status Running dan telah menginstal Cloud Assistant Agent. Lihat Instal Cloud Assistant Agent.
-
Variabel lingkungan
ALIBABA_CLOUD_ACCESS_KEY_IDdanALIBABA_CLOUD_ACCESS_KEY_SECRETtelah dikonfigurasi. Lihat Konfigurasi variabel lingkungan di Linux, macOS, dan Windows.CatatanUntuk melindungi Akun Alibaba Cloud Anda, buat Pengguna RAM, berikan izin ECS kepadanya, dan gunakan pasangan AccessKey-nya untuk memanggil operasi ECS SDK. Lihat Pengguna RAM.
-
Pengguna RAM memiliki izin Cloud Assistant. Lihat Akses Pengguna RAM ke Cloud Assistant.
-
Perintah Cloud Assistant (shell, batch, atau PowerShell) telah disiapkan.
-
Dependensi ECS SDK telah diinstal. Kunjungi halaman Elastic Compute Service di SDK Center.
Operasi API yang digunakan
Operasi API ECS berikut digunakan:
|
Operasi |
Deskripsi |
|
Menjalankan perintah pada satu atau beberapa instance ECS dan mengembalikan |
|
|
Menanyakan hasil eksekusi perintah berdasarkan |
Jenis perintah
Cloud Assistant mendukung tiga jenis perintah, yang ditentukan oleh parameter Type dalam RunCommand:
|
Type value |
Deskripsi |
|
|
Perintah shell untuk instance Linux |
|
|
Perintah batch untuk instance Windows |
|
|
Perintah PowerShell untuk instance Windows |
Nilai status invokasi
Saat melakukan polling hasil, bidang InvocationStatus menunjukkan status perintah:
|
Status |
Deskripsi |
|
|
Perintah sedang diverifikasi atau dikirim. |
|
|
Perintah sedang berjalan. |
|
|
Perintah sedang dihentikan. |
|
|
Perintah selesai. |
|
|
Perintah gagal. |
|
|
Perintah dihentikan. |
Kode contoh
Contoh Java dan Python berikut menunjukkan cara menjalankan perintah pada instance ECS dan melakukan polling terhadap hasil eksekusi menggunakan ECS SDK.
Java
Contoh ini membuat kelas CloudAssistantService yang menginisialisasi klien ECS, menjalankan perintah, dan melakukan polling terhadap hasil hingga perintah selesai atau timeout.
Perilaku utama:
-
Membuat klien ECS singleton yang aman untuk thread dengan double-checked locking.
-
Menjalankan
cat /proc/meminfopada instance yang ditentukan. -
Melakukan polling setiap 2 detik, hingga maksimal
commandTimeOut / delaykali percobaan.
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 {
/**
* Baca ID AccessKey dan rahasia AccessKey dari variabel lingkungan.
*/
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() {
}
/**
* Inisialisasi klien ECS.
*
* @param regionId ID wilayah tempat klien ECS terhubung.
* @return Klien ECS yang telah diinisialisasi.
* <p>
* Metode ini menggunakan pola double-checked locking untuk memastikan pembuatan
* singleton klien ECS yang aman untuk thread. Pertama-tama metode ini memeriksa apakah
* klien sudah ada, lalu memeriksa kembali di dalam blok synchronized sebelum membuatnya.
*/
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 wilayah.
String regionId = "cn-chengdu";
getEcsClient(regionId);
// ID instance ECS tempat perintah akan dijalankan.
List<String> instanceIds = Arrays.asList("i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot");
// Perintah yang akan dijalankan.
String commandContent = "#!/bin/bash\n cat /proc/meminfo";
// Timeout eksekusi perintah, dalam detik.
long commandTimeOut = 60;
// Jalankan perintah.
String invokeId = runCommand(commandContent, regionId, instanceIds, commandTimeOut);
// Tanyakan hasil eksekusi perintah.
DescribeInvocationsResponse invocationResult = describeInvocations(regionId, invokeId, commandTimeOut);
System.out.println("Hasil eksekusi perintah:" + new Gson().toJson(invocationResult));
// Catatan: Contoh ini tidak menyertakan konfigurasi logging.
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
SCHEDULER.shutdown();
}
}
/**
* Jalankan perintah pada instance ECS yang ditentukan.
*
* @param commandContent Perintah yang akan dijalankan.
* @param regionId ID wilayah instance ECS target.
* @param instanceIds ID instance ECS target.
* @param commandTimeOut Timeout eksekusi perintah, dalam detik.
* @return ID invokasi (InvokeId) dari tugas perintah.
*/
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);
}
}
/**
* Tanyakan hasil eksekusi perintah Cloud Assistant.
*
* @param regionId ID wilayah instance target.
* @param invokeId ID invokasi yang secara unik mengidentifikasi tugas perintah.
* @param commandTimeOut Timeout eksekusi perintah, dalam detik.
*/
public static DescribeInvocationsResponse describeInvocations(String regionId, String invokeId, long commandTimeOut) {
DescribeInvocationsRequest describeInvocationsRequest = new DescribeInvocationsRequest()
.setRegionId(regionId)
.setInvokeId(invokeId);
long delay = 2;
// Jumlah maksimum percobaan polling.
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("Hasil eksekusi perintah tidak ditemukan.");
}
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("Jumlah maksimum percobaan untuk hasil eksekusi perintah telah terlampaui.");
} catch (Exception e) {
throw new RuntimeException("describeInvocationResults failed", e);
}
}
public static class Constants {
// Jenis perintah.
public static final class COMMAND_TYPE {
// Perintah shell, berlaku untuk instance Linux.
public static final String RUN_SHELL_SCRIPT = "RunShellScript";
// Perintah batch, berlaku untuk instance Windows.
public static final String RUN_BAT_SCRIPT = "RunBatScript";
// Perintah PowerShell, berlaku untuk instance Windows.
public static final String RUN_POWERSHELL_SCRIPT = "RunPowerShellScript";
}
// Nilai status invokasi untuk perintah Cloud Assistant.
public static final class INVOCATION_STATUS {
// Sistem sedang memverifikasi atau mengirim perintah.
public static final String PENDING = "Pending";
// Perintah sedang dijalankan pada instance ECS.
public static final String RUNNING = "Running";
// Perintah sedang dihentikan.
public static final String STOPPING = "Stopping";
}
}
}
Python
Contoh ini menggunakan fungsi terpisah untuk inisialisasi klien, eksekusi perintah, penanyaan invokasi, dan polling penyelesaian.
Perilaku utama:
-
Memvalidasi jenis perintah terhadap
RunShellScript,RunBatScript, danRunPowerShellScriptsebelum eksekusi. -
Memastikan daftar ID instance tidak kosong.
-
Menggunakan exponential backoff saat polling: waktu tunggu berlipat ganda setiap percobaan (
2 ^ retry_countdetik). -
Secara default menggunakan
max(command_timeout // 2, 1)kali percobaan (30 kali untuk timeout 60 detik). -
Menangani status terminal:
Finished(sukses),Failed, danStopped.
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
# Konfigurasi 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(
"Variabel lingkungan wajib tidak ditemukan: ALIBABA_CLOUD_ACCESS_KEY_ID dan 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("Daftar ID instance tidak boleh kosong.")
valid_command_types = ["RunShellScript", "RunBatScript", "RunPowerShellScript"]
if command_type not in valid_command_types:
raise ValueError(f"Jenis perintah tidak valid: {command_type}. Jenis yang valid adalah {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"Gagal mengeksekusi perintah: {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"Gagal menanyakan invokasi: {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("Hasil eksekusi perintah tidak ditemukan.")
invocation_result = invocation_list[0]
invocation_status = invocation_result.get('InvocationStatus')
logging.info(f"Status invokasi saat ini: {invocation_status}")
if invocation_status == "Finished":
print("hasil query_invocations:", results)
break
elif invocation_status in ["Failed", "Stopped"]:
raise RuntimeError(f"Eksekusi perintah gagal dengan status: {invocation_status}")
else:
retry_count += 1
else:
raise TimeoutError("Waktu eksekusi perintah habis.")
def main():
# ID wilayah.
region_id = "cn-chengdu"
# ID instance ECS tempat perintah akan dijalankan.
instance_ids = ["i-2vcXXXXXXXXXXXXXXXb8", "i-2vcXXXXXXXXXXXXXXXot"]
# Perintah yang akan dijalankan.
command_content = "#!/bin/bash\n cat /proc/meminfo"
# Timeout eksekusi perintah, dalam detik.
command_timeout = 60
# Jenis perintah. Nilai yang valid: RunShellScript, RunBatScript, dan 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()