xuekailun 5 月之前
父節點
當前提交
07875aa3bb
共有 5 個文件被更改,包括 903 次插入208 次删除
  1. 0 43
      config.py
  2. 130 0
      ess_longvideo_k.py
  3. 180 0
      longvideo_config.py
  4. 0 128
      main.py
  5. 593 37
      utils.py

+ 0 - 43
config.py

@@ -1,43 +0,0 @@
-import os
-import logging
-
-# 设置阿里云访问密钥的ID
-ALIBABA_CLOUD_ACCESS_KEY_ID = 'LTAI4GBWbFvvXoXsSVBe1o9f'
-
-# 设置阿里云访问密钥的密钥
-ALIBABA_CLOUD_ACCESS_KEY_SECRET = 'kRAikWitb4kDxaAyBqNrmLmllMEDO3'
-
-# ALB配置信息
-alb_config = {
-    'server_group_id': 'sgp-ec4gopoclruofsfmxu',  # 服务器组ID
-    'server_type': 'ecs',  # 服务器类型
-    'weight': 100  # 权重
-}
-
-# ECS实例配置信息
-instance_config = {
-    'size': '200',  # 实例大小
-    'category': 'cloud_efficiency',  # 实例类型
-    'region_id': 'cn-hangzhou',  # 地域ID
-    'image_id': 'm-bp12bkuvg20k6ueqmb4v',  # 镜像ID
-    'instance_type': 'ecs.c6.2xlarge',  # 实例类型
-    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',  # 安全组ID
-    'v_switch_id': 'vsw-bp14e4xu6uzte9nyn6nvr',  # 虚拟交换机ID
-    'instance_name': 'ESS-vlogapi-alb-[1,2]',  # 实例名称
-    'unique_suffix': True,  # 是否使用唯一后缀
-    'password_inherit': True,  # 是否继承密码
-    'zone_id': 'cn-hangzhou-k',  # 可用区ID
-    'key_pair_name': 'stuuudy'  # 密钥对名称
-}
-
-# 读取启动脚本文件内容
-start_sh_dir = os.path.dirname(os.path.realpath(__file__))  # 获取当前文件所在目录
-start_sh_filename = 'start.sh'  # 启动脚本文件名
-with open(file=os.path.join(start_sh_dir, start_sh_filename), mode='r', encoding='utf-8') as rf:  # 打开启动脚本文件
-    file_content = rf.read()  # 读取文件内容
-    logging.info(f"start sh file content: {file_content}")  # 记录文件内容到日志
-start_sh = {
-    'target_dir': '/home/piao_server_sh',  # 目标目录
-    'name': start_sh_filename,  # 文件名
-    'content': file_content,  # 文件内容
-}

+ 130 - 0
ess_longvideo_k.py

@@ -0,0 +1,130 @@
+import sys
+import os
+import asyncio
+import logging
+import time
+import requests
+
+import utils
+import longvideo_config
+
+from concurrent.futures import ThreadPoolExecutor
+
+
+health_instances = []
+
+
+def longvideo_health_check(client, instance_id, max_wait_time=None):
+    """
+    服务健康检查
+    :param client: 客户端连接
+    :param instance_id: instanceId
+    :param max_wait_time: 最长等待时间,单位:s
+    :return:
+    """
+    global health_instances
+    start_time = time.time()
+    ip_address = utils.get_ip_address(client=client, instance_id=instance_id)
+    while True:
+        health_check_url = f"http://{ip_address}:8080/longvideoapi/test"
+        try:
+            http_code = requests.get(health_check_url).status_code
+        except:
+            logging.info(f"images is downloading ip: {ip_address}")
+            http_code = 0
+
+        if http_code == 200:
+            health_instances.append((instance_id, ip_address))
+            logging.info(f"health check success, instance: {instance_id}/{ip_address}")
+            break
+        elif max_wait_time is not None:
+            now = time.time()
+            if (now - start_time) >= max_wait_time:
+                logging.info(f"health check error, instance: {instance_id}/{ip_address}")
+                break
+            else:
+                time.sleep(10)
+        else:
+            time.sleep(10)
+
+
+async def ess_instance(create_client, alb_client, ess_count, max_workers):
+    """
+    扩容机器并运行新服务
+    :param create_client: 购买机器客户端连接
+    :param alb_client: 修改负载均衡权限
+    :param ess_count: 扩容数量
+    :param max_workers: 线程数
+    :return:
+    """
+    # 1. 购买机器并启动
+    ess_instance_ids = utils.create_multiple_instances(
+        amount=ess_count,
+        client=create_client,
+        **longvideo_config.instance_config_k,
+    )
+    time.sleep(60)
+
+    # 2. 发送启动脚本到机器上
+    utils.send_file_to_ecs(client=create_client, instance_id_list=ess_instance_ids, **longvideo_config.start_sh)
+    logging.info(f"send start shell file finished, instances: {ess_instance_ids}")
+    # 3. 启动服务
+    start_sh_param = "latest"
+    server_start_sh = os.path.join(longvideo_config.start_sh['target_dir'], longvideo_config.start_sh['name'])
+    server_start_commend = f"sh {server_start_sh} {start_sh_param}"
+    utils.run_command(client=create_client, instance_ids=ess_instance_ids, command=server_start_commend)
+    # 4. 异步探活
+    global health_instances
+    health_instances = []
+    max_wait_time = 180
+    loop = asyncio.get_running_loop()
+    executor = ThreadPoolExecutor(max_workers=max_workers)
+    tasks = [
+        loop.run_in_executor(executor, longvideo_health_check, *args) for args in
+        [(alb_client, instance_id, max_wait_time) for instance_id in ess_instance_ids]
+    ]
+    await asyncio.wait(tasks)
+    logging.info(f"health instances count: {len(health_instances)}, {health_instances}")
+
+    # 5. 挂载流量到ALB
+    if len(health_instances) > 0:
+        # 所有机器探活成功
+        time.sleep(20)
+        for instance_id, ip in health_instances:
+            for server_group_id in longvideo_config.server_group_id_list:
+                utils.add_servers_to_server_group(alb_client, server_group_id, instance_id, weight=100)
+
+
+        logging.info(f"ess count: {ess_count}, "
+                     f"create count: {len(ess_instance_ids)}, "
+                     f"finished count: {len(health_instances)}")
+    else:
+        logging.info(f"ess count: {ess_count}, "
+                     f"create count: {len(ess_instance_ids)}, "
+                     f"health count: {len(health_instances)}")
+        sys.exit()
+
+def main():
+    try:
+        create_client = utils.connect_client(access_key_id=longvideo_config.create_client_params['access_key_id'],
+                                             access_key_secret=longvideo_config.create_client_params['access_key_secret'],
+                                             region_id=longvideo_config.create_client_params['region_id'])
+        alb_client = utils.connect_alb_client(
+            access_key_id=longvideo_config.slb_client_params['access_key_id'],
+            access_key_secret=longvideo_config.slb_client_params['access_key_secret']
+        )
+        # 获取批量创建ECS实例的数量
+        ess_instance_count = int(sys.argv[1])
+        # 扩容机器并启动服务
+        logging.info(f"ess instances start ...")
+        logging.info(f"ess instance count: {ess_instance_count}")
+        asyncio.run(ess_instance(create_client=create_client,
+                                 alb_client=alb_client,
+                                 ess_count=ess_instance_count, max_workers=2))
+        logging.info(f"ess instances end!")
+    except Exception as e:
+        logging.error(e)
+
+
+if __name__ == '__main__':
+    main()

+ 180 - 0
longvideo_config.py

@@ -0,0 +1,180 @@
+import os
+import logging
+
+logging.basicConfig(level=logging.INFO,
+                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
+                    datefmt='%a, %d %b %Y %H:%M:%S')
+
+slb_id_list = ["lb-bp1qk9mkvjtundlzz7owm", "lb-bp1pj2v06ladvgftgxcp0", "lb-bp1y63rnrb2e64whryghz", "lb-bp17woaq4vz3gnb8ujzvh"]
+server_group_id_list = ["sgp-h79h3lkvua1xs3418y"]
+# 修改负载均衡权限
+slb_client_params = {
+    'access_key_id': 'LTAIuPbTPL3LDDKN',
+    'access_key_secret': 'ORcNedKwWuwVtcq4IRFtUDZgS0b1le',
+    'region_id': 'cn-hangzhou'
+}
+# 购买机器权限
+create_client_params = {
+    'access_key_id': 'LTAI4GBWbFvvXoXsSVBe1o9f',
+    'access_key_secret': 'kRAikWitb4kDxaAyBqNrmLmllMEDO3',
+    'region_id': 'cn-hangzhou'
+}
+
+# 机器配置
+instance_config = {
+    # 使用的镜像信息
+    'image_id': 'm-bp12bkuvg20k6ueqmb4v',
+    # 设置实例规格
+    'instance_type': 'ecs.c6.2xlarge',
+    # 选择的交换机
+    'vswitch_id': 'vsw-bp19lpjwtc6j0p0m9mdc2',
+    # 当前VPC类型的安全组
+    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',
+    # 硬盘的大小,单位:G
+    'disk_size': '200',
+    # 服务器命名
+    'instance_name': 'ESS-vlogapi-[1,2]',
+    # 服务器所在区域
+    'zone_id': 'cn-hangzhou-h',
+    # 磁盘类型:云盘
+    'disk_category': 'cloud_efficiency',
+    # 密钥
+    'key_pair_name': 'stuuudy',
+    # tag
+    'tags': [{"Key": "ecs", "Value": "longvideoapi.prod"}]
+}
+
+# 机器配置_hangzhou_i
+instance_config_i = {
+    # 使用的镜像信息
+    'image_id': 'm-bp12bkuvg20k6ueqmb4v',
+    # 设置实例规格
+    'instance_type': 'ecs.c6.2xlarge',
+    # 选择的交换机
+    'vswitch_id': 'vsw-bp17c002ovyomzwnhhdhj',
+    # 当前VPC类型的安全组
+    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',
+    # 硬盘的大小,单位:G
+    'disk_size': '200',
+    # 服务器命名
+    'instance_name': 'ESS-vlogapi-[1,2]',
+    # 服务器所在区域
+    'zone_id': 'cn-hangzhou-i',
+    # 磁盘类型:云盘
+    'disk_category': 'cloud_efficiency',
+    # 密钥
+    'key_pair_name': 'stuuudy',
+    # tag
+    'tags': [{"Key": "ecs", "Value": "longvideoapi.prod"}]
+}
+
+# 机器配置_hangzhou_g
+instance_config_g = {
+    # 使用的镜像信息
+    'image_id': 'm-bp12bkuvg20k6ueqmb4v',
+    # 设置实例规格
+    'instance_type': 'ecs.c6.2xlarge',
+    # 选择的交换机
+    'vswitch_id': 'vsw-bp10m69sb9ydfa64jdrn3',
+    # 当前VPC类型的安全组
+    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',
+    # 硬盘的大小,单位:G
+    'disk_size': '200',
+    # 服务器命名
+    'instance_name': 'ESS-vlogapi-[1,2]',
+    # 服务器所在区域
+    'zone_id': 'cn-hangzhou-g',
+    # 磁盘类型:云盘
+    'disk_category': 'cloud_efficiency',
+    # 密钥
+    'key_pair_name': 'stuuudy',
+    # tag
+    'tags': [{"Key": "ecs", "Value": "longvideoapi.prod"}]
+}
+
+# 机器配置_hangzhou_j
+instance_config_j = {
+    # 使用的镜像信息
+    'image_id': 'm-bp12bkuvg20k6ueqmb4v',
+    # 设置实例规格
+    'instance_type': 'ecs.c6.2xlarge',
+    # 选择的交换机
+    'vswitch_id': 'vsw-bp1ssuwxyrt0p17ceeir0',
+    # 当前VPC类型的安全组
+    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',
+    # 硬盘的大小,单位:G
+    'disk_size': '200',
+    # 服务器命名
+    'instance_name': 'ESS-vlogapi-[1,2]',
+    # 服务器所在区域
+    'zone_id': 'cn-hangzhou-j',
+    # 磁盘类型:云盘
+    'disk_category': 'cloud_efficiency',
+    # 密钥
+    'key_pair_name': 'stuuudy',
+    # tag
+    'tags': [{"Key": "ecs", "Value": "longvideoapi.prod"}]
+}
+
+# 机器配置_hangzhou_k
+instance_config_k = {
+    # 使用的镜像信息
+    'image_id': 'm-bp12bkuvg20k6ueqmb4v',
+    # 设置实例规格
+    'instance_type': 'ecs.c6.2xlarge',
+    # 选择的交换机
+    'vswitch_id': 'vsw-bp14e4xu6uzte9nyn6nvr',
+    # 当前VPC类型的安全组
+    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',
+    # 硬盘的大小,单位:G
+    'disk_size': '200',
+    # 服务器命名
+    'instance_name': 'ESS-vlogapi-[1,2]',
+    # 服务器所在区域
+    'zone_id': 'cn-hangzhou-k',
+    # 磁盘类型:云盘
+    'disk_category': 'cloud_efficiency',
+    # 密钥
+    'key_pair_name': 'stuuudy',
+    # tag
+    'tags': [{"Key": "ecs", "Value": "longvideoapi.prod"}]
+}
+
+# 机器配置_hangzhou_k alb
+instance_config_k_alb = {
+    # 使用的镜像信息
+    'image_id': 'm-bp12bkuvg20k6ueqmb4v',
+    # 设置实例规格
+    'instance_type': 'ecs.c6.2xlarge',
+    # 选择的交换机
+    'vswitch_id': 'vsw-bp14e4xu6uzte9nyn6nvr',
+    # 当前VPC类型的安全组
+    'security_group_id': 'sg-bp1irhrkr4vfj272hk4y',
+    # 硬盘的大小,单位:G
+    'disk_size': '200',
+    # 服务器命名
+    'instance_name': 'ESS-vlogapi-alb-[1,2]',
+    # 服务器所在区域
+    'zone_id': 'cn-hangzhou-k',
+    # 磁盘类型:云盘
+    'disk_category': 'cloud_efficiency',
+    # 密钥
+    'key_pair_name': 'stuuudy',
+    # tag
+    'tags': [{"Key": "ecs", "Value": "longvideoapi.prod"}]
+}
+
+
+
+
+# 服务启动脚本
+start_sh_dir = os.path.dirname(os.path.realpath(__file__))
+start_sh_filename = 'longvideo_start.sh'
+with open(file=os.path.join(start_sh_dir, start_sh_filename), mode='r', encoding='utf-8') as rf:
+    file_content = rf.read()
+    logging.info(f"start sh file content: {file_content}")
+start_sh = {
+    'target_dir': '/home/piaoquan_server_sh',
+    'name': start_sh_filename,
+    'content': file_content,
+}

+ 0 - 128
main.py

@@ -1,128 +0,0 @@
-
-import os
-import sys
-import logging
-import asyncio
-from typing import List
-from concurrent.futures import ThreadPoolExecutor
-from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
-from alibabacloud_alb20200616.client import Client as Alb20200616Client
-from alibabacloud_tea_openapi import models as open_api_models
-from alibabacloud_ecs20140526 import models as ecs_20140526_models
-from alibabacloud_alb20200616 import models as alb_20200616_models
-from alibabacloud_tea_util import models as util_models
-from alibabacloud_tea_util.client import Client as UtilClient
-import config
-import utils
-
-class Sample:
-    def __init__(self):
-        pass
-
-    @staticmethod
-    def create_ecs_client() -> Ecs20140526Client:
-        config = open_api_models.Config(  # 创建Config对象,用于配置访问凭证
-            access_key_id=config.ALIBABA_CLOUD_ACCESS_KEY_ID,  # 从config中获取ALIBABA_CLOUD_ACCESS_KEY_ID参数
-            access_key_secret=config.ALIBABA_CLOUD_ACCESS_KEY_SECRET  # 从config中获取ALIBABA_CLOUD_ACCESS_KEY_SECRET参数
-        )
-        config.endpoint = 'ecs-cn-hangzhou.aliyuncs.com'  # 设置ECS服务的访问地址
-        return Ecs20140526Client(config)  # 返回配置好的ECS客户端对象
-
-    @staticmethod
-    def create_alb_client() -> Alb20200616Client:
-        config = open_api_models.Config(  # 创建Config对象,用于配置访问凭证
-            access_key_id=config.ALIBABA_CLOUD_ACCESS_KEY_ID,  # 从config中获取ALIBABA_CLOUD_ACCESS_KEY_ID参数
-            access_key_secret=config.ALIBABA_CLOUD_ACCESS_KEY_SECRET  # 从config中获取ALIBABA_CLOUD_ACCESS_KEY_SECRET参数
-        )
-        config.endpoint = 'alb.cn-hangzhou.aliyuncs.com'  # 设置ALB服务的访问地址
-        return Alb20200616Client(config)  # 返回配置好的ALB客户端对象
-
-    @staticmethod
-    def create_ecs_instance(ecs_client: Ecs20140526Client, amount: int) -> List[str]:
-        instance_ids = []
-        for i in range(amount):
-            request = ecs_20140526_models.CreateInstanceRequest(  # 创建ECS实例请求
-                size=config.instance_config['size'],  # 实例大小
-                category=config.instance_config['category'],  # 实例类型
-                region_id=config.instance_config['region_id'],  # 地域ID
-                image_id=config.instance_config['image_id'],  # 镜像ID
-                instance_type=config.instance_config['instance_type'],  # 实例类型
-                security_group_id=config.instance_config['security_group_id'],  # 安全组ID
-                v_switch_id=config.instance_config['v_switch_id'],  # 虚拟交换机ID
-                instance_name=f"{config.instance_config['instance_name']}-{i+1}",  # 实例名称
-                unique_suffix=config.instance_config['unique_suffix'],  # 是否使用唯一后缀
-                password_inherit=config.instance_config['password_inherit'],  # 是否继承密码
-                zone_id=config.instance_config['zone_id'],  # 可用区ID
-                key_pair_name=config.instance_config['key_pair_name']  # 密钥对名称
-            )
-            response = ecs_client.create_instance(request)  # 调用ECS客户端的创建实例方法
-            instance_id = response.body.instance_id  # 获取创建的实例ID
-            instance_ids.append(instance_id)  # 将实例ID添加到列表中
-            logging.info(f"Created ECS instance with ID: {instance_id}")  # 记录日志
-        return instance_ids  # 返回实例ID列表
-
-    @staticmethod
-    async def start_services_and_health_check(create_client: Ecs20140526Client, slb_client: Alb20200616Client, ess_instance_ids: List[str]):
-        try:
-            await utils.send_file_to_ecs(client=create_client, instance_id_list=ess_instance_ids, **config.start_sh)  # 发送启动脚本到机器上
-            logging.info(f"send start shell file finished, instances: {ess_instance_ids}")
-
-            start_sh_param = "latest"
-            server_start_sh = os.path.join(config.start_sh['target_dir'], config.start_sh['name'])
-            server_start_commend = f"sh {server_start_sh} {start_sh_param}"
-            await utils.run_command(client=create_client, instance_ids=ess_instance_ids, command=server_start_commend)  # 启动服务
-
-            global health_instances
-            health_instances = []
-            max_wait_time = 180
-            loop = asyncio.get_running_loop()
-            max_workers = 10  # 设置最大线程数
-            executor = ThreadPoolExecutor(max_workers=max_workers)
-            tasks = [
-                loop.run_in_executor(executor, utils.health_check, *args) for args in
-                [(slb_client, instance_id, max_wait_time) for instance_id in ess_instance_ids]
-            ]
-            await asyncio.wait(tasks)  # 异步探活
-            logging.info(f"health instances count: {len(health_instances)}, {health_instances}")
-
-            if len(health_instances) > 0:  # 如果有机器探活成功
-                time.sleep(20)
-
-            Sample.mount_alb_to_instance(slb_client, ess_instance_ids)  # 将机器挂载到ALB服务器组中
-        except Exception as e:
-            logging.error(f"An error occurred: {e}")
-            sys.exit(1)  # 退出程序并返回错误状态码
-
-    @staticmethod
-    def mount_alb_to_instance(client: Alb20200616Client, instance_ids: List[str]) -> None:
-        try:
-            servers = [alb_20200616_models.AddServersToServerGroupRequestServers(  # 创建要添加的服务器列表
-                server_id=instance_id,
-                server_type=config.alb_config['server_type'],
-                weight=config.alb_config['weight']
-            ) for instance_id in instance_ids]
-            add_servers_to_server_group_request = alb_20200616_models.AddServersToServerGroupRequest(  # 创建添加服务器到服务器组的请求
-                server_group_id=config.alb_config['server_group_id'],  # 服务器组ID
-                servers=servers  # 服务器列表
-            )
-            runtime = util_models.RuntimeOptions()  # 创建运行时选项
-            client.add_servers_to_server_group_with_options(add_servers_to_server_group_request, runtime)  # 将机器挂载到ALB服务器组中
-        except Exception as e:
-            logging.error(f"An error occurred: {e}")
-            sys.exit(1)  # 退出程序并返回错误状态码
-
-    @staticmethod
-    def main(args: List[str]) -> None:
-        try:
-            ecs_client = Sample.create_ecs_client()  # 创建ECS客户端
-            alb_client = Sample.create_alb_client()  # 创建ALB客户端
-            amount = int(args[0]) if len(args) > 0 else 1  # 从命令行参数获取amount值,如果没有传入则默认为1
-            instance_ids = Sample.create_ecs_instance(ecs_client, amount)  # 创建ECS实例并获取实例ID列表
-
-            asyncio.run(Sample.start_services_and_health_check(ecs_client, alb_client, instance_ids))  # 使用asyncio运行启动服务和异步探活
-        except Exception as e:
-            logging.error(f"An error occurred: {e}")
-            sys.exit(1)  # 退出程序并返回错误状态码
-
-if __name__ == '__main__':
-    Sample.main(sys.argv[1:])  # 调用main方法,传递命令行参数

+ 593 - 37
utils.py

@@ -1,39 +1,595 @@
-
 import logging
-from typing import List
-from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
-from alibabacloud_ecs20140526 import models as ecs_20140526_models
+import json
+import sys
+import time
+import requests
+import asyncio
+
+from aliyunsdkcore.client import AcsClient
+from aliyunsdkslb.request.v20140515.AddBackendServersRequest import AddBackendServersRequest
+from aliyunsdkslb.request.v20140515.RemoveBackendServersRequest import RemoveBackendServersRequest
+from aliyunsdkecs.request.v20140526.RunInstancesRequest import RunInstancesRequest
+from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
+from aliyunsdkecs.request.v20140526.DescribeNetworkInterfacesRequest import DescribeNetworkInterfacesRequest
+from aliyunsdkslb.request.v20140515.DescribeLoadBalancerAttributeRequest import DescribeLoadBalancerAttributeRequest
+from aliyunsdkecs.request.v20140526.RunCommandRequest import RunCommandRequest
+from aliyunsdkecs.request.v20140526.SendFileRequest import SendFileRequest
+from aliyunsdkecs.request.v20140526.StopInstancesRequest import StopInstancesRequest
+from aliyunsdkecs.request.v20140526.DeleteInstancesRequest import DeleteInstancesRequest
+from aliyunsdkecs.request.v20140526.DescribeInstanceStatusRequest import DescribeInstanceStatusRequest
+from aliyunsdkcore.request import CommonRequest
+from alibabacloud_alb20200616.client import Client as Alb20200616Client
+from alibabacloud_tea_openapi import models as open_api_models
+from alibabacloud_alb20200616 import models as alb_models
 from alibabacloud_tea_util import models as util_models
-from alibabacloud_tea_util.client import Client as UtilClient
-import config
-
-
-# 发送文件到ECS实例
-def send_file_to_ecs(client: Ecs20140526Client, instance_id_list: List[str], target_dir: str, name: str, content: str) -> None:
-    for instance_id in instance_id_list:  # 遍历实例ID列表
-        describe_instance_attribute_request = ecs_20140526_models.DescribeInstanceAttributeRequest(  # 创建查询实例属性的请求
-            instance_id=instance_id  # 实例ID
-        )
-        runtime = util_models.RuntimeOptions()  # 创建运行时选项
-        response = client.describe_instance_attribute_with_options(describe_instance_attribute_request, runtime)  # 调用ECS客户端的查询实例属性方法
-        vpc_attributes = response.body.vpc_attributes  # 获取VPC属性
-        vpc_id = vpc_attributes.vpc_id  # 获取VPC ID
-        inner_ip_address = response.body.inner_ip_address  # 获取内网IP地址
-        ssh_client = UtilClient(vpc_id, inner_ip_address, 'root', config.ALIBABA_CLOUD_ACCESS_KEY_ID, config.ALIBABA_CLOUD_ACCESS_KEY_SECRET)  # 创建SSH客户端
-        ssh_client.upload_file(target_dir, name, content)  # 上传文件到实例
-        logging.info(f"File {name} sent to instance {instance_id}")  # 记录日志
-
-# 在ECS实例上执行命令
-def run_command(client: Ecs20140526Client, instance_ids: List[str], command: str) -> None:
-    for instance_id in instance_ids:  # 遍历实例ID列表
-        describe_instance_attribute_request = ecs_20140526_models.DescribeInstanceAttributeRequest(  # 创建查询实例属性的请求
-            instance_id=instance_id  # 实例ID
-        )
-        runtime = util_models.RuntimeOptions()  # 创建运行时选项
-        response = client.describe_instance_attribute_with_options(describe_instance_attribute_request, runtime)  # 调用ECS客户端的查询实例属性方法
-        vpc_attributes = response.body.vpc_attributes  # 获取VPC属性
-        vpc_id = vpc_attributes.vpc_id  # 获取VPC ID
-        inner_ip_address = response.body.inner_ip_address  # 获取内网IP地址
-        ssh_client = UtilClient(vpc_id, inner_ip_address, 'root', config.ALIBABA_CLOUD_ACCESS_KEY_ID, config.ALIBABA_CLOUD_ACCESS_KEY_SECRET)  # 创建SSH客户端
-        ssh_client.run_command(command)  # 在实例上执行命令
-        logging.info(f"Command '{command}' executed on instance {instance_id}")  # 记录日志
+
+
+logging.basicConfig(level=logging.INFO,
+                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
+                    datefmt='%a, %d %b %Y %H:%M:%S')
+
+
+def send_msg_to_feishu(webhook, key_word, msg_text):
+    """发送消息到飞书"""
+    headers = {'Content-Type': 'application/json'}
+    payload_message = {
+        "msg_type": "text",
+        "content": {
+            "text": '{}: {}'.format(key_word, msg_text)
+        }
+    }
+    response = requests.request('POST', url=webhook, headers=headers, data=json.dumps(payload_message))
+    logging.info(response.text)
+
+
+def connect_client(access_key_id, access_key_secret, region_id):
+    """
+    初始化账号,连接客户端
+    :param access_key_id: access key Id, type-string
+    :param access_key_secret: access key secret, type-string
+    :param region_id: region_id
+    :return: clt
+    """
+    try:
+        clt = AcsClient(ak=access_key_id, secret=access_key_secret, region_id=region_id)
+        return clt
+    except Exception as e:
+        # 失败,记录报错信息,发送通知,停止并退出
+        logging.error(e)
+        sys.exit()
+
+
+def build_create_instances_request(image_id, vswitch_id, security_group_id, zone_id, instance_type, instance_name,
+                                   disk_size, disk_category, key_pair_name, tags):
+    """
+    购买服务器参数配置
+    :param image_id: 使用的镜像信息 type-string
+    :param vswitch_id: 选择的交换机 type-string
+    :param security_group_id: 当前vpc类型的安全组 type-string
+    :param zone_id: 服务器所在区域 type-string
+    :param instance_type: 实例规格 type-string
+    :param instance_name: 实例命名 type-string
+    :param disk_size: 磁盘大小,单位:G,type-string
+    :param disk_category: 磁盘类型 type-string
+    :param key_pair_name: 密钥对名称 type-string
+    :param tags: 标签 type-list, eg: [{"Key": "ecs", "Value": "rov-server.prod"}, ...]
+    :return: request
+    """
+    request = RunInstancesRequest()
+    request.set_ImageId(image_id)
+    request.set_VSwitchId(vswitch_id)
+    request.set_SecurityGroupId(security_group_id)
+    request.set_ZoneId(zone_id)
+    request.set_InstanceType(instance_type)
+    request.set_InstanceName(instance_name)
+    request.set_SystemDiskSize(disk_size)
+    request.set_SystemDiskCategory(disk_category)
+    request.set_KeyPairName(key_pair_name)
+    request.set_Tags(tags)
+    return request
+
+
+def send_request(client, request):
+    """
+    发送API请求
+    :param client: 客户端连接
+    :param request: 请求配置
+    :return: response
+    """
+    request.set_accept_format('json')
+    try:
+        response = client.do_action_with_exception(request)
+        response = json.loads(response)
+        # logging.info(response)
+        return response
+    except Exception as e:
+        # 失败,记录报错信息,发送通知,停止并退出
+        logging.error(e)
+        sys.exit()
+
+
+def send_req(client, request):
+    """
+    发送API请求
+    :param client: 客户端连接
+    :param request: 请求配置
+    :return: response
+    """
+    request.set_accept_format('json')
+    response = client.do_action_with_exception(request)
+    #print(response)
+    response = json.loads(response)
+    print(response)
+        # logging.info(response)
+    print(response.get('Code'))
+    return response
+    #except Exception as e:
+        # 失败,记录报错信息,发送通知,停止并退出
+    #logging.error(e)
+    #sys.exit()
+
+def check_instance_running(client, instance_ids):
+    """
+    检查服务器运行状态
+    :param client: 客户端连接
+    :param instance_ids: 实例id列表, type-list
+    :return: running_count,Status为Running的实例数
+    """
+    try:
+        request = DescribeInstancesRequest()
+        request.set_InstanceIds(json.dumps(instance_ids))
+        request.set_PageSize(100)
+        response = send_request(client=client, request=request)
+        if response.get('Code') is None:
+            instances_list = response.get('Instances').get('Instance')
+            running_count = 0
+            running_instances = []
+            for instance_detail in instances_list:
+                if instance_detail.get('Status') == "Running":
+                    running_count += 1
+                    running_instances.append(instance_detail.get('InstanceId'))
+            return running_count, running_instances
+        else:
+            # 失败,记录报错信息,发送通知,停止并退出
+            logging.error(response)
+            sys.exit()
+    except Exception as e:
+        # 失败,记录报错信息,发送通知,停止并退出
+        logging.error(e)
+        sys.exit()
+
+
+def create_multiple_instances(amount, client,
+                              image_id, vswitch_id, security_group_id, zone_id, instance_type, instance_name,
+                              disk_size, disk_category, key_pair_name, tags):
+    """
+    创建多个ECS实例
+    :param amount: 创建实例数 type-int 取值范围:[1, 100]
+    :param client: 购买机器客户端连接
+    :param image_id: 使用的镜像信息 type-string
+    :param vswitch_id: 选择的交换机 type-string
+    :param security_group_id: 当前vpc类型的安全组 type-string
+    :param zone_id: 服务器所在区域 type-string
+    :param instance_type: 实例规格 type-string
+    :param instance_name: 实例命名 type-string
+    :param disk_size: 磁盘大小,单位:G,type-string
+    :param disk_category: 磁盘类型 type-string
+    :param key_pair_name: 密钥对名称 type-string
+    :param tags: 标签 type-list, eg: [{"Key": "ecs", "Value": "rov-server.prod"}, ...]
+    :return:
+    """
+    logging.info(f"create instances start, request amount: {amount}.")
+    # 1. 连接客户端
+    # create_instances_clt = connect_client(
+    #     access_key_id=access_key_id, access_key_secret=access_key_secret, region_id=region_id
+    # )
+    # 2. 请求参数配置
+    request = build_create_instances_request(
+        image_id=image_id, vswitch_id=vswitch_id, security_group_id=security_group_id, zone_id=zone_id,
+        instance_type=instance_type, instance_name=instance_name, disk_size=disk_size, disk_category=disk_category,
+        key_pair_name=key_pair_name, tags=tags
+    )
+    request.set_Amount(amount)
+    # 3. 发送API请求,购买机器并启动
+    response = send_request(client=client, request=request)
+    if response.get('Code') is None:
+        instance_ids = response.get('InstanceIdSets').get('InstanceIdSet')
+        logging.info(f"success amount: {len(instance_ids)}, instance ids: {instance_ids}.")
+        # 获取机器运行状态
+        running_amount = 0
+        while running_amount < amount:
+            time.sleep(10)
+            running_amount, running_instances = check_instance_running(client=client, instance_ids=instance_ids)
+            logging.info(f"running amount: {running_amount}, running instances: {running_instances}.")
+        return instance_ids
+    else:
+        # 失败,记录报错信息,发送通知,停止并退出
+        logging.error(response)
+        sys.exit()
+
+
+def run_command(client, instance_ids, command):
+    """
+    批量执行命令
+    :param client: 客户端连接
+    :param instance_ids: 实例id列表, type-list, 最多能指定50台ECS实例ID
+    :param command: 命令 type-string
+    :return:
+    """
+    for i in range(len(instance_ids) // 50 + 1):
+        instance_id_list = instance_ids[i * 50:(i + 1) * 50]
+        if len(instance_id_list) == 0:
+            return
+        request = RunCommandRequest()
+        request.set_accept_format('json')
+        request.set_Type("RunShellScript")
+        request.set_CommandContent(command)
+        request.set_InstanceIds(instance_id_list)
+        response = send_request(client=client, request=request)
+        logging.info(response)
+
+def run_per_command(client, instance, command):
+    """
+    批量执行命令
+    :param client: 客户端连接
+    :param instance_ids: 实例id列表, type-list, 最多能指定50台ECS实例ID
+    :param command: 命令 type-string
+    :return:
+    """
+    #for i in range(len(instance_ids) // 50 + 1)
+    request = RunCommandRequest()
+    request.set_accept_format('json')
+    request.set_Type("RunShellScript")
+    request.set_CommandContent(command)
+    request.set_InstanceIds([instance])
+    response = send_req(client=client, request=request)
+    logging.info(response)
+    return response
+
+
+def get_instance_ids(client, slb_id):
+    """
+    获取slb下所有服务器instanceId
+    :param client: 客户端连接
+    :param slb_id: 负载均衡id type-string
+    :return: instance_ids type-list
+    """
+    request = DescribeLoadBalancerAttributeRequest()
+    request.set_accept_format('json')
+    request.set_LoadBalancerId(slb_id)
+    response = send_request(client=client, request=request)
+    instance_ids = [instance["ServerId"] for instance in response["BackendServers"]["BackendServer"]]
+    return instance_ids
+
+
+def get_ip_address(client, instance_id):
+    """
+    获取实例IP地址
+    :param client: 客户端连接
+    :param instance_id: 实例id, type-string
+    :return: ip_address, type-string
+    """
+    request = DescribeNetworkInterfacesRequest()
+    request.set_accept_format('json')
+    request.set_InstanceId(instance_id)
+    response = send_request(client=client, request=request)
+    ip_address = response['NetworkInterfaceSets']['NetworkInterfaceSet'][0]['PrivateIpAddress']
+    return ip_address
+
+
+def set_weight_for_instances(client, slb_id, instance_id_list, weight):
+    """
+    同时设置多台服务器的slb权重,权重一样
+    :param client: 客户端连接
+    :param slb_id: slb_id
+    :param instance_id_list: 服务器id list
+    :param weight: 权重值
+    :return: None
+    """
+    for i in range(len(instance_id_list) // 20 + 1):
+        instances_list = instance_id_list[i * 20:(i + 1) * 20]
+        if len(instances_list) == 0:
+            return
+        BackendServers = [{"ServerId": instance_id, "Weight": weight} for instance_id in instances_list]
+        request = CommonRequest()
+        request.set_accept_format('json')
+        request.set_domain('slb.aliyuncs.com')
+        request.set_version('2014-05-15')
+        request.set_method('POST')
+        request.set_action_name('SetBackendServers')
+        request.add_query_param('BackendServers', BackendServers)
+        request.add_query_param('LoadBalancerId', slb_id)
+        response = send_request(client=client, request=request)
+
+
+def connect_alb_client(access_key_id, access_key_secret):
+    """
+    初始化ALB客户端
+    :param access_key_id: access key Id, type-string
+    :param access_key_secret: access key secret, type-string
+    :return: alb_client
+    """
+    config = open_api_models.Config(
+        access_key_id=access_key_id,
+        access_key_secret=access_key_secret,
+        endpoint='alb.cn-hangzhou.aliyuncs.com'
+    )
+    alb_client = Alb20200616Client(config)
+    return alb_client
+
+
+def add_servers_to_server_group(alb_client, server_group_id, instance_id, weight):
+    """
+    添加服务器到ALB服务器组
+    :param alb_client: ALB客户端连接
+    :param server_group_id: 服务器组ID
+    :param instance_id: 实例ID
+    :param weight: 权重
+    """
+    server = alb_models.AddServersToServerGroupRequestServers(
+        server_id=instance_id,
+        server_type='ecs',
+        weight=weight
+    )
+    request = alb_models.AddServersToServerGroupRequest(
+        server_group_id=server_group_id,
+        servers=[server]
+    )
+    runtime = util_models.RuntimeOptions()
+    try:
+        alb_client.add_servers_to_server_group_with_options(request, runtime)
+        logging.info(f"Successfully added server {instance_id} to server group {server_group_id} with weight {weight}.")
+    except Exception as e:
+        logging.error(f"Failed to add server {instance_id} to server group {server_group_id}: {str(e)}")
+
+def remove_servers_from_server_group(alb_client, server_group_id, instance_id):
+    """
+    从ALB服务器组中移除服务器
+    :param alb_client: ALB客户端连接
+    :param server_group_id: 服务器组ID
+    :param instance_id: 实例ID
+    """
+    server = alb_models.RemoveServersFromServerGroupRequestServers(
+        server_id=instance_id,
+        server_type='ecs'
+    )
+    request = alb_models.RemoveServersFromServerGroupRequest(
+        server_group_id=server_group_id,
+        servers=[server]
+    )
+    runtime = util_models.RuntimeOptions()
+    try:
+        alb_client.remove_servers_from_server_group_with_options(request, runtime)
+        logging.info(f"Successfully removed server {instance_id} from server group {server_group_id}.")
+    except Exception as e:
+        logging.error(f"Failed to remove server {instance_id} from server group {server_group_id}: {str(e)}")
+
+
+
+
+
+def send_file_to_ecs(client, instance_id_list, target_dir, name, content):
+    """
+    发送文件到ecs
+    :param client:
+    :param instance_id_list: 最多能指定50台ECS实例ID
+    :param target_dir: 文件存放目录 type-string
+    :param name: 文件名 type-string
+    :param content: 文件内容 type-string
+    :return:
+    """
+    for i in range(len(instance_id_list) // 50 + 1):
+        instance_ids = instance_id_list[i * 50:(i + 1) * 50]
+        if len(instance_ids) == 0:
+            return
+        request = SendFileRequest()
+        request.set_Content(content)
+        request.set_TargetDir(target_dir)
+        request.set_Name(name)
+        request.set_Overwrite(True)
+        request.set_InstanceIds(instance_ids)
+        response = send_request(client=client, request=request)
+
+
+def stop_instances(client, instance_ids, force_stop=False):
+    """
+    停止实例
+    :param client:
+    :param instance_ids: 实例ID, type-list
+    :param force_stop: 是否强制关机, True-强制关机, False-正常关机, type-bool
+    :return:
+    """
+    request = StopInstancesRequest()
+    request.set_InstanceIds(instance_ids)
+    request.set_ForceStop(force_stop)
+    response = send_request(client=client, request=request)
+    return response
+
+
+def release_instances(client, instance_ids, force=False):
+    """
+    释放实例
+    :param client:
+    :param instance_ids: instance_id, type-list
+    :param force: 是否强制释放, True-强制释放, False-正常释放, type-bool
+    :return:
+    """
+    request = DeleteInstancesRequest()
+    request.set_InstanceIds(instance_ids)
+    request.set_Force(force)
+    response = send_request(client=client, request=request)
+    return response
+
+
+def get_instances_status(client, instance_ids):
+    """
+    获取实例运行状态
+    :param client:
+    :param instance_ids: instance_id, type-liist
+    :return:
+    """
+    request = DescribeInstanceStatusRequest()
+    request.set_InstanceIds(instance_ids)
+    request.set_PageSize(50)
+    response = send_request(client=client, request=request)
+    return response
+
+
+def set_instance_weight_process(client, slb_id, instance_id_list, weight_list):
+    """
+    修改服务器的权重值
+    :param client: slb客户端连接·
+    :param slb_id: slb id
+    :param instance_id_list: instance id list
+    :param weight_list: 权重修改列表 type-list [(weight, sleep_time), ...]
+    :return:
+    """
+    for weight, sleep_time in weight_list:
+        logging.info(f"weight = {weight}")
+        flag = True
+        while flag:
+            try:
+                set_weight_for_instances(client=client, slb_id=slb_id, instance_id_list=instance_id_list, weight=weight)
+                time.sleep(sleep_time)
+                flag = False
+            except Exception as e:
+                time.sleep(10)
+                continue
+
+
+def add_backend_servers(client, slb_id, instances):
+    """
+    服务器挂载到负载均衡(必须是状态为运行中的后端服务器才可以加入负载均衡实例,每次调用最多可添加20个后端服务器)
+    :param client:
+    :param slb_id:
+    :param instances: 实例列表 [(instance_id, ip), ...]
+    :return:
+    """
+    try:
+        for i in range(len(instances) // 20 + 1):
+            instances_list = instances[i * 20:(i + 1) * 20]
+            if len(instances_list) == 0:
+                return
+            request = AddBackendServersRequest()
+            request.set_accept_format('json')
+            request.set_LoadBalancerId(slb_id)
+            backend_servers = [
+                {"ServerId": instance_id, "Weight": "0", "Type": "ecs", "ServerIp": ip_address}
+                for instance_id, ip_address in instances_list]
+            request.set_BackendServers(backend_servers)
+            response = client.do_action_with_exception(request)
+            return response
+    except Exception as e:
+        logging.error(e)
+        sys.exit()
+
+
+def remove_backend_servers(client, slb_id, instances):
+    """
+    服务器从负载均衡移除(一次调用最多可以移除20个后端服务器)
+    :param client:
+    :param slb_id:
+    :param instances: 实例列表 [instance_id, ...]
+    :return:
+    """
+    try:
+        for i in range(len(instances) // 20 + 1):
+            instances_list = instances[i * 20:(i + 1) * 20]
+            if len(instances_list) == 0:
+                return
+            request = RemoveBackendServersRequest()
+            request.set_accept_format('json')
+            request.set_LoadBalancerId(slb_id)
+            backend_servers = [
+                {"ServerId": instance_id, "Weight": "0", "Type": "ecs"}
+                for instance_id in instances_list]
+            request.set_BackendServers(backend_servers)
+            response = client.do_action_with_exception(request)
+            return response
+    except Exception as e:
+        logging.error(e)
+        sys.exit()
+
+
+def set_instance_weight_process_with_slbs(client, slb_id_list, instance_id_list, weight_list):
+    """
+    修改服务器的权重值
+    :param client: slb客户端连接
+    :param slb_id_list: slb id list
+    :param instance_id_list: instance id list
+    :param weight_list: 权重修改列表 type-list [(weight, sleep_time), ...]
+    :return:
+    """
+    for weight, sleep_time in weight_list:
+        logging.info(f"修改权重中: weight = {weight}")
+        for slb_id in slb_id_list:
+            flag = True
+            while flag:
+                try:
+                    set_weight_for_instances(client=client, slb_id=slb_id, instance_id_list=instance_id_list, weight=weight)
+                    logging.info(f"slb: {slb_id} finished!")
+                    flag = False
+                except Exception as e:
+                    time.sleep(10)
+                    continue
+        time.sleep(sleep_time)
+
+
+def add_backend_servers_with_slbs(client, slb_id_list, instances):
+    """
+    服务器挂载到负载均衡(必须是状态为运行中的后端服务器才可以加入负载均衡实例,每次调用最多可添加20个后端服务器)
+    :param client:
+    :param slb_id_list:
+    :param instances: 实例列表 [(instance_id, ip), ...]
+    :return:
+    """
+    try:
+        for i in range(len(instances)//20 + 1):
+            instances_list = instances[i*20:(i+1)*20]
+            if len(instances_list) == 0:
+                return
+            for slb_id in slb_id_list:
+                request = AddBackendServersRequest()
+                request.set_accept_format('json')
+                request.set_LoadBalancerId(slb_id)
+                backend_servers = [
+                    {"ServerId": instance_id, "Weight": "0", "Type": "ecs", "ServerIp": ip_address}
+                    for instance_id, ip_address in instances_list]
+                request.set_BackendServers(backend_servers)
+                response = client.do_action_with_exception(request)
+                logging.info(f"slb: {slb_id} add backend servers finished!")
+            logging.info(f"i: {i}, count: {len(instances_list)}, instances: {instances_list} "
+                         f"add backend servers finished!")
+    except Exception as e:
+        logging.error(e)
+        sys.exit()
+
+
+def remove_backend_servers_with_slbs(client, slb_id_list, instances):
+    """
+    服务器从负载均衡移除(一次调用最多可以移除20个后端服务器)
+    :param client:
+    :param slb_id_list:
+    :param instances: 实例列表 [instance_id, ...]
+    :return:
+    """
+    try:
+        for i in range(len(instances)//20 + 1):
+            instances_list = instances[i*20:(i+1)*20]
+            if len(instances_list) == 0:
+                return
+            for slb_id in slb_id_list:
+                request = RemoveBackendServersRequest()
+                request.set_accept_format('json')
+                request.set_LoadBalancerId(slb_id)
+                backend_servers = [
+                    {"ServerId": instance_id, "Weight": "0", "Type": "ecs"}
+                    for instance_id in instances_list]
+                request.set_BackendServers(backend_servers)
+                response = client.do_action_with_exception(request)
+                logging.info(f"slb: {slb_id} remove backend servers finished!")
+            logging.info(f"i: {i}, count: {len(instances_list)}, instances: {instances_list} "
+                         f"remove backend servers finished!")
+    except Exception as e:
+        logging.error(e)
+        sys.exit()