如果您在执行云助手命令后需要重启实例,建议避免直接在云助手命令中添加rebootshutdown等操作,否则云助手服务不能上报命令执行结果,会导致命令状态异常。本文介绍如何使用OpenAPI和运维编排服务OOS批量执行命令并重启实例,您可以根据需要选择合适的方式。

使用OpenAPI批量执行命令并重启实例

阿里云提供了丰富的OpenAPI供您管理云上资源,本步骤以在本地Linux环境中运行Python代码调用OpenAPI为例,演示如何批量执行命令并重启实例。

  1. 准备执行命令所需的信息。
    1. 获取AccessKey。
      建议您获取RAM用户的AccessKey,具体操作,请参见获取AccessKey
    2. 获取地域ID。
      您可以调用DescribeRegions获取地域列表,详细的参数说明,请参见DescribeRegions
    3. 获取待执行命令的实例ID。
      您可以调用DescribeInstances筛选符合指定条件的实例,例如状态为运行中的实例、绑定了指定标签的实例。详细的参数说明,请参见DescribeInstances
  2. 在本地配置环境并运行示例代码。
    1. 安装阿里云Python SDK。
      sudo pip install aliyun-python-sdk-ecs
    2. 升级Python SDK至最新版本。
      sudo pip install --upgrade aliyun-python-sdk-ecs
    3. 创建.py文件,并写入示例代码。
      请用您获取的信息替换示例代码中的以下信息:
      • AccessKey ID:access_key = '<yourAccessKey ID>'
      • AccessKey Secret:access_key_secret = '<yourAccessKey Secret>'
      • Region ID:region_id = '<yourRegionId>'
      • 实例ID:ins_ids= ["i-bp185fcs****","i-bp14wwh****","i-bp13jbr****"]
      示例代码:
      # coding=utf-8
      # If the Python sdk is not installed, run 'sudo pip install aliyun-python-sdk-ecs'.
      # Make sure you're using the latest sdk version.
      # Run 'sudo pip install --upgrade aliyun-python-sdk-ecs' to upgrade.
      
      import json
      import sys
      import base64
      import time
      import logging
      from aliyunsdkcore.client import AcsClient
      from aliyunsdkcore.acs_exception.exceptions import ClientException
      from aliyunsdkcore.acs_exception.exceptions import ServerException
      from aliyunsdkecs.request.v20140526.RunCommandRequest import RunCommandRequest
      from aliyunsdkecs.request.v20140526.DescribeInvocationResultsRequest import DescribeInvocationResultsRequest
      from aliyunsdkecs.request.v20140526.RebootInstancesRequest import RebootInstancesRequest
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      
      # Configure the log output formatter
      logging.basicConfig(level=logging.INFO,
                          format="%(asctime)s %(name)s [%(levelname)s]: %(message)s",
                          datefmt='%m-%d %H:%M')
      
      logger = logging.getLogger()
      
      access_key = '<yourAccessKey ID>'  # 请填入您获取的AccessKey ID
      access_key_secret = '<yourAccessKey Secret>'  # 请填入您获取的AccessKey Secret
      region_id = '<yourRegionId>'  # 请填入您获取的Region ID
      
      client = AcsClient(access_key, access_key_secret, region_id)
      
      def base64_decode(content, code='utf-8'):
          if sys.version_info.major == 2:
              return base64.b64decode(content)
          else:
              return base64.b64decode(content).decode(code)
      
      def get_invoke_result(invoke_id):
          request = DescribeInvocationResultsRequest()
          request.set_accept_format('json')
      
          request.set_InvokeId(invoke_id)
          response = client.do_action_with_exception(request)
          response_details = json.loads(response)["Invocation"]["InvocationResults"]["InvocationResult"]
          dict_res = { detail.get("InstanceId",""):{"status": detail.get("InvocationStatus",""),"output":base64_decode(detail.get("Output",""))}  for detail in response_details }
          return dict_res
      
      def get_instances_status(instance_ids):
          request = DescribeInstancesRequest()
          request.set_accept_format('json')
          request.set_InstanceIds(instance_ids)
          response = client.do_action_with_exception(request)
          response_details = json.loads(response)["Instances"]["Instance"]
          dict_res = { detail.get("InstanceId",""):{"status":detail.get("Status","")} for detail in response_details }
          return dict_res
      
      def run_command(cmdtype,cmdcontent,instance_ids,timeout=60):
          """
          cmdtype: 命令类型: RunBatScript;RunPowerShellScript;RunShellScript
          cmdcontent: 命令内容
          instance_ids 实例ID列表
          """
          try:
              request = RunCommandRequest()
              request.set_accept_format('json')
      
              request.set_Type(cmdtype)
              request.set_CommandContent(cmdcontent)
              request.set_InstanceIds(instance_ids)
              # 执行命令的超时时间,单位s,默认是60s,请根据执行的实际命令来设置合适的超时时间
              request.set_Timeout(timeout)
              response = client.do_action_with_exception(request)
              invoke_id = json.loads(response).get("InvokeId")
              return invoke_id
          except Exception as e:
              logger.error("run command failed")
      
      def reboot_instances(instance_ids,Force=False):
          """
          instance_ids: 需要重启的实例列表
          Force: 是否强制重启,默认否
          """
          request = RebootInstancesRequest()
          request.set_accept_format('json')
          request.set_InstanceIds(instance_ids)
          request.set_ForceReboot(Force)
          response = client.do_action_with_exception(request)
      
      def wait_invoke_finished_get_out(invoke_id,wait_count,wait_interval):
          for i in range(wait_count):
              result = get_invoke_result(invoke_id)
              if set([res["status"] for _,res in result.items()]) & set(["Running","Pending","Stopping"]):
                  time.sleep(wait_interval)
              else:
                  return result
          return result
      
      def wait_instance_reboot_ready(ins_ids,wait_count,wait_interval):
          for i in range(wait_count):
              result = get_instances_status(ins_ids)
              if set([res["status"] for _,res in result.items()]) != set(["Running"]):
                  time.sleep(wait_interval)
              else:
                  return result
          return result
      
      def run_task():
          # 设置云助手命令的命令类型
          cmdtype = "RunShellScript"
          # 设置云助手命令的命令内容
          cmdcontent = """
          #!/bin/bash
          echo helloworld
          """
          # 设置超时时间
          timeout = 60
          # 请填入需要执行命令并重启的实例的ID
          ins_ids= ["i-bp185fcs****","i-bp14wwh****","i-bp13jbr****"]
      
          # 执行命令
          invoke_id = run_command(cmdtype,cmdcontent,ins_ids,timeout)
          logger.info("run command,invoke-id:%s" % invoke_id)
      
          # 等待命令执行完成,循环查询10次,每次间隔5秒,查询次数和间隔请根据实际情况配置
          invoke_result = wait_invoke_finished_get_out(invoke_id,10,5)
          for ins_id,res in invoke_result.items():
              logger.info("instance %s command execute finished,status: %s,output:%s" %(ins_id,res["status"],res["output"]))
      
          # 重启实例
          logger.warn("reboot instance Now")
          reboot_instances(ins_ids)
      
          time.sleep(5)
          # 等待实例重启至Running状态,循环查询30次,每次间隔10秒
          reboot_result = wait_instance_reboot_ready(ins_ids,30,10)
          logger.warn("reboot instance Finished")
          for ins_id,res in reboot_result.items():
              logger.info("instance %s status: %s" %(ins_id,res["status"]))
      
      if __name__ == '__main__':
          run_task()
    4. 运行.py文件。
      运行效果如下图所示,为3台实例执行命令输出helloworld,然后自动重启实例。openapi-exec-reboot

使用OOS批量执行命令并重启实例

运维编排服务OOS是阿里云提供的云上自动化运维服务,您可以通过模板定义运维动作,然后执行模板自动化运行运维任务。

  1. 进入模板配置页面。
    1. 登录OOS控制台
    2. 在左侧导航栏,单击我的模板
    3. 单击创建模板
  2. 完成模板配置。
    1. 输入模板名称runcommand_reboot_instances
    2. 单击YAML页签,并输入以下代码。
      FormatVersion: OOS-2019-06-01
      Description:
        en: Bulky run command on ECS instances and reboot instance.
        zh-cn: 批量在多台ECS实例上运行云助手命令并重启实例。
        name-en: ACS-ECS-BulkyRunCommandRboot
        name-zh-cn: 批量在ECS实例上运行命令并重启实例
        categories:
          - run_command
      Parameters:
        regionId:
          Type: String
          Description:
            en: The id of region
            zh-cn: 地域ID
          Label:
            en: Region
            zh-cn: 地域
          AssociationProperty: RegionId
          Default: '{{ ACS::RegionId }}'
        targets:
          Type: Json
          Label:
            en: TargetInstance
            zh-cn: 目标实例
          AssociationProperty: Targets
          AssociationPropertyMetadata:
            ResourceType: ALIYUN::ECS::Instance
            RegionId: regionId
        commandType:
          Description:
            en: The type of command
            zh-cn: 云助手命令类型
          Label:
            en: CommandType
            zh-cn: 云助手命令类型
          Type: String
          AllowedValues:
            - RunBatScript
            - RunPowerShellScript
            - RunShellScript
          Default: RunShellScript
        commandContent:
          Description:
            en: Command content to run in ECS instance
            zh-cn: 在ECS实例中执行的云助手命令
          Label:
            en: CommandContent
            zh-cn: 云助手命令
          Type: String
          MaxLength: 16384
          AssociationProperty: Code
          Default: echo hello
        workingDir:
          Description:
            en: 'The directory where the created command runs on the ECS instances.Linux instances: under the home directory of the administrator (root user): /root.Windows instances: under the directory where the process of the Cloud Assistant client is located, such asC:\Windows\System32.'
            zh-cn: 脚本在ECS实例中的运行目录。Linux系统实例默认在管理员(root用户)的home目录下,即/root。Windows系统实例默认在云助手客户端进程所在目录,例如C:\Windows\System32。
          Label:
            en: WorkingDir
            zh-cn: 运行目录
          Type: String
          Default: ''
        timeout:
          Description:
            en: The value of the invocation timeout period of a command on ECS instances
            zh-cn: ECS实例中执行命令的超时时间
          Label:
            en: Timeout
            zh-cn: 超时时间
          Type: Number
          Default: 600
        enableParameter:
          Description:
            en: Whether to include secret parameters or custom parameters in the command
            zh-cn: 命令中是否包含加密参数或自定义参数
          Label:
            en: EnableParameter
            zh-cn: 命令中是否包含加密参数或自定义参数
          Type: Boolean
          Default: false
        username:
          Description:
            en: The username that is used to run the command on the ECS instance
            zh-cn: 在ECS实例中执行命令的用户名称
          Label:
            en: Username
            zh-cn: 执行命令的用户名称
          Type: String
          Default: ''
        windowsPasswordName:
          Description:
            en: The name of the password used to run the command on a Windows instance
            zh-cn: 在Windows实例中执行命令的用户的密码名称
          Label:
            en: WindowsPasswordName
            zh-cn: 在Windows实例中执行命令的用户的密码名称
          Type: String
          Default: ''
          AssociationProperty: SecretParameterName
        rateControl:
          Description:
            en: Concurrency ratio of task execution
            zh-cn: 任务执行的并发比率
          Label:
            en: RateControl
            zh-cn: 任务执行的并发比率
          Type: Json
          AssociationProperty: RateControl
          Default:
            Mode: Concurrency
            MaxErrors: 0
            Concurrency: 10
        OOSAssumeRole:
          Description:
            en: The RAM role to be assumed by OOS
            zh-cn: OOS扮演的RAM角色
          Label:
            en: OOSAssumeRole
            zh-cn: OOS扮演的RAM角色
          Type: String
          Default: OOSServiceRole
      RamRole: '{{ OOSAssumeRole }}'
      Tasks:
        - Name: getInstance
          Description:
            en: Views the ECS instances.
            zh-cn: 获取ECS实例。
          Action: ACS::SelectTargets
          Properties:
            ResourceType: ALIYUN::ECS::Instance
            RegionId: '{{ regionId }}'
            Filters:
              - '{{ targets }}'
          Outputs:
            instanceIds:
              Type: List
              ValueSelector: Instances.Instance[].InstanceId
        - Name: runCommand
          Action: ACS::ECS::RunCommand
          Description:
            en: Execute cloud assistant command.
            zh-cn: 执行云助手命令。
          Properties:
            regionId: '{{ regionId }}'
            commandContent: '{{ commandContent }}'
            instanceId: '{{ ACS::TaskLoopItem }}'
            commandType: '{{ commandType }}'
            workingDir: '{{ workingDir }}'
            timeout: '{{ timeout }}'
            enableParameter: '{{ enableParameter }}'
            username: '{{ username }}'
            windowsPasswordName: '{{ windowsPasswordName }}'
          Loop:
            RateControl: '{{ rateControl }}'
            Items: '{{ getInstance.instanceIds }}'
            Outputs:
              commandOutputs:
                AggregateType: Fn::ListJoin
                AggregateField: commandOutput
          Outputs:
            commandOutput:
              Type: String
              ValueSelector: invocationOutput
        - Name: rebootInstance
          Action: ACS::ECS::RebootInstance
          Description:
            en: Restarts the ECS instances.
            zh-cn: 重启实例。
          Properties:
            regionId: '{{ regionId }}'
            instanceId: '{{ ACS::TaskLoopItem }}'
          Loop:
            RateControl: '{{ rateControl }}'
            Items: '{{ getInstance.instanceIds }}'
      Outputs:
        instanceIds:
          Type: List
          Value: '{{ getInstance.instanceIds }}'
    3. 单击创建模板
  3. 执行模板。
    1. 找到刚创建的模板,在操作列单击创建执行
    2. 完成执行配置。
      按提示逐步完成配置,在设置参数页面选择多台实例,其他设置保持默认即可。exec-temp
    3. 确定页面,单击创建
      创建执行后自动开始执行模板,并跳转至执行的基本详情页面,等待执行状态变为成功后即执行完成。
  4. 查看执行结果。
    您可以在高级视图页签查看执行过程和输出,如下图所示,可以看出预定动作均已成功完成。exec-result