【问题标题】:Auto Shutdown and Start Amazon EC2 Instance自动关闭和启动 Amazon EC2 实例
【发布时间】:2011-01-25 15:54:16
【问题描述】:

我可以使用 Amazon API 自动启动和终止我的 Amazon 实例吗?你能描述一下如何做到这一点吗?理想情况下,我需要每天以指定的时间间隔启动和停止实例。

【问题讨论】:

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


【解决方案1】:

以防万一有人偶然发现这个老问题,现在您可以通过向 Auto Scaling 组添加计划来实现相同的目的:在特定时间将 Auto Scaling 组中的实例数量增加到 1,然后再减少之后为 0。

由于这个答案获得了很多意见,我想链接到一个非常有用的指南:Running EC2 Instances on a Recurring Schedule with Auto Scaling

【讨论】:

  • 我尝试了链接中描述的方法,它确实在教程指定的时间启动/停止实例。但是,我在 AWS Web 控制台中注意到,当一个实例通过这种方法启动时,它并没有使用密钥启动(这样你就可以 ssh 进入它),而且它似乎也没有和我一样的东西安装在我用作测试的微实例上(我不是云专家,但我认为这意味着这个启动的新实例没有连接到 EBS?)有没有办法自动启动和按时间安排停止同一个实例?
  • @KiranK。这是否意味着新实例未附加到当前使用的 EBS 卷?你用了什么?
  • @KiranK 我会使用 Packer 来构建 AMI,这样它就有了启动所需的任何东西。 packer.io/docs/builders/amazon/ebsmartinfowler.com/bliki/PhoenixServer.html
【解决方案2】:

您可以尝试直接使用 Amazon EC2 API 工具。有 实际上只需要两个命令:ec2-start-instances 和 ec2-stop-instances。 确保环境变量,例如 EC2_HOME, AWS_CREDENTIAL_FILE、EC2_CERT、EC2_PRIVATE_KEY 等都正确 配置和所有 AWS 凭证、证书和私钥文件 位置正确 - 您可以在 AWS EC2 API 中找到更多信息 工具文档。

您可以先手动测试该命令,然后当一切正常时, 在 Windows 上配置 Unix crontab 或计划任务。你可以找到 下面是 Linux /etc/crontab 文件的示例(不要忘记所有 上面提到的那些环境变量需要存在 “您的帐户”用户。

/etc/crontab
0 8     * * *   your-account ec2-start-instances <your_instance_id>
0 16    * * *   your-account ec2-stop-instances <your_instance_id>
# Your instance will be started at 8am and shutdown at 4pm.

我是 BitNami Cloud 项目的开发人员,我们在其中打包 免费、易于使用的 AWS 工具(包括我提到的那些) 您可能想尝试的安装程序:BitNami CloudTools pack stack

【讨论】:

  • 为此,您仍然需要另一个实例。因为关闭不是问题,而是启动。关闭后,Crone 或任何东西都不会在死机中运行。
  • 我跟随 these steps 在我的亚马逊 Linux 实例上设置 AWS CLI 工具。停止实例工作正常。但是启动一个已经停止的实例会给出 400 错误,找不到实例 ID。如何启动已经停止的实例?
【解决方案3】:

我建议您查看EC2 Getting Started Guide,它向您展示了如何使用 EC2 命令行工具执行您需要的操作。您可以轻松地将其编写为 cron 作业(在 Linux / UNIX 上)或 Windows 上的计划作业,以在给定时间调用启动和停止命令。

如果您想通过自己的代码执行此操作,可以使用 SOAP 或 REST API;详情请见Developer Guide

【讨论】:

    【解决方案4】:

    我使用 Boto 库在 Python 中编写代码来执行此操作。您可以调整它以供自己使用。确保将其作为 cron 作业的一部分运行,然后您将能够在 cron 作业运行期间根据需要启动或关闭任意数量的实例。

    #!/usr/bin/python
    #
    # Auto-start and stop EC2 instances
    #
    import boto, datetime, sys
    from time import gmtime, strftime, sleep
    
    # AWS credentials
    aws_key = "AKIAxxx"
    aws_secret = "abcd"
    
    # The instances that we want to auto-start/stop
    instances = [
        # You can have tuples in this format:
        # [instance-id, name/description, startHour, stopHour, ipAddress]
        ["i-12345678", "Description", "00", "12", "1.2.3.4"]
    ]
    
    # --------------------------------------------
    
    # If its the weekend, then quit
    # If you don't care about the weekend, remove these three 
    # lines of code below.
    weekday = datetime.datetime.today().weekday()
    if (weekday == 5) or (weekday == 6):
        sys.exit()
    
    # Connect to EC2
    conn = boto.connect_ec2(aws_key, aws_secret)
    
    # Get current hour
    hh = strftime("%H", gmtime())
    
    # For each instance
    for (instance, description, start, stop, ip) in instances:
        # If this is the hour of starting it...
        if (hh == start):
            # Start the instance
            conn.start_instances(instance_ids=[instance])
            # Sleep for a few seconds to ensure starting
            sleep(10)
            # Associate the Elastic IP with instance
            if ip:
                conn.associate_address(instance, ip)
        # If this is the hour of stopping it...
        if (hh == stop):
            # Stop the instance
            conn.stop_instances(instance_ids=[instance])
    

    【讨论】:

    • 这对 Elastic Beanstalk 环境也可行吗?
    【解决方案5】:

    如果它不是关键任务 - 一个简单的事情是安排批处理文件在每天凌晨 3 点运行“SHUTDOWN”(Windows)。那么至少您不会冒意外让不需要的实例无限期运行的风险。

    显然这只是故事的一半!

    【解决方案6】:

    我工作的公司有客户经常询问这个问题,因此我们编写了一个免费的 EC2 调度应用程序,可在此处获取:

    http://blog.simple-help.com/2012/03/free-ec2-scheduler/

    它适用于 Windows 和 Mac,可让您创建多个每日/每周/每月计划,并允许您使用匹配过滤器轻松包含大量实例或包含您将来添加的实例。

    【讨论】:

      【解决方案7】:

      AWS 数据管道运行良好。 https://aws.amazon.com/premiumsupport/knowledge-center/stop-start-ec2-instances/

      如果您希望排除开始日期(例如周末),请添加一个 ShellCommandPrecondition 对象。

      在 AWS 控制台/数据管道中,创建一个新管道。编辑/导入定义 (JSON) 更容易

          {
      "objects": [
      {
        "failureAndRerunMode": "CASCADE",
        "schedule": {
          "ref": "DefaultSchedule"
        },
        "resourceRole": "DataPipelineDefaultResourceRole",
        "role": "DataPipelineDefaultRole",
        "pipelineLogUri": "s3://MY_BUCKET/log/",
        "scheduleType": "cron",
        "name": "Default",
        "id": "Default"
      },
      {
        "name": "CliActivity",
        "id": "CliActivity",
        "runsOn": {
          "ref": "Ec2Instance"
        },
        "precondition": {
          "ref": "PreconditionDow"
        },
        "type": "ShellCommandActivity",
        "command": "(sudo yum -y update aws-cli) && (#{myAWSCLICmd})"
      },
      {
        "period": "1 days",
        "startDateTime": "2015-10-27T13:00:00",
        "name": "Every 1 day",
        "id": "DefaultSchedule",
        "type": "Schedule"
      },
      {
        "scriptUri": "s3://MY_BUCKET/script/dow.sh",
        "name": "DayOfWeekPrecondition",
        "id": "PreconditionDow",
        "type": "ShellCommandPrecondition"
      },
      {
        "instanceType": "t1.micro",
        "name": "Ec2Instance",
        "id": "Ec2Instance",
        "type": "Ec2Resource",
        "terminateAfter": "50 Minutes"
      }
      ],
      "parameters": [
      {
        "watermark": "aws [options] <command> <subcommand> [parameters]",
        "description": "AWS CLI command",
        "id": "myAWSCLICmd",
        "type": "String"
      }
       ],
      "values": {
      "myAWSCLICmd": "aws ec2 start-instances --instance-ids i-12345678 --region eu-west-1"
      }
      }
      

      将要下载并执行的 Bash 脚本作为前提条件放入您的 S3 存储桶中

      #!/bin/sh
      if [ "$(date +%u)" -lt 6 ]
      then exit 0
      else exit 1
      fi
      

      在周末激活和运行管道时,AWS 控制台管道健康状态读取误导性“错误”。 bash 脚本返回错误(退出 1)并且 EC2 未启动。在第 1 天到第 5 天,状态为“健康”。

      要在关闭办公时间自动停止 EC2,请每天使用 AWS CLI 命令,无需前置条件。

      【讨论】:

        【解决方案8】:

        您可以查看Ylastic 来执行此操作。另一种方法似乎是让一台机器运行,使用 cron 作业或计划任务关闭/启动其他实例。

        显然,如果您只想要一个实例,这是一个昂贵的解决方案,因为一台机器必须始终运行,并且每月支付约 80 美元让一台机器运行 cron 作业并不划算。

        【讨论】:

        【解决方案9】:

        AutoScaling 仅限于终止实例。如果您想停止实例并保留服务器状态,那么外部脚本是最好的方法。

        您可以通过在另一个 24/7 运行的实例上运行作业来做到这一点,或者您可以使用第三方服务,例如 Ylastic(如上所述)或Rocket Peak

        例如,在 C# 中,停止服务器的代码非常简单:

        public void stopInstance(string instance_id, string AWSRegion)
                {
                    RegionEndpoint myAWSRegion = RegionEndpoint.GetBySystemName(AWSRegion);
                    AmazonEC2 ec2 = AWSClientFactory.CreateAmazonEC2Client(AWSAccessKey, AWSSecretKey, myAWSRegion);
                    ec2.StopInstances(new StopInstancesRequest().WithInstanceId(instance_id));
                }
        

        【讨论】:

          【解决方案10】:

          恕我直言,将计划添加到 Auto Scaling 组是前面提到的最佳“类似云”的方法。

          但如果您无法终止实例并使用新实例,例如,如果您有与之关联的弹性 IP 等。

          您可以创建一个 Ruby 脚本来根据日期时间范围启动和停止您的实例。

          #!/usr/bin/env ruby
          
          # based on https://github.com/phstc/amazon_start_stop
          
          require 'fog'
          require 'tzinfo'
          
          START_HOUR = 6 # Start 6AM
          STOP_HOUR  = 0 # Stop  0AM (midnight)
          
          conn = Fog::Compute::AWS.new(aws_access_key_id:     ENV['AWS_ACCESS_KEY_ID'],
                                       aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'])
          
          server = conn.servers.get('instance-id')
          
          tz = TZInfo::Timezone.get('America/Sao_Paulo')
          
          now = tz.now
          
          stopped_range = (now.hour >= STOP_HOUR && now.hour < START_HOUR)
          running_range = !stopped_range
          
          if stopped_range && server.state != 'stopped'
            server.stop
          end
          
          if running_range && server.state != 'running'
            server.start
          
            # if you need an Elastic IP
            # (everytime you stop an instance Amazon dissociates Elastic IPs)
            #
            # server.wait_for { state == 'running' }
            # conn.associate_address server.id, 127.0.0.0
          end
          

          查看amazon_start_stop,使用Heroku Scheduler免费创建调度程序。

          【讨论】:

          【解决方案11】:

          尽管有一些方法可以使用自动缩放来实现这一点,但它可能并不适合所有场合,因为它会终止实例。 Cron 作业永远不会为单个实例工作(尽管它可以完美地用于停止单个实例和在运行多个实例时调度其他实例等情况)。您可以使用StartInstancesRequestStopInstancesRequest 之类的API 调用来实现相同的目的,但您必须再次依赖第三个资源。有许多应用程序可以安排具有许多功能的 AWS 实例,但对于一个简单的解决方案,我会推荐一个免费应用程序,例如 snapleaf.io

          【讨论】:

            【解决方案12】:

            是的,您可以使用 AWS Lambda 做到这一点。您可以在 Cloudwatch 中选择在 UTC 的 Cron 表达式上运行的触发器。

            这里是相关链接https://aws.amazon.com/premiumsupport/knowledge-center/start-stop-lambda-cloudwatch/

            另一种选择是使用awscli,可从pipapt-getyumbrew 获得,然后使用从 IAM 导出的凭据运行 aws configure 并执行以下 bash 脚本,停止使用Name: AppnameValue: Appname Prod 标记的EC2。您可以使用 awscli 标记您的实例或从 AWS 控制台手动标记它。 aws ec2 stop-instances 将停止实例,jq 用于过滤 json 查询并使用来自 aws ec2 describe-instances 的标签获取正确的实例 ID。

            要验证 aws configure 是否成功并返回 json 输出运行 aws ec2 describe-instances 并且您的运行实例 ID 应该在输出中。这是一个示例输出

            {
                "Reservations": [
                    {
                        "Instances": [
                            {
                                "Monitoring": {
                                    "State": "disabled"
                                },
                                "PublicDnsName": "ec2-xxx.ap-south-1.compute.amazonaws.com",
                                "State": {
                                    "Code": xx,
                                    "Name": "running"
                                },
                                "EbsOptimized": false,
                                "LaunchTime": "20xx-xx-xxTxx:16:xx.000Z",
                                "PublicIpAddress": "xx.127.24.xxx",
                                "PrivateIpAddress": "xxx.31.3.xxx",
                                "ProductCodes": [],
                                "VpcId": "vpc-aaxxxxx",
                                "StateTransitionReason": "",
                                "InstanceId": "i-xxxxxxxx",
                                "ImageId": "ami-xxxxxxx",
                                "PrivateDnsName": "ip-xxxx.ap-south-1.compute.internal",
                                "KeyName": "node",
                                "SecurityGroups": [
                                    {
                                        "GroupName": "xxxxxx",
                                        "GroupId": "sg-xxxx"
                                    }
                                ],
                                "ClientToken": "",
                                "SubnetId": "subnet-xxxx",
                                "InstanceType": "t2.xxxxx",
                                "NetworkInterfaces": [
                                    {
                                        "Status": "in-use",
                                        "MacAddress": "0x:xx:xx:xx:xx:xx",
                                        "SourceDestCheck": true,
                                        "VpcId": "vpc-xxxxxx",
                                        "Description": "",
                                        "NetworkInterfaceId": "eni-xxxx",
                                        "PrivateIpAddresses": [
                                            {
                                                "PrivateDnsName": "ip-xx.ap-south-1.compute.internal",
                                                "PrivateIpAddress": "xx.31.3.xxx",
                                                "Primary": true,
                                                "Association": {
                                                    "PublicIp": "xx.127.24.xxx",
                                                    "PublicDnsName": "ec2-xx.ap-south-1.compute.amazonaws.com",
                                                    "IpOwnerId": "xxxxx"
                                                }
                                            }
                                        ],
                                        "PrivateDnsName": "ip-xxx-31-3-xxx.ap-south-1.compute.internal",
                                        "Attachment": {
                                            "Status": "attached",
                                            "DeviceIndex": 0,
                                            "DeleteOnTermination": true,
                                            "AttachmentId": "xxx",
                                            "AttachTime": "20xx-xx-30Txx:16:xx.000Z"
                                        },
                                        "Groups": [
                                            {
                                                "GroupName": "xxxx",
                                                "GroupId": "sg-xxxxx"
                                            }
                                        ],
                                        "Ipv6Addresses": [],
                                        "OwnerId": "xxxx",
                                        "PrivateIpAddress": "xx.xx.xx.xxx",
                                        "SubnetId": "subnet-xx",
                                        "Association": {
                                            "PublicIp": "xx.xx.xx.xxx",
                                            "PublicDnsName": "ec2-xx.ap-south-1.compute.amazonaws.com",
                                            "IpOwnerId": "xxxx"
                                        }
                                    }
                                ],
                                "SourceDestCheck": true,
                                "Placement": {
                                    "Tenancy": "default",
                                    "GroupName": "",
                                    "AvailabilityZone": "xx"
                                },
                                "Hypervisor": "xxx",
                                "BlockDeviceMappings": [
                                    {
                                        "DeviceName": "/dev/xxx",
                                        "Ebs": {
                                            "Status": "attached",
                                            "DeleteOnTermination": true,
                                            "VolumeId": "vol-xxx",
                                            "AttachTime": "20xxx-xx-xxTxx:16:xx.000Z"
                                        }
                                    }
                                ],
                                "Architecture": "x86_64",
                                "RootDeviceType": "ebs",
                                "RootDeviceName": "/dev/xxx",
                                "VirtualizationType": "xxx",
                                "Tags": [
                                    {
                                        "Value": "xxxx centxx",
                                        "Key": "Name"
                                    }
                                ],
                                "AmiLaunchIndex": 0
                            }
                        ],
                        "ReservationId": "r-xxxx",
                        "Groups": [],
                        "OwnerId": "xxxxx"
                    }
                ]
            }
            

            以下 bash 脚本是 stop-ec2.sh in /home/centos/cron-scripts/,其灵感来自 this SO post

            (instance=$(aws ec2 describe-instances | jq '.Reservations[].Instances | select(.[].Tags[].Value | startswith("Appname Prod") ) |  select(.[].Tags[].Key == "Appname") |  {InstanceId: .[].InstanceId, PublicDnsName: .[].PublicDnsName, State: .[].State, LaunchTime: .[].LaunchTime, Tags: .[].Tags}  | [.]' | jq -r .[].InstanceId) && aws ec2 stop-instances --instance-ids ${instance} )
            

            使用 sh /home/centos/cron-scripts/stop-ec2.sh 运行文件并验证 EC2 实例是否已停止。调试运行 aws ec2 describe-instances | jq '.Reservations[].Instances | select(.[].Tags[].Value | startswith("Appname Prod") ) | select(.[].Tags[].Key == "Appname") | {InstanceId: .[].InstanceId, PublicDnsName: .[].PublicDnsName, State: .[].State, LaunchTime: .[].LaunchTime, Tags: .[].Tags} | [.]' | jq -r .[].InstanceId 并查看它返回已标记的正确实例 ID。

            那么在crontab -e可以添加下面一行

            30 14 * * * sh /home/centos/cron-scripts/stop-ec2.sh &gt;&gt; /tmp/stop

            这会将输出记录到/tmp/stop30 14 * * * 是您可以在 https://crontab.guru/ 中签入的 UTC cron 表达式。同样替换为aws ec2 start-instances 可以启动一个实例。

            【讨论】:

              【解决方案13】:

              我相信最初的问题有点令人困惑。这取决于意大利面需要什么: 1.launch/terminate(实例存储)- Auto Scaling 是正确的解决方案(Nakedible 的回答) 2.启动/停止 EBS 启动实例 - Auto Scaling 无济于事,我使用远程计划脚本(即 ec2 CLI)。

              【讨论】:

                【解决方案14】:

                您无法自动执行此操作,或者至少在没有脚本文件中的一些编程和 API 操作的情况下无法执行此操作。如果您想要一个可靠的解决方案来停止、重新启动和管理您的图像(大概是为了控制您的环境中的成本),那么您可能需要查看LabSlice。免责声明:我在这家公司工作。

                【讨论】: