| OLD | NEW |
| (Empty) |
| 1 .. _ec2_tut: | |
| 2 | |
| 3 ======================================= | |
| 4 An Introduction to boto's EC2 interface | |
| 5 ======================================= | |
| 6 | |
| 7 This tutorial focuses on the boto interface to the Elastic Compute Cloud | |
| 8 from Amazon Web Services. This tutorial assumes that you have already | |
| 9 downloaded and installed boto. | |
| 10 | |
| 11 Creating a Connection | |
| 12 --------------------- | |
| 13 The first step in accessing EC2 is to create a connection to the service. | |
| 14 There are two ways to do this in boto. The first is: | |
| 15 | |
| 16 >>> from boto.ec2.connection import EC2Connection | |
| 17 >>> conn = EC2Connection('<aws access key>', '<aws secret key>') | |
| 18 | |
| 19 At this point the variable conn will point to an EC2Connection object. In | |
| 20 this example, the AWS access key and AWS secret key are passed in to the | |
| 21 method explicitely. Alternatively, you can set the environment variables: | |
| 22 | |
| 23 AWS_ACCESS_KEY_ID - Your AWS Access Key ID | |
| 24 AWS_SECRET_ACCESS_KEY - Your AWS Secret Access Key | |
| 25 | |
| 26 and then call the constructor without any arguments, like this: | |
| 27 | |
| 28 >>> conn = EC2Connection() | |
| 29 | |
| 30 There is also a shortcut function in the boto package, called connect_ec2 | |
| 31 that may provide a slightly easier means of creating a connection: | |
| 32 | |
| 33 >>> import boto | |
| 34 >>> conn = boto.connect_ec2() | |
| 35 | |
| 36 In either case, conn will point to an EC2Connection object which we will | |
| 37 use throughout the remainder of this tutorial. | |
| 38 | |
| 39 A Note About Regions | |
| 40 -------------------- | |
| 41 The 2008-12-01 version of the EC2 API introduced the idea of Regions. | |
| 42 A Region is geographically distinct and is completely isolated from | |
| 43 other EC2 Regions. At the time of the launch of the 2008-12-01 API | |
| 44 there were two available regions, us-east-1 and eu-west-1. Each | |
| 45 Region has it's own service endpoint and therefore would require | |
| 46 it's own EC2Connection object in boto. | |
| 47 | |
| 48 The default behavior in boto, as shown above, is to connect you with | |
| 49 the us-east-1 region which is exactly the same as the behavior prior | |
| 50 to the introduction of Regions. | |
| 51 | |
| 52 However, if you would like to connect to a region other than us-east-1, | |
| 53 there are a couple of ways to accomplish that. The first way, is to | |
| 54 as EC2 to provide a list of currently supported regions. You can do | |
| 55 that using the regions function in the boto.ec2 module: | |
| 56 | |
| 57 >>> import boto.ec2 | |
| 58 >>> regions = boto.ec2.regions() | |
| 59 >>> regions | |
| 60 [RegionInfo:eu-west-1, RegionInfo:us-east-1] | |
| 61 >>> | |
| 62 | |
| 63 As you can see, a list of available regions is returned. Each region | |
| 64 is represented by a RegionInfo object. A RegionInfo object has two | |
| 65 attributes; a name and an endpoint. | |
| 66 | |
| 67 >>> eu = regions[0] | |
| 68 >>> eu.name | |
| 69 u'eu-west-1' | |
| 70 >>> eu.endpoint | |
| 71 u'eu-west-1.ec2.amazonaws.com' | |
| 72 >>> | |
| 73 | |
| 74 You can easily create a connection to a region by using the connect | |
| 75 method of the RegionInfo object: | |
| 76 | |
| 77 >>> conn_eu = eu.connect() | |
| 78 >>> conn_eu | |
| 79 <boto.ec2.connection.EC2Connection instance at 0xccaaa8> | |
| 80 >>> | |
| 81 | |
| 82 The variable conn_eu is now bound to an EC2Connection object connected | |
| 83 to the endpoint of the eu-west-1 region and all operations performed via | |
| 84 that connection and all objects created by that connection will be scoped | |
| 85 to the eu-west-1 region. You can always tell which region a connection | |
| 86 is associated with by accessing it's region attribute: | |
| 87 | |
| 88 >>> conn_eu.region | |
| 89 RegionInfo:eu-west-1 | |
| 90 >>> | |
| 91 | |
| 92 Supporting EC2 objects such as SecurityGroups, KeyPairs, Addresses, | |
| 93 Volumes, Images and SnapShots are local to a particular region. So | |
| 94 don't expect to find the security groups you created in the us-east-1 | |
| 95 region to be available in the eu-west-1 region. | |
| 96 | |
| 97 Some objects in boto, such as SecurityGroup, have a new method called | |
| 98 copy_to_region which will attempt to create a copy of the object in | |
| 99 another region. For example: | |
| 100 | |
| 101 >>> regions | |
| 102 [RegionInfo:eu-west-1, RegionInfo:us-east-1] | |
| 103 >>> conn_us = regions[1].connect() | |
| 104 >>> groups = conn_us.get_all_security_groups() | |
| 105 >>> groups | |
| 106 [SecurityGroup:alfresco, SecurityGroup:apache, SecurityGroup:vnc, | |
| 107 SecurityGroup:appserver2, SecurityGroup:FTP, SecurityGroup:webserver, | |
| 108 SecurityGroup:default, SecurityGroup:test-1228851996] | |
| 109 >>> us_group = groups[0] | |
| 110 >>> us_group | |
| 111 SecurityGroup:alfresco | |
| 112 >>> us_group.rules | |
| 113 [IPPermissions:tcp(22-22), IPPermissions:tcp(80-80), IPPermissions:tcp(1445-1445
)] | |
| 114 >>> eu_group = us_group.copy_to_region(eu) | |
| 115 >>> eu_group.rules | |
| 116 [IPPermissions:tcp(22-22), IPPermissions:tcp(80-80), IPPermissions:tcp(1445-1445
)] | |
| 117 | |
| 118 In the above example, we chose one of the security groups available | |
| 119 in the us-east-1 region (the group alfresco) and copied that security | |
| 120 group to the eu-west-1 region. All of the rules associated with the | |
| 121 original security group will be copied as well. | |
| 122 | |
| 123 If you would like your default region to be something other than | |
| 124 us-east-1, you can override that default in your boto config file | |
| 125 (either ~/.boto for personal settings or /etc/boto.cfg for system-wide | |
| 126 settings). For example: | |
| 127 | |
| 128 [Boto] | |
| 129 ec2_region_name = eu-west-1 | |
| 130 ec2_region_endpoint = eu-west-1.ec2.amazonaws.com | |
| 131 | |
| 132 The above lines added to either boto config file would set the default | |
| 133 region to be eu-west-1. | |
| 134 | |
| 135 Images & Instances | |
| 136 ------------------ | |
| 137 | |
| 138 An Image object represents an Amazon Machine Image (AMI) which is an | |
| 139 encrypted machine image stored in Amazon S3. It contains all of the | |
| 140 information necessary to boot instances of your software in EC2. | |
| 141 | |
| 142 To get a listing of all available Images: | |
| 143 | |
| 144 >>> images = conn.get_all_images() | |
| 145 >>> images | |
| 146 [Image:ami-20b65349, Image:ami-22b6534b, Image:ami-23b6534a, Image:ami-25b6534c,
Image:ami-26b6534f, Image:ami-2bb65342, Image:ami-78b15411, Image:ami-a4aa4fcd,
Image:ami-c3b550aa, Image:ami-e4b6538d, Image:ami-f1b05598] | |
| 147 >>> for image in images: | |
| 148 ... print image.location | |
| 149 ec2-public-images/fedora-core4-base.manifest.xml | |
| 150 ec2-public-images/fedora-core4-mysql.manifest.xml | |
| 151 ec2-public-images/fedora-core4-apache.manifest.xml | |
| 152 ec2-public-images/fedora-core4-apache-mysql.manifest.xml | |
| 153 ec2-public-images/developer-image.manifest.xml | |
| 154 ec2-public-images/getting-started.manifest.xml | |
| 155 marcins_cool_public_images/fedora-core-6.manifest.xml | |
| 156 khaz_fc6_win2003/image.manifest | |
| 157 aes-images/django.manifest | |
| 158 marcins_cool_public_images/ubuntu-6.10.manifest.xml | |
| 159 ckk_public_ec2_images/centos-base-4.4.manifest.xml | |
| 160 | |
| 161 The most useful thing you can do with an Image is to actually run it, so let's | |
| 162 run a new instance of the base Fedora image: | |
| 163 | |
| 164 >>> image = images[0] | |
| 165 >>> image.location | |
| 166 ec2-public-images/fedora-core4-base.manifest.xml | |
| 167 >>> reservation = image.run() | |
| 168 | |
| 169 This will begin the boot process for a new EC2 instance. The run method | |
| 170 returns a Reservation object which represents a collection of instances | |
| 171 that are all started at the same time. In this case, we only started one | |
| 172 but you can check the instances attribute of the Reservation object to see | |
| 173 all of the instances associated with this reservation: | |
| 174 | |
| 175 >>> reservation.instances | |
| 176 [Instance:i-6761850e] | |
| 177 >>> instance = reservation.instances[0] | |
| 178 >>> instance.state | |
| 179 u'pending' | |
| 180 >>> | |
| 181 | |
| 182 So, we have an instance booting up that is still in the pending state. We | |
| 183 can call the update method on the instance to get a refreshed view of it's | |
| 184 state: | |
| 185 | |
| 186 >>> instance.update() | |
| 187 >>> instance.state | |
| 188 u'pending' | |
| 189 >>> # wait a few minutes | |
| 190 >>> instance.update() | |
| 191 >>> instance.state | |
| 192 u'running' | |
| 193 | |
| 194 So, now our instance is running. The time it takes to boot a new instance | |
| 195 varies based on a number of different factors but usually it takes less than | |
| 196 five minutes. | |
| 197 | |
| 198 Now the instance is up and running you can find out its DNS name like this: | |
| 199 | |
| 200 >>> instance.dns_name | |
| 201 u'ec2-72-44-40-153.z-2.compute-1.amazonaws.com' | |
| 202 | |
| 203 This provides the public DNS name for your instance. Since the 2007--3-22 | |
| 204 release of the EC2 service, the default addressing scheme for instances | |
| 205 uses NAT-addresses which means your instance has both a public IP address and a | |
| 206 non-routable private IP address. You can access each of these addresses | |
| 207 like this: | |
| 208 | |
| 209 >>> instance.public_dns_name | |
| 210 u'ec2-72-44-40-153.z-2.compute-1.amazonaws.com' | |
| 211 >>> instance.private_dns_name | |
| 212 u'domU-12-31-35-00-42-33.z-2.compute-1.internal' | |
| 213 | |
| 214 Even though your instance has a public DNS name, you won't be able to | |
| 215 access it yet because you need to set up some security rules which are | |
| 216 described later in this tutorial. | |
| 217 | |
| 218 Since you are now being charged for that instance we just created, you will | |
| 219 probably want to know how to terminate the instance, as well. The simplest | |
| 220 way is to use the stop method of the Instance object: | |
| 221 | |
| 222 >>> instance.stop() | |
| 223 >>> instance.update() | |
| 224 >>> instance.state | |
| 225 u'shutting-down' | |
| 226 >>> # wait a minute | |
| 227 >>> instance.update() | |
| 228 >>> instance.state | |
| 229 u'terminated' | |
| 230 >>> | |
| 231 | |
| 232 When we created our new instance, we didn't pass any args to the run method | |
| 233 so we got all of the default values. The full set of possible parameters | |
| 234 to the run method are: | |
| 235 | |
| 236 min_count - The minimum number of instances to launch. | |
| 237 max_count - The maximum number of instances to launch. | |
| 238 keypair - Keypair to launch instances with (either a KeyPair object or a string
with the name of the desired keypair. | |
| 239 security_groups - A list of security groups to associate with the instance. Thi
s can either be a list of SecurityGroup objects or a list of strings with the na
mes of the desired security groups. | |
| 240 user_data - Data to be made available to the launched instances. This should be
base64 encoded according to the EC2 documentation. | |
| 241 | |
| 242 So, if I wanted to create two instances of the base image and launch them | |
| 243 with my keypair, called gsg-keypair, I would to this: | |
| 244 | |
| 245 >>> reservation.image.run(2,2,'gsg-keypair') | |
| 246 >>> reservation.instances | |
| 247 [Instance:i-5f618536, Instance:i-5e618537] | |
| 248 >>> for i in reservation.instances: | |
| 249 ... print i.status | |
| 250 u'pending' | |
| 251 u'pending' | |
| 252 >>> | |
| 253 | |
| 254 Later, when you are finished with the instances you can either stop each | |
| 255 individually or you can call the stop_all method on the Reservation object: | |
| 256 | |
| 257 >>> reservation.stop_all() | |
| 258 >>> | |
| 259 | |
| 260 If you just want to get a list of all of your running instances, use | |
| 261 the get_all_instances method of the connection object. Note that the | |
| 262 list returned is actually a list of Reservation objects (which contain | |
| 263 the Instances) and that the list may include recently terminated instances | |
| 264 for a small period of time subsequent to their termination. | |
| 265 | |
| 266 >>> instances = conn.get_all_instances() | |
| 267 >>> instances | |
| 268 [Reservation:r-a76085ce, Reservation:r-a66085cf, Reservation:r-8c6085e5] | |
| 269 >>> r = instances[0] | |
| 270 >>> for inst in r.instances: | |
| 271 ... print inst.state | |
| 272 u'terminated' | |
| 273 >>> | |
| 274 | |
| 275 A recent addition to the EC2 api's is to allow other EC2 users to launch | |
| 276 your images. There are a couple of ways of accessing this capability in | |
| 277 boto but I'll show you the simplest way here. First of all, you need to | |
| 278 know the Amazon ID for the user in question. The Amazon Id is a twelve | |
| 279 digit number that appears on your Account Activity page at AWS. It looks | |
| 280 like this: | |
| 281 | |
| 282 1234-5678-9012 | |
| 283 | |
| 284 To use this number in API calls, you need to remove the dashes so in our | |
| 285 example the user ID would be 12345678912. To allow the user associated | |
| 286 with this ID to launch one of your images, let's assume that the variable | |
| 287 image represents the Image you want to share. So: | |
| 288 | |
| 289 >>> image.get_launch_permissions() | |
| 290 {} | |
| 291 >>> | |
| 292 | |
| 293 The get_launch_permissions method returns a dictionary object two possible | |
| 294 entries; user_ids or groups. In our case we haven't yet given anyone | |
| 295 permission to launch our image so the dictionary is empty. To add our | |
| 296 EC2 user: | |
| 297 | |
| 298 >>> image.set_launch_permissions(['123456789012']) | |
| 299 True | |
| 300 >>> image.get_launch_permissions() | |
| 301 {'user_ids': [u'123456789012']} | |
| 302 >>> | |
| 303 | |
| 304 We have now added the desired user to the launch permissions for the Image | |
| 305 so that user will now be able to access and launch our Image. You can add | |
| 306 multiple users at one time by adding them all to the list you pass in as | |
| 307 a parameter to the method. To revoke the user's launch permissions: | |
| 308 | |
| 309 >>> image.remove_launch_permissions(['123456789012']) | |
| 310 True | |
| 311 >>> image.get_launch_permissions() | |
| 312 {} | |
| 313 >>> | |
| 314 | |
| 315 It is possible to pass a list of group names to the set_launch_permissions | |
| 316 method, as well. The only group available at the moment is the group "all" | |
| 317 which would allow any valid EC2 user to launch your image. | |
| 318 | |
| 319 Finally, you can completely reset the launch permissions for an Image with: | |
| 320 | |
| 321 >>> image.reset_launch_permissions() | |
| 322 True | |
| 323 >>> | |
| 324 | |
| 325 This will remove all users and groups from the launch permission list and | |
| 326 makes the Image private, again. | |
| 327 | |
| 328 Security Groups | |
| 329 ---------------- | |
| 330 | |
| 331 Amazon defines a security group as: | |
| 332 | |
| 333 "A security group is a named collection of access rules. These access rules | |
| 334 specify which ingress, i.e. incoming, network traffic should be delivered | |
| 335 to your instance." | |
| 336 | |
| 337 To get a listing of all currently defined security groups: | |
| 338 | |
| 339 >>> rs = conn.get_all_security_groups() | |
| 340 >>> print rs | |
| 341 [SecurityGroup:appserver, SecurityGroup:default, SecurityGroup:vnc, SecurityGrou
p:webserver] | |
| 342 >>> | |
| 343 | |
| 344 Each security group can have an arbitrary number of rules which represent | |
| 345 different network ports which are being enabled. To find the rules for a | |
| 346 particular security group, use the rules attribute: | |
| 347 | |
| 348 >>> sg = rs[1] | |
| 349 >>> sg.name | |
| 350 u'default' | |
| 351 >>> sg.rules | |
| 352 [IPPermissions:tcp(0-65535), | |
| 353 IPPermissions:udp(0-65535), | |
| 354 IPPermissions:icmp(-1--1), | |
| 355 IPPermissions:tcp(22-22), | |
| 356 IPPermissions:tcp(80-80)] | |
| 357 >>> | |
| 358 | |
| 359 In addition to listing the available security groups you can also create | |
| 360 a new security group. I'll follow through the "Three Tier Web Service" | |
| 361 example included in the EC2 Developer's Guide for an example of how to | |
| 362 create security groups and add rules to them. | |
| 363 | |
| 364 First, let's create a group for our Apache web servers that allows HTTP | |
| 365 access to the world: | |
| 366 | |
| 367 >>> web = conn.create_security_group('apache', 'Our Apache Group') | |
| 368 >>> web | |
| 369 SecurityGroup:apache | |
| 370 >>> web.authorize('tcp', 80, 80, '0.0.0.0/0') | |
| 371 True | |
| 372 >>> | |
| 373 | |
| 374 The first argument is the ip protocol which can be one of; tcp, udp or icmp. | |
| 375 The second argument is the FromPort or the beginning port in the range, the | |
| 376 third argument is the ToPort or the ending port in the range and the last | |
| 377 argument is the CIDR IP range to authorize access to. | |
| 378 | |
| 379 Next we create another group for the app servers: | |
| 380 | |
| 381 >>> app = conn.create_security_group('appserver', 'The application tier') | |
| 382 >>> | |
| 383 | |
| 384 We then want to grant access between the web server group and the app | |
| 385 server group. So, rather than specifying an IP address as we did in the | |
| 386 last example, this time we will specify another SecurityGroup object. | |
| 387 | |
| 388 >>> app.authorize(src_group=web) | |
| 389 True | |
| 390 >>> | |
| 391 | |
| 392 Now, to verify that the web group now has access to the app servers, we want to | |
| 393 temporarily allow SSH access to the web servers from our computer. Let's | |
| 394 say that our IP address is 192.168.1.130 as it is in the EC2 Developer | |
| 395 Guide. To enable that access: | |
| 396 | |
| 397 >>> web.authorize(ip_protocol='tcp', from_port=22, to_port=22, cidr_ip='192.168.
1.130/32') | |
| 398 True | |
| 399 >>> | |
| 400 | |
| 401 Now that this access is authorized, we could ssh into an instance running in | |
| 402 the web group and then try to telnet to specific ports on servers in the | |
| 403 appserver group, as shown in the EC2 Developer's Guide. When this testing is | |
| 404 complete, we would want to revoke SSH access to the web server group, like this: | |
| 405 | |
| 406 >>> web.rules | |
| 407 [IPPermissions:tcp(80-80), | |
| 408 IPPermissions:tcp(22-22)] | |
| 409 >>> web.revoke('tcp', 22, 22, cidr_ip='192.168.1.130/32') | |
| 410 True | |
| 411 >>> web.rules | |
| 412 [IPPermissions:tcp(80-80)] | |
| 413 >>> | |
| 414 | |
| 415 | |
| 416 | |
| 417 | |
| 418 | |
| 419 | |
| 420 | |
| OLD | NEW |