【问题标题】:Schedule to start an EC2 instance and run a python script within it安排启动 EC2 实例并在其中运行 python 脚本
【发布时间】:2018-09-12 08:05:40
【问题描述】:

我正在尝试在 AWS 中安排我的 python 脚本,但是我不希望这些实例一直在运行。因此,尝试自动化以下过程:

  1. 在特定时间启动 EC2 实例
  2. 在其中运行 python 脚本
  3. 作业完成后停止 EC2 实例。

我不能直接将此脚本作为 Lambda 函数运行,因为该脚本会执行一些需要更多 RAM 的并行处理,因此选择更大的 AWS 实例而不是将其编写为 lambda 函数。此外,不希望此实例一直运行,因为它很昂贵。

到目前为止,我关注Automatic starting and stopping of AWS EC2 instances with Lambda and CloudWatch · matoski.com 并创建了一个 Lambda 函数来在特定时间启动和停止实例,但是一旦启动实例,我找不到运行 python 脚本的方法。

谁能指出我正确的方向?

【问题讨论】:

  • /etc/rc.local调用脚本
  • 但是脚本完成后如何停止实例。
  • 您编写脚本来调用操作系统关闭命令
  • 哦,这是新的。我们可以从脚本中关闭该实例吗?请举个例子。否则没问题,我会尝试找到它。但感谢您的意见。
  • 它需要root权限,但在这里查看各种解决方案(我自己不确定接受的答案)stackoverflow.com/questions/23013274/…

标签: python amazon-web-services amazon-ec2


【解决方案1】:

我在使用本文中的解决方案启动和停止实例时遇到问题。然后我按照https://aws.amazon.com/premiumsupport/knowledge-center/start-stop-lambda-cloudwatch/ 上的说明进行操作,这真的很容易。基本上:

  1. 转到https://console.aws.amazon.com/iam/home#/home 并在左侧单击策略,然后单击创建策略。然后单击 JSON 选项卡。然后复制粘贴以创建新策略:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Start*",
        "ec2:Stop*"
      ],
      "Resource": "*"
    }
  ]
}
  1. 转到https://console.aws.amazon.com/iam/home#/home,然后在左侧选择角色。确保您选择 Lambda 作为您的 AWS 服务,并附上您在第 1 步中创建的策略。

  2. 然后转到 Lambda 控制台,单击创建函数。选择 Python 3.7,然后单击 Permissions 和 Use An Existing Role 旁边的下拉菜单,并附加您在​​第 2 步中创建的 IAM 角色。

  3. 将此用作您的代码:

import boto3
region = 'us-west-1' # Dont use the specific, like instead of us-east-1d just write us-east-1
instances = ['i-xxxxxxxxxxxx']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.start_instances(InstanceIds=instances)
    print('started your instances: ' + str(instances))
  1. 启动您的 EC2 实例,然后键入 which python 以找到您的 python 路径并将其写下来。然后,输入crontab -e 以编辑您的 CRON 作业。不要使用sudo...因为有时sudo 在您没有使用它来运行Python 文件时会搞砸。在我的例子中,我有一个 pgpass 文件存储我的密码,sudo 看不到,但删除 sudo 有效!
  2. 在 crontab 编辑器中,在注释行之后输入 @reboot /path/to/python /path/to/file.py 例如,对我来说这是 @reboot /home/init/python /home/init/Notebooks/mypredictor.py
  3. 在 Python 文件结束时,您需要停止实例。你可以这样做:
import boto3
region = 'us-west-1' # Dont use the specific, like instead of us-east-1d just write us-east-1
instances = ['i-xxxxxxxxxxxx']
ec2 = boto3.client('ec2', region_name=region)

ec2.stop_instances(InstanceIds=instances)

【讨论】:

    【解决方案2】:

    对于遇到这个问题的未来开发人员,一个更新的方法是:

    1. 使用包含AmazonEC2RoleforSSM 策略的角色创建您的 EC2
    2. 创建一个 lambda 来执行唤醒、运行命令、关机
    3. 使用 Cloudwatch 事件触发 lambda

    所以:

    1. 按照此处的步骤操作:https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

    2. 使用以下 lambda 框架:

    import time
    import boto3
    
    REGION_NAME = 'us-east-1'
    
    WORKING_DIRECTORY = '<YOUR WORKING DIRECTORY, IF ANY>'
    
    COMMAND = """
        echo "Hello, world!"
        """
    
    INSTANCE_ID = '<YOUR INSTANCE ID>'
    
    
    def start_ec2():
        ec2 = boto3.client('ec2', region_name=REGION_NAME)
        ec2.start_instances(InstanceIds=[INSTANCE_ID])
    
        while True:
            response = ec2.describe_instance_status(InstanceIds=[INSTANCE_ID], IncludeAllInstances=True)
            state = response['InstanceStatuses'][0]['InstanceState']
    
            print(f"Status: {state['Code']} - {state['Name']}")
    
            # If status is 16 ('running'), then proceed, else, wait 5 seconds and try again
            if state['Code'] == 16:
                break
            else:
                time.sleep(5)
    
        print('EC2 started')
    
    
    def stop_ec2():
        ec2 = boto3.client('ec2', region_name=REGION_NAME)
        ec2.stop_instances(InstanceIds=[INSTANCE_ID])
    
        while True:
            response = ec2.describe_instance_status(InstanceIds=[INSTANCE_ID], IncludeAllInstances=True)
            state = response['InstanceStatuses'][0]['InstanceState']
    
            print(f"Status: {state['Code']} - {state['Name']}")
    
            # If status is 80 ('stopped'), then proceed, else wait 5 seconds and try again
            if state['Code'] == 80:
                break
            else:
                time.sleep(5)
    
        print('Instance stopped')
    
    
    def run_command():
        client = boto3.client('ssm', region_name=REGION_NAME)
    
        time.sleep(10)  # I had to wait 10 seconds to "send_command" find my instance 
    
        cmd_response = client.send_command(
            InstanceIds=[INSTANCE_ID],
            DocumentName='AWS-RunShellScript',
            DocumentVersion="1",
            TimeoutSeconds=300,
            MaxConcurrency="1",
            CloudWatchOutputConfig={'CloudWatchOutputEnabled': True},
            Parameters={
                'commands': [COMMAND],
                'executionTimeout': ["300"],
                'workingDirectory': [WORKING_DIRECTORY],
            },
        )
    
        command_id = cmd_response['Command']['CommandId']
        time.sleep(1)  # Again, I had to wait 1s to get_command_invocation recognises my command_id
    
        retcode = -1
        while True:
            output = client.get_command_invocation(
                CommandId=command_id,
                InstanceId=INSTANCE_ID,
            )
    
            # If the ResponseCode is -1, the command is still running, so wait 5 seconds and try again
            retcode = output['ResponseCode']
            if retcode != -1:
                print('Status: ', output['Status'])
                print('StdOut: ', output['StandardOutputContent'])
                print('StdErr: ', output['StandardErrorContent'])
                break
    
            print('Status: ', retcode)
            time.sleep(5)
    
        print('Command finished successfully') # Actually, 0 means success, anything else means a fail, but it didn't matter to me
        return retcode
    
    
    def lambda_handler(event, context):
        retcode = -1
        try:
            start_ec2()
            retcode = run_command()
        finally:  # Independently of what happens, try to shutdown the EC2
            stop_ec2()
    
        return retcode
    
    
    1. 按照此处的步骤操作:https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/RunLambdaSchedule.html

    【讨论】:

      【解决方案3】:

      我的应用程序每天在美国东部时间 13:39 运行一个实例,并在处理完成后自行关闭。它在下面使用

      1. 使用云观察事件规则的预定 lambda 函数

      Cloud watch Event/rules config

      1. lambda 触发器将启动一个实例(带有硬编码的 id)

      import boto3
      def lambda_handler(event, context):
          ec2 = boto3.client('ec2', region_name='ap-south-1')
          ec2.start_instances(InstanceIds=['i-xxxxxxx'])
          print('started your instances: ' + str('i-xxxxxx'))
          return
      1. 这会触发一个运行 cron 来执行 Python 脚本的实例

        @reboot python /home/Init.py

      2. 脚本完成后,python 作业会使用下面的 sn-p 自行关闭

      import boto.ec2
      import boto.utils
      import logging
      logger=logging.getLogger()
      def stop_ec2():
          conn = boto.ec2.connect_to_region("ap-south-1") # or your region
          # Get the current instance's id
          my_id = boto.utils.get_instance_metadata()['instance-id']
          logger.info(' stopping EC2 :'+str(my_id))
          conn.stop_instances(instance_ids=[my_id])

      【讨论】:

      • 实例启动后如何让 cron 作业运行?
      • @reboot 是 cron 的特殊规范,而不是时间/日期字段。这可确保在实例启动/重新启动时运行脚本。我使用 Amazon Linux AMI 成功完成了上述配置
      • 谢谢。但是你在哪里指定@reboot 命令。这是一个shell脚本吗?你能解释一下吗?
      • 我启动了一个实例,复制了 py 脚本并在下面添加了 sudo crontab -e @reboot /usr/bin/python /path/to/file/script.py 然后停止了实例。这是 lambda 使用实例 id 启动的同一个实例
      • 我触发了一个实例,使用 Sudo crontab -e 在 Cron 中添加了@reboot 复制了所有脚本,然后停止了该实例。稍后 lambda 将使用实例 ID 为同一实例启动,并且脚本运行原因是 @reboot
      猜你喜欢
      • 1970-01-01
      • 2022-01-23
      • 2012-04-24
      • 2017-06-10
      • 1970-01-01
      • 2021-05-28
      • 1970-01-01
      • 2014-04-06
      • 1970-01-01
      相关资源
      最近更新 更多