簡體   English   中英

計划啟動 EC2 實例並在其中運行 Python 腳本

[英]Schedule to start an EC2 instance and run a python script within it

我正在嘗試在 AWS 中安排我的 python 腳本,但是我不希望實例一直運行。 因此,嘗試自動化以下過程:

  1. 在特定時間啟動 EC2 實例
  2. 在其中運行 python 腳本
  3. 作業完成后停止 EC2 實例。

我不能直接將此腳本作為 Lambda 函數運行,因為該腳本執行一些需要更多 RAM 的並行處理,因此選擇更大的 AWS 實例而不是將其編寫為 lambda 函數。 此外,不希望此實例一直運行,因為它很昂貴。

到目前為止,我使用 Lambda 和 CloudWatch · matoski.com 跟蹤了 AWS EC2 實例的自動啟動和停止,並創建了一個 Lambda 函數來在特定時間啟動和停止實例,但是我找不到運行一次 python 腳本的方法實例已啟動。

任何人都可以指出我正確的方向嗎?

我的應用程序每天在 UST 的 13:39 運行一個實例,並在處理完成后自行關閉。 它在下面使用

  1. 使用雲監視事件規則的預定 lambda 函數

Cloud watch 事件/規則配置

  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 作業將使用以下代碼段自行關閉

 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])

對於遇到這個問題的未來開發人員來說,一個更新的方法是:

  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

我在使用這篇文章中的解決方案啟動和停止實例時遇到問題。 然后我按照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,然后單擊“權限”和“使用現有角色”旁邊的下拉菜單,並附加您在​​步驟 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 ...因為當你沒有使用它來運行 Python 文件時,有時sudo會把事情搞砸。 在我的例子中,我有一個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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM