DevOps is the new rage among system administrators, applying agile software development techniques to infrastructure configuration management. In the center of the DevOps movement is the open-source Chef tool, implemented in Ruby atop CouchDB. Unsatisfied with the performance of the open-source and/or hosted Chef server and needing better integration with our Python web application, we set out to build a new implementation in Python atop MongoDB. This talk will give you an overview of Chef, reasons for doing a new implementation, and lots of code examples of how we made it all work together to get a chef server that screams.
This talk is updated with the latest version of MongoPyChef, ported to run on Pyramid and open sourced at https://github.com/rick446/MongoPyChef
2. Infrastructure as Code
Resources & Providers
Cookbooks, Recipes, Clients, and Nodes
3. CouchDB Solr
SolrIndexer
Chef Server
RabbitMQ
RabbitMQ
RabbitMQ
(API)
HTTP REST API
Chef Server
knife chef-client
(Web UI)
4. Build, register, and authenticate the node
Synchronize cookbooks
Build resource collection (run the recipes in order)
Configure node (“converge”)
Run notification handlers
5. CouchDB Solr
Ruby
SolrIndexer
Chef Server
RabbitMQ
RabbitMQ
RabbitMQ
(API)
Ruby
Ruby
HTTP REST API
Chef Server
knife chef-client
(Web UI)
6. Chef assumes a bootstrapped node exists
Chef doesn’t keep release notes
Code and infrastructure are versioned
differently
Solution: Web app to manage deployments &
generate release notes
7. MonQ
MongoDB
SolrIndexer
Python
Chef Server
(API + web) Solr
Ruby
HTTP REST API
knife chef-client
8. Reduce # of processes & technologies
Don’t know Ruby well
Keep private keys out of the system
Integrate with existing authentication
Performance
12. classRole(ModelBase): Models know
where they live
def__name__(self):
returnself.name
Models can be
def__json__(self): turned into dict (to
returndict( be JSONified)
chef_type='role',
json_class='Chef::Role',
…
default_attributes=loads(self.default_attributes),
Models can be
…)
updated from dict
defupdate(self, d):
self.name = d['name']
…
self.default_attributes = dumps(d['default_attributes'])
self.override_attributes = dumps(d['override_attributes'])
…
16. @view_config(context=Roles, renderer='json', Resources can be
request_method='GET’, permission='read') located
deflist_roles(context, request):
returndict(
(n.name, request.resource_url(n))
fornincontext.find())
Convert and
@view_config(context=Roles, renderer='json', validate JSON
request_method='POST’,permission='create') input
defcreate_role(context, request):
n = context.new_object()
value = V.RoleSchema().to_python(
request.json, None)
n.update(value)
…
17. Don’t trust the docs
Don’t trust the docs
▪ Don’t trust the docs
Use fat models
Framework support for REST & JSON
You’re gonna have to learn some Ruby anyway
JSON != BSON
18. Port from homegrown framework to Pyramid
Better test coverage
Search support (SOLR / ElasticSearch)
More testing with real-world deployments
Finalize integration with deployment
manager
Keep your infrastructure definitions in source controlResources = users, packages, services,files, etc. Providers = How you build a resource on a particular platformCookbook = Collection of resources, providers, templates, libraries, and filesRecipe = Ruby script defining the configuration of some resourcesClient = anyone talking to the chef serverNode = a machine instance managed by chef
“url” field Opscode stores everything on S3, I store it all in GridFS
Note the ‘loads’ there IS valid JSON that is not a valid BSON doc for a collection. Dotted key names, for example.