Schedule EC2 Instance Start and Stop
If you use EC2 instances for tasks that only need to be performed during work hours, it can be challenging and annoying to manually start up and shutdown instances when you need them. There are services that can handle this for you (Skeddly, Ylastic, etc) but they cost money and are overkill for small companies that just need to manage a few (or a few dozen) instances.
We recently had a client that had a few dozen instances that only needed to be operating during weekday work hours. We needed these to be pre-started before they were needed and shut down on an easily controllable schedule.
We stumbled across Shing Chen’s EC2 Python script on github and it was a great start, supporting stopping and starting of instances based on auto:start and auto:stop tags in cron format.
However, it had a few shortcomings for our use, so we rewired it!
We overhauled it to support timestamped logging to a file, a dry run option, configurable run windows, avoid re-stopping instances that someone started, and fixing Elastic Load Balancers by re-registering instances that booted up. We also fixed a few bugs and cleaned up the code, then documented it and pushed it to github.
You can grab out version here: https://github.com/internetstaff/ec2
EC2 Operator
Automatically starts and stops Amazon EC2 instances based on auto:start and auto:stop tags in cron format. Designed to run simply without excessive configuration, all options can be configured via command line or instance tags.
It will also check elastic load balancers when starting instances and de-register then re-register the instance against any load balancers to make sure that the instances comes online in the elastic load balancer in a reasonable time frame.
Example
Start an instance at 15:00 UTC Monday through Friday and stop it at 04:00 UTC every day.
auto:start 0 15 * * 1-5
auto:stop 0 4 * * *
Usage
usage: ec2_operator.py [-h] [-l {debug,info,warning,error,critical}] [-f LOGFILE] [-m LOGMAX] [-b LOGBACKUPS] [-s STARTWIN] [-t STOPWIN] [-n] Automatically stop and start ec2 instances based on tags. optional arguments: -h, --help show this help message and exit -l {debug,info,warning,error,critical}, --loglevel {debug,info,warning,error,critical} Set logging level (default: None) -f LOGFILE, --logfile LOGFILE Enable logging to filename (default: None) -m LOGMAX, --logmax LOGMAX Maximum log size before rotation in megabytes (default: 1) -b LOGBACKUPS, --logbackups LOGBACKUPS Maximum number of rotated logs to keep (default: 10) -s STARTWIN, --startwin STARTWIN How many minutes early an instance may be started (default: 10) -t STOPWIN, --stopwin STOPWIN How many minutes after an instance will be stopped (default: 60) -n, --dry-run trial run with no instance stops or starts (default: False)
Example
ec2_operator.py -l debug -f /var/log/ec2_operator.log -m 5 -b 10 -s 5 -t 30
Runs with debug level log output to /var/log/ec2_operator. Log files are rotated at 5 megabytes and up to 10 log files are kept. Instances will be started if they are scheduled to start within 5 minutes of a run. They will be stopped if the they are found running within 60 minutes of a run.
An instance will not be restarted if its launch time is after the beginning of the shutdown window. For example, if the shutdown window is 01:00 and someone restarted the instance at 01:05, a run at 01:10 would not start that instance back up.
The stop window is 60 minutes by default to give ample room to make sure the instance is shut down.
The start window is 10 minutes in order to give several chances to start the instance if run on a */5 schedule as well as to give the instance plenty of time to start up before it is needed.
Scheduling
This is typically executed via cron. The interval needs to make sense according to the start and stop windows used. Running every 5 minutes with the default windows is the normal use case.
*/5 * * * * /usr/local/bin/ec2_operator.py --loglevel info --logfile /var/log/ec2-operator/ec2-operator.log
Permissions
Requires either an instance role or AWS credentials configured with the following permissions:
{ "Statement":[ { "Action":[ "ec2:DescribeInstances", "ec2:StartInstances", "ec2:StopInstances", "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", "elasticloadbalancing:DescribeLoadBalancerAttributes", "elasticloadbalancing:DescribeLoadBalancers", "elasticloadbalancing:RegisterInstancesWithLoadBalancer" ], "Effect":"Allow", "Resource":"*" } ] }
The improvements we made were graciously donated by one of our clients: TASER‘s Evidence.com. Consider joining them and working on a great product (police body cameras and evidence management) that makes a difference in the world at a cool company with a start-up atmosphere.
Geoffrey Rance
March 31, 2016 @ 10:15 am
I set this up and I’m seeing this in my log file:
2016-03-31 11:11:37,317 DEBUG region: us-east-1 name: id: launch: 2016-03-30T09:30:03.000Z state: running start_sched: 0 5 * * * stop_sched: 0 22 * * *
2016-03-31 11:11:37,317 DEBUG window_start <= cron_time <= now = 2016-03-31 15:11:36.674422+00:00 < 2016-03-30 22:00:00+00:00 < 2016-03-31 16:11:36.674422+00:00
It looks like it wants to shutdown in the past and my instances aren't actually shutting down. Any ideas? (I redacted my instance name and ID)
Internet Staff
March 31, 2016 @ 11:27 am
It’s hard to say with certainty, but, from the above, I suspect you’re seeing time zone differences. It will not shut down instances except during the “stop window” which defaults to 60 minutes after the stop time.
It also won’t stop an instance that has been restarted since the stop window began, so that people can restart an instance and use it.