cncf / demo

Demo of CNCF technologies
https://cncf.io
Apache License 2.0
77 stars 39 forks source link

Boto vs Terraform #97

Open namliz opened 8 years ago

namliz commented 8 years ago

Boto3 is the Amazon Web Services (AWS) SDK for Python.

Terraform is a higher level abstraction that provides additional features like multi-cloud support, complex changesets, and so on.

Some people have embraced Terraform wholeheartedly already, however there are cons.

While multi-cloud support is an excellent feature for the people who require it - it is not currently in scope for this demo. This might change, at which point the cost-benefit of using Terraform will have to be reevaluated.

The multi-cloud support is a leaky abstraction, going up the ladder of abstraction in this case means learning yet another configuration syntax and introducing yet another dependency on yet another tool.

The union set of users who can use Terraform and not be familiar with the underlying AWS API's that Boto exposes is approximately zero. Furthermore, its worthwhile to consider that virtually all users of AWS are allowed to use the official SDK (and probably already have it configured) but not all are allowed (or capable, at least in a timely manner) of using Terraform.

There's something to be said for avoiding the situation of: "to try out our demo, first install and understand a separate project".

Finally, Terraform predates Boto3, which is significantly improved and simplified, and sprinkles in some of the higher levelness.

As a result, for our limited use case, one can get most of the pros of terraform sans the cons.


Execution Plans in a few lines of Python

Lets define a sequence of named tuples.

The tuple consists of an object, method name, and arguments, respectively.

bootstrap = [(EC2, 'create_key_pair', {'KeyName': keyname}),
             (IAM, 'create_instance_profile', {'InstanceProfileName': instanceprofile}),
             (IPE, 'wait', {'InstanceProfileName': instanceprofile}),
             (IAM, 'create_role', {'RoleName': rolename, 'AssumeRolePolicyDocument': json.dumps(TrustedPolicy)}),
             (IAM, 'add_role_to_instance_profile', {'RoleName': rolename, 'InstanceProfileName': instanceprofile}),
             (IAM, 'attach_role_policy', {'RoleName': rolename, 'PolicyArn': policyarn})]

There's absolutely no mystery here.

import boto3
EC2 = boto3.resource('ec2')
IAM = boto3.resource('iam')

You can see the method names are lifted directly from the Boto3 API Documentation along with the arguments which are typically json dictionaries.

One can simply copy-paste from the docs the json blobs and reference back and forth to understand exactly what is going on.

If we print out just the method column we get something that scans beautifully:

Which just reads as plain English and such a laundry list can be found in any number of tutorials and blog posts that enumerate the steps for creating an auto scaling group.

There are no other complex steps, the only thing modified after these things are provisioned is the scaling factor of the group.

There are no complex dependencies or teardowns as we create a dedicated vpc and blow it away completely per cluster each time -- we never edit an existing deployment as you would in a production environment and thus the raison d'etre of terraform and other such tools - complex changesets - is not relevant.

In short, this seems like a good sweet spot to ramp users unto a kubernetes cluster deployment process without unnecessary indirection steps.

It's entirely a Terraform recipe will be added future and primarily used, but the vanilla way should definitely come first and be supported.

namliz commented 8 years ago

How Execution Plans are Unrolled

Once the bootstrap sequence above is built up you pass it off to an execute function:

execute(bootstrap)

def execute(actions):

  for action in actions:
    client, method, args = action
    result = getattr(client, method)(**args)

As Willy Wonka once said, there's nothing to it. These three lines go far.

However, we're basically throwing away the result away (after printing it to the terminal), turns out sometimes it needs to be referenced down the line.

The above was fine to create everything necessary for an autoscaling group into an existing vpc. However, recycling the same vpc for kubernetes clusters is a source of possible bugs*, and so:

**Request Syntax**
::

  subnet = ec2.create_subnet(
      DryRun=True|False,
      VpcId='string',
      CidrBlock='string',
      AvailabilityZone='string'
  )

That Vpcid needs to come from somewhere, and unlike all the parameters so far, it doesn't come from the user or the script defaults, but at creation time from the AWS API.

Boto3 actually has a somewhat nicer way:

vpc = ec2resource.create_vpc(CidrBlock=cidr)
subnet = vpc.create_subnet(CidrBlock=subcidr) 

So no need to reference the Vpc id, can just pass this newly created object down sequence.

Summary

And finally, execute returns a big blob of pairs of actions and results that's just nice to keep around for debugging purposes.

philips commented 7 years ago

You may want to checkout the Tectonic Installer which deploys pure upstream Kubernetes self-hosted (although with a todo) across AWS/Azure/OpenStack/etc.