<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0"><channel><title>Surfing in Kansas posts category feed</title><link>http://ericholscher.com/blog/categories/planet-django/</link><description>Posts recently categorized as Planet Django</description><language>en-us</language><lastBuildDate>Sat, 26 May 2012 20:24:55 -0000</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/EricPlanetDjango" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="ericplanetdjango" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>Why Read the Docs matters </title><link>http://ericholscher.com/blog/2012/jan/22/why-read-docs-matters/</link><description>


&lt;p&gt;Documenting projects is hard, hosting them shouldn't be. &lt;a href="http://readthedocs.org"&gt;Read the Docs&lt;/a&gt; was created to make hosting documentation simple. I think that we have solved this problem well, but now we need to start thinking about the larger picture.&lt;/p&gt;
&lt;p&gt;Along with hosting, Read the Docs was created with 2 other main goals. One was to encourage people to write documentation, by removing the barrier of entry of hosting. The other was to create a central platform for people to find documentation. Having a shared platform for all documentation allows for innovation at the platform level, allowing work to be done once and benefit everyone. Having run the site for over a year now, I think there is a third thing that we should be striving for. That is to make the quality of documentation better.&lt;/p&gt;
&lt;p&gt;I think that &lt;strong&gt;we can help a documentation culture flourish&lt;/strong&gt; within the open source world. &lt;a href="https://docs.djangoproject.com/en/1.3/"&gt;Django&lt;/a&gt; is a shining example of what a project with great documentation can do, and it has a community that values docs more than the norm. I think we can help &lt;strong&gt;spread this culture throughout the Python world, and beyond&lt;/strong&gt;. This has already started, and I want to think about how something like RTD can help.&lt;/p&gt;
&lt;h2&gt;What we can do to help&lt;/h2&gt;
&lt;p&gt;I think that having a &lt;strong&gt;guide for writing useful documentation&lt;/strong&gt; would be a great step towards helping people along the path of documentation enlightenment. Jacob Kaplan-Moss has started down this road with his &lt;a href="http://jacobian.org/writing/great-documentation/"&gt;blog series&lt;/a&gt; and Pycon 2011 &lt;a href="http://blip.tv/file/4881071"&gt;talk&lt;/a&gt; on this subject. I think that we could start by collecting these into a section of the site.&lt;/p&gt;
&lt;p&gt;We could build on top of that great start with simple guides for how to get started with Sphinx, best practices for documentation, and providing a general place to learn more about how to write good documentation. Since we host a lot of documentation, we could point to live examples of techniques, and provide helpers for people to enable the techniques.&lt;/p&gt;
&lt;p&gt;I have started a &lt;a href="http://restructuredtext-philosophy.readthedocs.org/en/latest/index.html"&gt;reStructedText Philosophy&lt;/a&gt; document that is meant to help people understand the ideas behind how reST works, so that it isn't as mystifying. This &lt;a href="http://thomas-cokelaer.info/tutorials/sphinx/rest_syntax.html"&gt;reST cheatsteet&lt;/a&gt; also appears to have similar goals. These are a very basic start, and I think some more along these lines would really help a lot of people get over the barrier to starting and continuing to write good documentation.&lt;/p&gt;
&lt;p&gt;I think that we could also help &lt;strong&gt;create contributors&lt;/strong&gt; to projects, if we
could find an easy way to provide patches to documentation. If you could go to
the project documentation, and fix small typos, or help add a paragraph in the
tutorial, it would lower the bar to helping.&lt;/p&gt;
&lt;p&gt;However, it isn't a wiki. These changes would be represented to the project
author as pull requests in their VCS, and they would still be responsible for
tending the garden. This gets rid of the "Just Edit The Wiki" solution of
documentation, and also helps new contributors get started in an easier fashion.&lt;/p&gt;
&lt;p&gt;The Plone community has built a &lt;a href="http://opensourcehacker.com/2012/01/08/readthedocs-org-github-edit-backlink-and-short-history-of-plone-documentation/"&gt;proof of concept, linking to Github's edit pages for the current document&lt;/a&gt;. I think we can integrate this at the platform level, and make it available to everyone.&lt;/p&gt;
&lt;h2&gt;Want to help?&lt;/h2&gt;
&lt;p&gt;Read the Docs is &lt;a href="https://github.com/rtfd/readthedocs.org"&gt;open source&lt;/a&gt;. You can help by writing docs for the site, writing code for the site, or just writing documentation in general. People can also help just by using the site, and reporting bugs. Telling us how to make the site better helps everyone in the long run. Come join us on Freenode in the #readthedocs channel as well.&lt;/p&gt;
&lt;p&gt;Another area that we're hurting is in the design front. We have been adding features over time, and the design of the site is getting a bit strained. Having someone with a good sense of design help re-think and re-architect some of the features and ideas that we've been working on I think would help a lot.&lt;/p&gt;
&lt;p&gt;A lot of the RTD contributors will be at Pycon 2012, where we will be having a sprint on the site. If you want to get started contributing, that is a great place to come and get started.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2012/jan/22/why-read-docs-matters/</guid></item><item><title>Read the Docs Update </title><link>http://ericholscher.com/blog/2011/apr/11/read-docs-update/</link><description>


&lt;p&gt;It's been a while since I last talked about &lt;a href="http://readthedocs.org"&gt;Read the Docs&lt;/a&gt;, and there has been a lot of activity. This is an update on the latest and greatest new features.&lt;/p&gt;
&lt;h2&gt;PSF Funding&lt;/h2&gt;
&lt;p&gt;The biggest news that has happened is that we have been given a grant from the Python Software Foundation to help host the site. Thanks PSF! They have &lt;a href="http://pyfound.blogspot.com/2011/03/psf-funds-readthedocsorg.html"&gt;blogged about it&lt;/a&gt;, and I am grateful that they have given us support. With the funds they have offered, we have been able to make Read the Docs a lot faster, and more robust. I will outline some of the changes below.&lt;/p&gt;
&lt;p&gt;This also means we won't be going away any time soon!&lt;/p&gt;
&lt;h2&gt;New Theme&lt;/h2&gt;
&lt;p&gt;We have a fancy &lt;a href="http://read-the-docs.readthedocs.org/en/latest/getting_started.html"&gt;new theme&lt;/a&gt; for documentation on Read the Docs! If you have the 'default' theme for your project, it will show up on the build of your docs on the site. I think it is pretty great, thanks to the designers who spent their time making it awesome. A really great feature is that the &lt;strong&gt;new theme is mobile ready&lt;/strong&gt;. Go ahead and view a project using it on your phone, or make your browser smaller and you will see the fanciness. Having a custom theme will give us a base to build lots of other neat features on top of.&lt;/p&gt;
&lt;h2&gt;Better architecture&lt;/h2&gt;
&lt;p&gt;We had some connectivity trouble in between our servers a while back, and this prompted me to make the site respond better to these conditions. Every time you view documentation on a subdomain of readthedocs.org, your request will never hit Django. So all of these requests will work without a database. We have also added a second application server with a load balance in front, which means that one of the app servers could go away and your documentation would still get served.&lt;/p&gt;
&lt;p&gt;That leaves our load balancer as the main single point of failure at the moment. We're using Varnish for the load balancer, and we've implemented strong caching of data. Varnish will cache your docs for up to a week, and it will be actively purged when you rebuild your docs. This means that your docs will usually be served out of memory, and without dependence on any other server but that one. We have plans to elininate Varnish as a single POF, and then it would only be our hosting provider that would be a single point of failure (famous last words).&lt;/p&gt;
&lt;h2&gt;Intersphinx support&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://sphinx.readthedocs.org/en/latest/ext/intersphinx.html#sphinx.ext.intersphinx"&gt;Intersphinx&lt;/a&gt; is an awesome feature of Sphinx that allows you to reference remote sphinx documentation easily. &lt;a href="http://read-the-docs.readthedocs.org/en/latest/features.html#intersphinx-support"&gt;RTD now supports it&lt;/a&gt; for every project that we host.&lt;/p&gt;
&lt;h2&gt;Improved rtfd.org&lt;/h2&gt;
&lt;p&gt;I've always had big ideas for rtfd.org, since it can act as a short-url for things. &lt;em&gt;projectname&lt;/em&gt;.rtfd.org has always redirects to the projects docs, but now we have something a lot better. Inspired by Jacob Kaplan-Moss and his work on django.me, we now support human-edited deep-linking within documentation hosted on RTD.&lt;/p&gt;
&lt;p&gt;Taking another page from Jacob's book, we seeded the index of our projects with their Intersphinx data, so a lot of references will automatically work. This works best with API reference docs, but anything people have put links to in their documentation should have been picked up. A couple of examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://pip.rtfd.org/git"&gt;http://pip.rtfd.org/git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://celery.rtfd.org/Task"&gt;http://celery.rtfd.org/Task&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sqlalchemy.rtfd.org/relationship"&gt;http://sqlalchemy.rtfd.org/relationship&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you go to a non-existent link on rtfd.org, you will be prompted to enter a suggested URL. This will help build the data, and make it more useful for everyone.&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://github.com/ericholscher/rtd"&gt;rtd&lt;/a&gt; command line utility&lt;/h2&gt;
&lt;p&gt;RTD has had an &lt;a href="http://read-the-docs.readthedocs.org/en/latest/api.html"&gt;API&lt;/a&gt; for a while now, and with the addition of the support for rtfd.org, I thought it would be neat to make it easier to access docs from the command line. With a simple &lt;code&gt;pip install rtd&lt;/code&gt;, you will get an rtd utility that will open docs on RTD. It supports 2 arguments, the first being a project name, and the second being a slug to append for the rtfd.org functionality. So like the example above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rtd&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt;
&lt;span class="n"&gt;Pip&lt;/span&gt; &lt;span class="n"&gt;Installs&lt;/span&gt; &lt;span class="n"&gt;Packages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; 
&lt;span class="n"&gt;Opening&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rtfd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rtd&lt;/span&gt; &lt;span class="n"&gt;celery&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;
&lt;span class="n"&gt;Distributed&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;
&lt;span class="n"&gt;Opening&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;http:&lt;/span&gt;&lt;span class="sr"&gt;//c&lt;/span&gt;&lt;span class="n"&gt;elery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rtfd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It hits the RTD API to see if the project is on the site, and only opens your browser if it doesn't exist. I hope that in the future we'll make it easy to upload a project from the shell, and more.&lt;/p&gt;
&lt;h2&gt;More docs&lt;/h2&gt;
&lt;p&gt;Since we are a documentation site, we've always had documentation. I've been adding more as time has gone on, and most of the features I'll be talking about today are &lt;a href="http://read-the-docs.readthedocs.org/en/latest/features.html"&gt;already documented&lt;/a&gt;. I also broke the documentation up into sections for users of the site, and developers on the codebase, so it should be easier to find for everyone to find what they are looking for.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I think that RTD can be doing a lot more to help out the community with regards to documentation. I'll write another post about that soon. But if you are interested in helping out with the effort, all of the code is &lt;a href="https://github.com/rtfd/readthedocs.org"&gt;open source&lt;/a&gt; and we love people to contribute. Feel free to jump in #readthedocs on Freenode as well, if you have any questions or thoughts.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2011/apr/11/read-docs-update/</guid></item><item><title>Read the Docs Updates</title><link>http://ericholscher.com/blog/2011/jan/11/read-docs-updates/</link><description>


&lt;p&gt;Documentation writing will always be hard work. It's a much different mind-set than programming, and people that write good code might not necessarily write good docs. However, this is a known issue, and something that can't really be solved.&lt;/p&gt;
&lt;p&gt;What you can do is make it easier to write documentation. Every step along the way that you can give yourself an excuse to not write documentation is another undocumented open source project.&lt;/p&gt;
&lt;p&gt;Luke Plant has a &lt;a href="http://lukeplant.me.uk/blog/posts/docs-or-it-doesnt-exist/"&gt;great post&lt;/a&gt; up about how important documentation is, and I completely agree. I imagine a lot of the people using Django are using it because of the documentation. I think as members of the Django community, we need to build a culture of documentation within the greater Python world. Python does tend to have better documentation than a lot of languages, but it's still not nearly what it could be.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://readthedocs.org"&gt;Read the Docs&lt;/a&gt; exists to make it easier to host your Sphinx documentation. Over the weekend, &lt;a href="http://bobbygrace.info/"&gt;Bobby&lt;/a&gt;, &lt;a href="https://github.com/ojii"&gt;Jonas&lt;/a&gt;, and I added a bunch of new features to the site. I think it's getting to the point where there isn't an easier or better way to host the documentation for your Django project, and we're only going to keep improving it!&lt;/p&gt;
&lt;p&gt;A different &lt;a href="http://www.automation-excellence.com/team/eric-pierce"&gt;Eric&lt;/a&gt; added a really nice &lt;a href="http://readthedocs.org/docs/read-the-docs/latest/getting_started.html"&gt;Getting Started&lt;/a&gt; guide for RTD, that shows how easy it is to get your projects hosted with us.&lt;/p&gt;
&lt;p&gt;Anyway, on to the new features that we added.&lt;/p&gt;
&lt;h2&gt;New Features&lt;/h2&gt;
&lt;h2&gt;Versions&lt;/h2&gt;
&lt;p&gt;Versions of projects are easily one of the biggest requested features on the site. For a long time we just supported building the latest versions of your documentation. Now we support versions of your documentation that are tagged in your VCS (hg/git only). &lt;/p&gt;
&lt;p&gt;A lot of larger projects need versioning because they support one or two versions, as well as developing in the trunk. Django was the main project we were thinking of, but some other projects have put this to good use. A couple of examples are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://readthedocs.org/docs/django/1.2.4/"&gt;Django's latest stable build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://readthedocs.org/docs/fabric/0.9.3/"&gt;Fabric 0.9.3&lt;/a&gt;, &lt;/li&gt;
&lt;li&gt;&lt;a href="http://django-admin-tools.readthedocs.org/"&gt;django-admin-tools's awesome integration&lt;/a&gt; that has all it's versions hosted with us.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;PDF Support&lt;/h2&gt;
&lt;p&gt;Sphinx has interesting support for PDF generation through Latex. In my testing it was pretty unreliable, but I was able to rangle it into working well enough to expose in the UI. So now almost every project will have a "Download PDF" button. This code has version support as well, so we can offer PDFs of certain versions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://media.readthedocs.org/pdf/django/latest/django.pdf"&gt;Django's trunk documentation PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://media.readthedocs.org/pdf/django-cms/2.1.0.rc2/django-cms.pdf"&gt;Django CMS 2.1.0-rc2 PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://media.readthedocs.org/pdf/varnish/latest/varnish.pdf"&gt;Varnish trunk PDF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another interesting part of this feature is that this building code has been abstracted out, so we can support epub, plain text, and all the other Sphinx output formats that people want.&lt;/p&gt;
&lt;h2&gt;Badges on the project pages&lt;/h2&gt;
&lt;p&gt;We killed the RTD header on hosted documentation pages in favor of a Badge in the lower right hand corner. The header clashed with a lot of the themes, and the badge is nice because it gives us a place to put functionality that is always visible, but is obviously not part of the hosted documentation. We want to build some more functionality into the badge, like switching between versions and linking back to the project's RTD page, once we build a good UI for it.&lt;/p&gt;
&lt;h2&gt;Sponsorship&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://www.revsys.com/"&gt;Revsys&lt;/a&gt; has agreed to sponsor the hosting costs for RTD. Jacob Kaplan-Moss has always been a big proponent of documentation, and I'm glad that he and Frank Wiles are helping us keep Read the Docs around and get better. We tried to make the sponsorship subtle and not intrusive, so please let me know if it bothers you and we can try and figure something out.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I think that these features are really starting to make RTD a compelling platform for hosting your documentation. We are planning more awesome features that will make RTD even better. I'm really excited about the project and I hope that you either host your docs with us, or find docs that we host useful.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2011/jan/11/read-docs-updates/</guid></item><item><title>Handling Django Settings Files</title><link>http://ericholscher.com/blog/2011/jan/10/handling-django-settings-files/</link><description>


&lt;p&gt;I have seen a lot of talk over the past couple years about how to handle different settings files and databases, synced between production and development. I have happened onto a way of doing it that makes me happy, and figured I would share it with the world.&lt;/p&gt;
&lt;h2&gt;File structure&lt;/h2&gt;
&lt;p&gt;I use a file structure that looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
        &lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
        &lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
        &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The base.py contains all of the configuration options that are shared among the databases. INSTALLED_APPS, etc. All of the DATABASE settings should be specified in the more-specific files. As well as things that differ by environment, like remote servers, cache settings, cookie domains, and other things.&lt;/p&gt;
&lt;p&gt;This allows you to run the sqlite settings file, and have it be set to localhost, or whatever your development settings are. Then in production you just run against the postgres settings.&lt;/p&gt;
&lt;p&gt;A good example of this being used in practice is on &lt;a href="https://github.com/rtfd/readthedocs.org/tree/master/settings"&gt;Read the Docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But wait, there's more!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;manage.py for dev&lt;/h2&gt;
&lt;p&gt;./manage.py is great for development. It is the easiest way to get started, and it automatically sets up your paths and stuff. With my setup, I actually &lt;a href="https://github.com/rtfd/readthedocs.org/blob/master/manage.py#L3"&gt;explicitly set&lt;/a&gt; manage.py's settings file to the sqlite file.&lt;/p&gt;
&lt;p&gt;This means that whenever you are using manage.py, you are in a development context. So, what do you do about production?&lt;/p&gt;
&lt;h2&gt;django-admin.py is for production&lt;/h2&gt;
&lt;p&gt;In production, you set your DJANGO_SETTINGS_MODULE to the postgres settings file. So whenever you use django-admin.py, you will be running against the production database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I really like this scheme, because it gives you a logical distinction between production and development in your code, and in your interface on the CLI.&lt;/strong&gt; When you are developing, you are using manage.py and editing the sqlite settings file. The reverse for production.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2011/jan/10/handling-django-settings-files/</guid></item><item><title>Using Haystack to index non-database content</title><link>http://ericholscher.com/blog/2010/nov/17/using-haystack-index-non-database-content/</link><description>


&lt;p&gt;Over on ReadTheDocs, I wanted to build &lt;a href="http://readthedocs.org/search/?q=crawler"&gt;search&lt;/a&gt; around the documentation that we're hosting. I chose &lt;a href="http://haystacksearch.org/"&gt;Haystack&lt;/a&gt; and &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt; for this, because it's the best way to do search in Django these days. However, I've only ever used Haystack to index content that is in the database. I thought about trying to add all the rendered HTML from the documentation into the database, but that was a non-starter.&lt;/p&gt;
&lt;p&gt;I ended up adding a ImportedFile model to the database, which would contain the metadata for the HTML file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImportedFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;imported_files&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;md5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This allows me to link the SearchIndex in haystack to a model. Then the interesting part is in the Haystack SearchIndex, where I override the prepare_text method, allowing me to read the data in from the filesystem instead of from the database.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ImportedFileIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchIndex&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_attr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;project__user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_attr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;project__name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_attr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;full_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_html_path&lt;/span&gt;
        &lt;span class="n"&gt;to_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;codecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; not found&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;full_path&lt;/span&gt;

&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ImportedFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ImportedFileIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that I don't have to bloat my database with all my rendered HTML, but have the full HTML stored in Solr which works for querying.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2010/nov/17/using-haystack-index-non-database-content/</guid></item><item><title>Building a Django App Server with Chef: Part 4</title><link>http://ericholscher.com/blog/2010/nov/11/building-django-app-server-chef-part-4/</link><description>


&lt;p&gt;Alternate title: There's no place like home!&lt;/p&gt;
&lt;p&gt;This is Part 4, the final part, of my Chef tutorial. Today we're talking about the odds and ends left over to make the server nice to use. You can check out the first 3 parts of the series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://ericholscher.com/blog/2010/nov/8/building-django-app-server-chef/"&gt;Part 1: Chef Beginnings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ericholscher.com/blog/2010/nov/9/building-django-app-server-chef-part-2/"&gt;Part 2: Python environment buildout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ericholscher.com/blog/2010/nov/10/building-django-app-server-chef-part-3/"&gt;Part 3: Deployment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Today's code will be in the git repo under the tag &lt;a href="https://github.com/ericholscher/chef-django-example/tree/blog-post-4"&gt;blog-post-4&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What we'll need&lt;/h2&gt;
&lt;p&gt;So we have our app server up and running, and ready for traffic. Now we just need to add some other bits around the outside for it to be fully functioning and nice to use.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monitoring with &lt;a href="http://munin-monitoring.org/"&gt;Munin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Background tasks with &lt;a href="http://celeryproject.org/"&gt;Celery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A firewall for security&lt;/li&gt;
&lt;li&gt;A /etc/hosts file for talking with other nodes&lt;/li&gt;
&lt;li&gt;A .bash_profile file so that when you shell in you'll have a nice environment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let's get started.&lt;/p&gt;
&lt;h2&gt;Monitoring with Munin&lt;/h2&gt;
&lt;p&gt;For doing monitoring with munin, we're going to need to learn our final Chef concept, which is Templates. You should be pretty familiar with them already, except they use Erb, which is a template language that lets you embed Ruby. &lt;/p&gt;
&lt;p&gt;We're only going to be configuring the Munin node here. This assumes that you have a munin server running on another machine that you want to give access to monitor your new app server. These configs depend on you putting an entry like this in your node.json, which points at the IP of the master server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;munin_servers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10.177.243.34&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then here is how you would write the Recipe.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/munin.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;munin-node&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;munin-node&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;munin_servers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/munin/munin-node.conf&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;munin-node.conf&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0640&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;munin_servers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;munin_servers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;notifies&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;munin-node&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The template Resource here is the interesting part. We're surrounding it with a conditional, that makes sure that we're defined a 'munin_servers' key in our node.json. Then we're saying that we're going to render the munin-node.conf file with the source template 'munin-node.conf'. This template will be given the extra varibale 'munin_servers', which is passed in using the variables attribute.&lt;/p&gt;
&lt;p&gt;Template are placed inside the cookbook in a similar place to files.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/templates/default/munin-node.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@munin_servers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;-%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;allow ^&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\./&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;$&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;-%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="x"&gt;allow ^127\.0\.0\.1$&lt;/span&gt;

&lt;span class="x"&gt;host *&lt;/span&gt;
&lt;span class="x"&gt;port 4949&lt;/span&gt;

&lt;span class="x"&gt;log_level 4&lt;/span&gt;
&lt;span class="x"&gt;log_file /var/log/munin/munin-node.log&lt;/span&gt;
&lt;span class="x"&gt;pid_file /var/run/munin/munin-node.pid&lt;/span&gt;
&lt;span class="x"&gt;background 1&lt;/span&gt;
&lt;span class="x"&gt;setsid 1&lt;/span&gt;
&lt;span class="x"&gt;user root&lt;/span&gt;
&lt;span class="x"&gt;group root&lt;/span&gt;

&lt;span class="x"&gt;ignore_file ~$&lt;/span&gt;
&lt;span class="x"&gt;ignore_file DEADJOE$&lt;/span&gt;
&lt;span class="x"&gt;ignore_file \.bak$&lt;/span&gt;
&lt;span class="x"&gt;ignore_file %$&lt;/span&gt;
&lt;span class="x"&gt;ignore_file \.dpkg-(tmp|new|old|dist)$&lt;/span&gt;
&lt;span class="x"&gt;ignore_file \.rpm(save|new)$&lt;/span&gt;
&lt;span class="x"&gt;ignore_file \.pod$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The interesting part here is the iteration over the munin_servers list. It's just doing a simple ruby loop, and then outputting the IP address that it contains into the format that munin's configuration file expects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This data-driven template rendering is a really powerful idiom, and one of my favorite parts about Chef. This allows you to add a new server to your pool, and have all of your configuration files updated automatically across all your server. This is hugely powerful, and one of the primary wins of Configuration Management. This will be shown to better effect in the /etc/hosts file later.&lt;/p&gt;
&lt;h2&gt;Installing Celery&lt;/h2&gt;
&lt;p&gt;Installing celery is much akin to Gunicorn that was discussed yesterady. The dependencies were installed from our pip requirements file, and we just need to make it run in upstart. We'll be doing that with the following setup.&lt;/p&gt;
&lt;p&gt;Additions to &lt;code&gt;cookbooks/main/recipes/readthedocs.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;cookbook_file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/init/readthedocs-celery.conf&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;celery.conf&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0644&lt;/span&gt;
    &lt;span class="n"&gt;notifies&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;readthedocs-celery&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;readthedocs-celery&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="nn"&gt;Chef::Provider::Service::&lt;/span&gt;&lt;span class="n"&gt;Upstart&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;cookbooks/main/files/celery.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Celery for ReadTheDocs&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;runlevel&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2345&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;runlevel&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="mi"&gt;2345&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;#Send KILL after 20 seconds&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;

&lt;span class="n"&gt;script&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;celeryd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="sr"&gt;/home/&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="sr"&gt;/sites/&lt;/span&gt;&lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/run/c&lt;/span&gt;&lt;span class="n"&gt;elery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;

&lt;span class="n"&gt;respawn&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There isn't anything new or interesting here. Just more of the same as before, to get another piece of infrastructure up and running.&lt;/p&gt;
&lt;h2&gt;A ghetto firewall install&lt;/h2&gt;
&lt;p&gt;I'm a big fan of not enabling services that aren't running as a fundamental security practice, but having a basic firewall to make sure that those are the only ports open isn't a bad idea either. I'm not a great expert, so this is probably the weakest part of my knowledge in this series, so take it with a grain of salt.&lt;/p&gt;
&lt;p&gt;My favorite firewall utility is ufw. It makes managing your firewall really simple. Here is my super basic way to configure my firewall, it pretty much sucks :)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/security.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;package &lt;span class="s"&gt;&amp;quot;ufw&amp;quot;&lt;/span&gt; do
    :upgrade

service &lt;span class="s"&gt;&amp;quot;ufw&amp;quot;&lt;/span&gt; do
  enabled true
  running true
  supports :status &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; true&lt;span class="p"&gt;,&lt;/span&gt; :restart &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; true&lt;span class="p"&gt;,&lt;/span&gt; :reload &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; true
  action &lt;span class="p"&gt;[&lt;/span&gt;:enable&lt;span class="p"&gt;,&lt;/span&gt; :start&lt;span class="p"&gt;]&lt;/span&gt;
end

bash &lt;span class="s"&gt;&amp;quot;Enable UFW&amp;quot;&lt;/span&gt; do
user &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  code &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;EOH
  ufw allow &lt;span class="m"&gt;22&lt;/span&gt; &lt;span class="c1"&gt;#SSH&lt;/span&gt;
  ufw allow &lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="c1"&gt;#Nginx&lt;/span&gt;
  ufw allow &lt;span class="m"&gt;4949&lt;/span&gt; &lt;span class="c1"&gt;#Munin&lt;/span&gt;
  EOH
end
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we're just enabling SSH, Nginx, and Munin. If we need to install any more packages, we'll need to expicitly open a port, which is usually good to remind me that I'm doing it.&lt;/p&gt;
&lt;h2&gt;/etc/hosts&lt;/h2&gt;
&lt;p&gt;Whenever I'm in the cloud, I find keeping track of my other servers to be a pain. You generally want to use the internal backplane to communicate between your servers, so I use the /etc/hosts file to make that simple.&lt;/p&gt;
&lt;p&gt;We're going to depend on an entry in your node.json that looks something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;  &lt;span class="s"&gt;&amp;quot;all_servers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Golem&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10.177.234.234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;173.203.234.234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                   &lt;span class="s"&gt;&amp;quot;Chimera&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10.177.234.234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;204.232.234.234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                   &lt;span class="s"&gt;&amp;quot;Hydra&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10.177.234.234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;173.203.234.234&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which is a mapping of all your servers, with their internal and external IPs. This will be useful to have for lots of different recipes, and it would be nice to autogenerate this, but when you only have a few servers it isn't so bad.&lt;/p&gt;
&lt;p&gt;The rest of out hosts configuration looks like this:&lt;/p&gt;
&lt;p&gt;Addition to &lt;code&gt;cookbooks/main/recipes/default.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;?(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;all_servers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/hosts&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;hosts&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mi"&gt;644&lt;/span&gt;
    &lt;span class="n"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;all_servers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;all_servers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;cookbooks/main/templates/default/hosts&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="x"&gt;127.0.0.1     localhost localhost.localdomain&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@all_servers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_pair&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;-%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt; &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;-%&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, when we add a server to the all_servers hash, it will propogate out to the /etc/hosts file of our app server. This makes me really happy, and showcases some of the more advanced use cases of Chef.&lt;/p&gt;
&lt;h2&gt;Customizing your shell&lt;/h2&gt;
&lt;p&gt;Now that we have the server all set up, it won't be much good if it isn't nice to use when we shell in. So here is how I go ahead and add in some nicities to bash for when you log in.&lt;/p&gt;
&lt;p&gt;Addition to &lt;code&gt;cookbooks/main/recipes/readthedocs.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;cookbook_file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/docs/.bash_profile&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;bash_profile&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0755&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;cookbooks/main/files/default/bash_profile&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bashrc&lt;/span&gt;

&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;PIP_DOWNLOAD_CACHE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/tmp/&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PYTHONPATH:&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/sites/&lt;/span&gt;&lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/checkouts/&lt;/span&gt;&lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;

&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="sr"&gt;/readthedocs.org/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;

&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/sites/&lt;/span&gt;&lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;chk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cd /home/docs/sites/readthedocs.org/checkouts/readthedocs.org&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cd /home/docs/sites/readthedocs.org/run&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First off, we're sourcing the .bashrc file, so that we get all the nice things it provides, like a colored PS1. Then we're setting some environment variables so that django-admin.py and pip work nicely. Then we activate our virtualenv and switch into it's base directory, so we're always starting where we want to be on login. Then we just have a couple of aliases for easy navigation around. &lt;/p&gt;
&lt;p&gt;I like how this makes the user experience of shelling into the server a lot nicer, and makes the manual workflow that you'll eventually have to fiddle with really nice.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So that's the end of this tutorial. I hope that it was instructive in learning Chef, as well as providing some insights into the deployment of a Django application. Tomorrow (or if I'm too tired, next week), I'll be providing some thoughts on how I think chef treated me, and how I feel about the build out.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2010/nov/11/building-django-app-server-chef-part-4/</guid></item><item><title>Building a Django App Server with Chef: Part 3</title><link>http://ericholscher.com/blog/2010/nov/10/building-django-app-server-chef-part-3/</link><description>


&lt;p&gt;Alternate title: Show the world what you've got.&lt;/p&gt;
&lt;p&gt;This is Part 3 of my Chef tutorial. Today we're talking about deployment. You can check out the first 2 parts of the series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://ericholscher.com/blog/2010/nov/8/building-django-app-server-chef/"&gt;Part 1: Chef Beginnings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ericholscher.com/blog/2010/nov/9/building-django-app-server-chef-part-2/"&gt;Part 2: Python environment buildout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Today's code will be in the git repo under the tag &lt;a href="https://github.com/ericholscher/chef-django-example/tree/blog-post-3"&gt;blog-post-3&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What we'll need&lt;/h2&gt;
&lt;p&gt;We'll be taking the Django application that we have on the server and actually deploying it. Let's make a list of what we'll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A web server to sit in front and proxy requests&lt;/li&gt;
&lt;li&gt;A WSGI server &lt;/li&gt;
&lt;li&gt;A way to keep both of these things running&lt;/li&gt;
&lt;li&gt;A caching layer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'll be using Nginx, Memcached, Upstart, and Gunicorn. This is my preferred deployment stack as of late, mainly because of the simple setup.&lt;/p&gt;
&lt;p&gt;Let's get started&lt;/p&gt;
&lt;h2&gt;A web server&lt;/h2&gt;
&lt;p&gt;Getting Nginx up and running should be old hat by this point. We're going to need the package and service Resources, which will tell Chef to install and run it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/nginx.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nginx&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nginx&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;cookbook_file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/nginx/sites-enabled/readthedocs&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nginx/readthedocs&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0640&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;notifies&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nginx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;cookbook_file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/nginx/nginx.conf&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nginx/nginx.conf&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0640&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;notifies&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;nginx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we're providing our own nginx.conf and a readthedocs site configuration. I'm not going to paste these in, as they are pretty application specific, but you can look at them &lt;a href="https://github.com/ericholscher/chef-django-example/tree/blog-post-3/cookbooks/main/files/default/nginx/"&gt;on Github&lt;/a&gt; if you're curious. I also wrote about it &lt;a href="http://ericholscher.com/blog/2010/aug/28/new-feautures-read-docs/"&gt;a while back&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The only new part here is the &lt;code&gt;notifies&lt;/code&gt; command, which is pretty nifty. It basically means that whenever you change the nginx.conf file, it should restart Nginx, which is a really nice feature.&lt;/p&gt;
&lt;h2&gt;A WSGI Server&lt;/h2&gt;
&lt;p&gt;Yesterday, when we installed the deploy_requirements.txt with pip, it pulled in &lt;a href="http://gunicorn.org/"&gt;gunicorn&lt;/a&gt;. So we actually have Gunicorn already installed in our virtualenv, waiting for us to use it. The only difference is I actually committed a change to the ReadTheDocs source so that it will pull Gunicorn from the git master, which I'll explain below.&lt;/p&gt;
&lt;h2&gt;Upstart&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I use upstart because it ships with Ubuntu, so you don't need to install a separate package. However, it has pretty horrible documentation, with the &lt;a href="http://upstart.ubuntu.com/wiki/Stanzas"&gt;Stanzas&lt;/a&gt; doc probably the best clue as to what it supports.&lt;/p&gt;
&lt;p&gt;Here is where things get interesting. I spent a bunch of time trying to get gunicorn and upstart to play nicely yesterday night, but it wasn't working. I went on the #gunicorn IRC channel on Freenode today, and talked with benoitc. He was awesome and &lt;a href="https://github.com/benoitc/gunicorn/commit/f29c61091691135dcfae029a7eadf1663a06a73e"&gt;patched gunicorn&lt;/a&gt; to work with Upstart for me.&lt;/p&gt;
&lt;p&gt;Here is the upstart script that we're using to keep gunicorn running:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/files/default/gunicorn.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Gunicorn for ReadTheDocs&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;runlevel&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2345&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;runlevel&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="mi"&gt;2345&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;#Send KILL after 5 seconds&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;respawn&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;VENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/home/docs/sites/readthedocs.org&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;#Serve Gunicorn on the internal rackspace IP.&lt;/span&gt;
&lt;span class="n"&gt;script&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="nv"&gt;$VENV&lt;/span&gt;&lt;span class="sr"&gt;/bin/g&lt;/span&gt;&lt;span class="n"&gt;unicorn_django&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;preload&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="nv"&gt;$VENV&lt;/span&gt;&lt;span class="sr"&gt;/run/g&lt;/span&gt;&lt;span class="n"&gt;unicorn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="nv"&gt;$VENV&lt;/span&gt;&lt;span class="sr"&gt;/run/g&lt;/span&gt;&lt;span class="n"&gt;unicorn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;177&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;207&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8888&lt;/span&gt; &lt;span class="nv"&gt;$VENV&lt;/span&gt;&lt;span class="sr"&gt;/checkouts/&lt;/span&gt;&lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/settings/&lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, an Upstart script is a pretty clean way to do this. If you've ever tried to write an old SysV-style init script, this will look beautiful. You'll notice that we aren't passing the --daemon parameter to gunicorn, this is because upstart will background the process for us, and keep track of everything, so we don't need gunicorn's daemonizing behavior.&lt;/p&gt;
&lt;p&gt;It should be pointed out how awesome it is that we can run a production ready WSGI server with a single line of bash. If you've ever set up a mod_wsgi install, needing to fuddle with your apache.conf and a WSGI file and everything makes it a chore. This is quite simply the easiest way to deploy a WSGI application.&lt;/p&gt;
&lt;p&gt;Then we need some additions to &lt;code&gt;cookbooks/main/recipes/readthedocs.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;cookbook_file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/init/readthedocs-gunicorn.conf&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;gunicorn.conf&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0644&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;readthedocs-gunicorn&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="nn"&gt;Chef::Provider::Service::&lt;/span&gt;&lt;span class="n"&gt;Upstart&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you can see we're doing a similar thing to the other service declarations. We however need to tell Chef to use Upstart for this service, instead of defaulting to init.d. Other than that, everything here should look similar to the other files and services we've set up.&lt;/p&gt;
&lt;h2&gt;Memcached&lt;/h2&gt;
&lt;p&gt;As you would expect, installing memcached is just like nginx:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/memcached.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;memcached&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;memcached&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;cookbook_file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/memcached.conf&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;memcached.conf&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0640&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;notifies&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;memcached&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The memcached.conf is so short, I might as well include it here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="n"&gt;logfile&lt;/span&gt; &lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;memcached&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="mi"&gt;11211&lt;/span&gt; 
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;nobody&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Memcache's config file is pretty neat, because it's basically just a list of arguments to pass to the daemon when it's started. A little bit like a pip requirements file is just commands to pass to pip install when it's run.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Now that you have these awesome new recipes, and additions to old ones, we need to make sure they're actually being run. Your run_list in your node.json file should now look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;quot;run_list&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::readthedocs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::memcached&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::nginx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point, it's pretty neat. I can run a &lt;code&gt;fab install_chef update&lt;/code&gt;, wait about 5 minutes, and go from a freshly paved server to a fully functioning app server.&lt;/p&gt;
&lt;p&gt;Tomorrow we'll be adding some monitoring and auxiliary niceties. This includes setting up Munin, Celery, generating the /etc/hosts file, and throwing in a little .bashrc magic to make the user experience nicer.&lt;/p&gt;
&lt;p&gt;There were a couple of questions yesterday about databases and other things. My current problem is running an application server, which is what I've accomplished. However, with my new-found love affair for chef, I will definitely be making my Database/Utility box into a chef configuration really soon. I might not write it up in so much detail, but hopefully you've learned enough from this series that I can just publish the code.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2010/nov/10/building-django-app-server-chef-part-3/</guid></item><item><title>Building a Django App Server with Chef: Part 2</title><link>http://ericholscher.com/blog/2010/nov/9/building-django-app-server-chef-part-2/</link><description>


&lt;p&gt;Alternate title: Actually doing something useful.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ericholscher.com/blog/2010/nov/8/building-django-app-server-chef/"&gt;Yesterday&lt;/a&gt; we covered the basics to getting started with Chef. You should have a remote server configured with chef, and have curl installed! Now lets go ahead and get some useful bits for your Django application.&lt;/p&gt;
&lt;h2&gt;What we'll need&lt;/h2&gt;
&lt;p&gt;So this is going to be based around the way that I set up my servers, so if this is different than you, I'm sorry. However, I think it is a pretty solid way of managing them. A lot of the ideas here are stolen from &lt;a href="http://traviscline.com/"&gt;Travis&lt;/a&gt; when he set up the server for Pypants.&lt;/p&gt;
&lt;p&gt;So lets assemble a list of things we're going to want in order to get a super basic Django configuration running:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A user to run our code as and who's home directory we'll store the data.&lt;/li&gt;
&lt;li&gt;A basic global python ecosystem, including setuptools and pip&lt;/li&gt;
&lt;li&gt;A virtualenv to store all the project-specific packages and code in&lt;/li&gt;
&lt;li&gt;A copy of the project that we'll be running&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let's get started.&lt;/p&gt;
&lt;p&gt;The finished code for today is located on github, with the &lt;a href="https://github.com/ericholscher/chef-django-example/tree/blog-post-2"&gt;tag blog-post-2&lt;/a&gt;. It is a copy of the completed steps, so feel free to peek through that and come back here for clarification (or to ask questions).&lt;/p&gt;
&lt;h2&gt;Setting up our user&lt;/h2&gt;
&lt;p&gt;For &lt;a href="http://readthedocs.org/"&gt;RTD&lt;/a&gt;, I run everything under the user docs. So we'll go ahead and set up that user so that we can get our site set up. We're going to go ahead and replace our "default" recipe, because right now it isn't doing anything much useful. The relevant part is below:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/default.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;base_packages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_pair&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;gid&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="n"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
        &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;uid&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;gid&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/sbin/nologin&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/bin/bash&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;manage_home&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/#{username}&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/#{username}/.ssh&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0700&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/#{username}/.ssh/authorized_keys&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0600&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_pair&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;gid&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There's a lot of stuff going on here, so lets go over it. First you might notice that there's this node variable, the node data structure is the JSON that you have in your node.json file. It is looping over the keys and values with ruby's each_pair and pair functions.&lt;/p&gt;
&lt;p&gt;The base_packages bit is a cool example of the power of the chef configuration. We have a list of packages that we want to install in our Attributes, and we're looping over them and setting using the package Resource.&lt;/p&gt;
&lt;p&gt;I realize I skipped over the run_list part yesterday, but it basically is just a list of recipes to run. Each of the resources in the default.rb file should be pretty self explanatory. The &lt;a href="http://wiki.opscode.com/display/chef/Resources"&gt;Chef Resource Documentation&lt;/a&gt; is really comprehensive, and will probably be the most referenced document that you use. The main resource's that we used were &lt;strong&gt;group, user, file, directory&lt;/strong&gt;, let's take a look at the &lt;a href="http://wiki.opscode.com/display/chef/Resources#Resources-User"&gt;User&lt;/a&gt; declaration in particular.&lt;/p&gt;
&lt;p&gt;Everything there should be pretty obvious, as it's the information that goes into /etc/passwd for the user. However, the &lt;code&gt;supports&lt;/code&gt; keyword isn't obvious at first. This is part of the &lt;a href="http://wiki.opscode.com/display/chef/Resources#Resources-CommonAttributes"&gt;Common Attributes&lt;/a&gt; that can be set on all Resources. It's a way of passing along configuration options to the Resource. manage_home actually just makes it so that the users home directory is created when the user is created.&lt;/p&gt;
&lt;p&gt;So we're going to have to go ahead and put some data in there for it to work with. Our node.json will now look like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;node.json&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s"&gt;&amp;quot;run_list&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;main::readthedocs&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="s"&gt;&amp;quot;base_packages&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;git-core&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;bash-completion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="s"&gt;&amp;quot;users&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;full_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Docs User&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ssh-rsa key-goes-here eric@Bahamut&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="s"&gt;&amp;quot;groups&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;gid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;members&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Adding a Basic Python Environment&lt;/h2&gt;
&lt;p&gt;Now lets go ahead and add a python recipe to build out some basic python stuff that we'll be needing.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/python.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;ubuntu_python_packages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# System-wide packages installed by pip.&lt;/span&gt;
&lt;span class="c1"&gt;# Careful here: most Python stuff should be in a virtualenv.&lt;/span&gt;

&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;pip_python_packages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each_pair&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;install-#{pkg}&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;pip install #{pkg}==#{version}&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;not_if&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;[ `pip freeze | grep #{pkg} | cut -d&amp;#39;=&amp;#39; -f3` = &amp;#39;#{version}&amp;#39; ]&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additions to &lt;code&gt;node.json&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;  &lt;span class="s"&gt;&amp;quot;ubuntu_python_packages&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;python-setuptools&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;python-pip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;python-dev&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;libpq-dev&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="s"&gt;&amp;quot;pip_python_packages&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;virtualenv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;1.5.1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;mercurial&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;1.7&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we're adding some global packages that we need. We're going to install setuptools and pip so that we can install further python packages. python-dev and libpq-dev are so that we have the headers for libraries that need to compile against postgres and python. We'll also be installing virtualenv and mercurial globally so that we can create our virtualenv and install packages from mercurial.&lt;/p&gt;
&lt;h2&gt;Creating a virtualenv&lt;/h2&gt;
&lt;p&gt;We're going to introduce the first new Chef concept here, which is called a &lt;a href="http://wiki.opscode.com/display/chef/Definitions"&gt;Definition&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Definition (cookbooks/*/definitions/*.rb)&lt;/p&gt;
&lt;p&gt;A definition is a custom Resource that you build to abstract a set of operations. Pretty simple&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a definition that &lt;a href="https://gist.github.com/612395"&gt;Jacob published&lt;/a&gt; and then I updated to make the permissions correct. It allows you to set up a virtualenv:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/definitions/virtualenv.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;virtualenv&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0755&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;
        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Manage&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;create-virtualenv-#{path}&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;virtualenv #{path}&amp;quot;&lt;/span&gt;
            &lt;span class="n"&gt;not_if&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;test -f #{path}/bin/python&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;each_pair&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kd"&gt;package&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#{path}/bin/pip&amp;quot;&lt;/span&gt;
            &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;install-#{package}-#{path}&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
                &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;#{pip} install #{package}==#{version}&amp;quot;&lt;/span&gt;
                &lt;span class="n"&gt;not_if&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;[ `#{pip} freeze | grep #{package} | cut -d&amp;#39;=&amp;#39; -f3` = &amp;#39;#{version}&amp;#39; ]&amp;quot;&lt;/span&gt;
            &lt;span class="n"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;
        &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;
            &lt;span class="n"&gt;recursive&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, it takes a bunch of arguments, then just wraps up a bunch of Resource definitions in a nice little package. There is a little bit of magic with the pip freezing things, but it's basically just how we're checking to make sure that a package isn't instead before we install it. We are using only using the &lt;strong&gt;directory and execute&lt;/strong&gt; Resources here. &lt;/p&gt;
&lt;p&gt;Now we're going to use this virtualenv Definition, and create the home virtualenv for our site. I like to keep my virtualenv's in &lt;code&gt;~/sites/&lt;domain&gt;&lt;/code&gt;, so this will go into &lt;code&gt;/home/docs/sites/readthedocs.org/&lt;/code&gt;. Since this is becoming specific to the site we're building, it's going to go into a readthedocs recipe:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cookbooks/main/recipes/readthedocs.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/docs/sites/&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0775&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;virtualenv&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/home/docs/sites/readthedocs.org&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mo"&gt;0775&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will set up a basic virtualenv in our directory.&lt;/p&gt;
&lt;h2&gt;Getting our site set up&lt;/h2&gt;
&lt;p&gt;To get our site set up, we need to pull in the source code, and make sure our virtualenv has all the requirements. This code is a little bit hacky, and could probably be abstracted out a bit, but it will work for now. We're going to go ahead and add some things to our readthedocs Recipe.&lt;/p&gt;
&lt;p&gt;Additions to &lt;code&gt;cookbooks/main/recipes/readthedocs.rb&lt;/code&gt; &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;directory &lt;span class="s"&gt;&amp;quot;/home/docs/sites/readthedocs.org/run&amp;quot;&lt;/span&gt; do
    owner &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    group &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
    mode &lt;span class="m"&gt;0775&lt;/span&gt;
end

git &lt;span class="s"&gt;&amp;quot;/home/docs/sites/readthedocs.org/checkouts/readthedocs.org&amp;quot;&lt;/span&gt; do
  repository &lt;span class="s"&gt;&amp;quot;git://github.com/rtfd/readthedocs.org.git&amp;quot;&lt;/span&gt;
  reference &lt;span class="s"&gt;&amp;quot;HEAD&amp;quot;&lt;/span&gt;
  user &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
  group &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
  action :sync
end

script &lt;span class="s"&gt;&amp;quot;Install Requirements&amp;quot;&lt;/span&gt; do
  interpreter &lt;span class="s"&gt;&amp;quot;bash&amp;quot;&lt;/span&gt;
  user &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
  group &lt;span class="s"&gt;&amp;quot;docs&amp;quot;&lt;/span&gt;
  code &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;EOH
  &lt;span class="o"&gt;/&lt;/span&gt;home&lt;span class="o"&gt;/&lt;/span&gt;docs&lt;span class="o"&gt;/&lt;/span&gt;sites&lt;span class="o"&gt;/&lt;/span&gt;readthedocs.org&lt;span class="o"&gt;/&lt;/span&gt;bin&lt;span class="o"&gt;/&lt;/span&gt;pip install &lt;span class="o"&gt;-&lt;/span&gt;r &lt;span class="o"&gt;/&lt;/span&gt;home&lt;span class="o"&gt;/&lt;/span&gt;docs&lt;span class="o"&gt;/&lt;/span&gt;sites&lt;span class="o"&gt;/&lt;/span&gt;readthedocs.org&lt;span class="o"&gt;/&lt;/span&gt;checkouts&lt;span class="o"&gt;/&lt;/span&gt;readthedocs.o
rg&lt;span class="o"&gt;/&lt;/span&gt;deploy_requirements.txt
  EOH
end
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I like to have my runtime files in the &lt;code&gt;venv/run&lt;/code&gt; directory, so we'll go ahead and create that directory. Then comes the fun part.&lt;/p&gt;
&lt;p&gt;We are checking the Readthedocs source out of github with the &lt;a href="http://wiki.opscode.com/display/chef/Deploy+Resource#DeployResource-Examples"&gt;git&lt;/a&gt; Resource. Chef only supports git and svn as far as I can tell, so luckily I'm using git. &lt;/p&gt;
&lt;p&gt;Then we're going to install from the pip requirements file. This is using the &lt;a href="http://wiki.opscode.com/display/chef/Resources#Resources-Script"&gt;script Resource&lt;/a&gt;, which allows you to inline a bash, ruby, python, or more script inside your Recipe. This is using a hard coded bash script to install the requirements, which sucks, but will work for now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Chef appears to buffer output and not show itself as doing anything when running the script Resource here, so it will look like your build will hang while it installs your pip requirements file for the first time.&lt;/p&gt;
&lt;h2&gt;Done for now&lt;/h2&gt;
&lt;p&gt;Alright, this post has gotten long enough, so we're done for today. But we're in a pretty awesome spot, I think. We now have our app server set up with a runnable version of our code. You can go ssh in and play around, you should be able to run simple manage.py commands inside the virtualenv and whatnot (after a syncdb).&lt;/p&gt;
&lt;p&gt;Tomorrow we'll talk about deploying our code with Nginx and Gunicorn. I've been having trouble with Upstart, so we might switch our deployment to Supervisord, but we'll see how it goes.&lt;/p&gt;
&lt;p&gt;Don't forget to check out the finished code &lt;a href="https://github.com/ericholscher/chef-django-example/tree/blog-post-2"&gt;on Github&lt;/a&gt; to see the actual running examples.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2010/nov/9/building-django-app-server-chef-part-2/</guid></item><item><title>Building a Django App Server with Chef: Part 1</title><link>http://ericholscher.com/blog/2010/nov/8/building-django-app-server-chef/</link><description>


&lt;p&gt;Alternate title: Fucking Chef, How does it work?&lt;/p&gt;
&lt;p&gt;When I started looking at &lt;a href="http://wiki.opscode.com/display/chef/Home"&gt;Chef&lt;/a&gt;, I found it incredibly complex and lacking in fundamental documentation. This is going to be my experience understanding Chef while setting up a single server. This strategy can be used across multiple servers, with a little tweaking.&lt;/p&gt;
&lt;p&gt;I'd like to thank &lt;a href="http://jacobian.org/"&gt;Jacob&lt;/a&gt; for the ideas and some of the inspiration behind the code and ideas. The code for this blog post can be found &lt;a href="https://github.com/ericholscher/chef-django-example"&gt;on github&lt;/a&gt;. It will be expanded as I write updates.&lt;/p&gt;
&lt;p&gt;Today's code will be using tag &lt;a href="https://github.com/ericholscher/chef-django-example/tree/blog-post-1"&gt;blog-post-1&lt;/a&gt; in the repo.&lt;/p&gt;
&lt;h2&gt;Goal&lt;/h2&gt;
&lt;p&gt;I'm hoping to write a blog series that goes from explaining what Chef is, to having a working Django server, and to release all this code so that you can tweak and use it with your own servers. I'll be doing this to set up a new and configuration managed server for &lt;a href="http://readthedocs.org/"&gt;ReadTheDocs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are a &lt;a href="http://brainspl.at/articles/2009/01/31/cooking-with-chef-101"&gt;couple of&lt;/a&gt; other &lt;a href="http://morethanseven.net/2010/10/30/Chef-hello-world.html"&gt;good chef intros&lt;/a&gt; available. However, they only get you to the super basic first step of setting up the server. Hopefully this series will be more in depth and useful to actually getting a real server running under Chef.&lt;/p&gt;
&lt;h2&gt;Basic terminology&lt;/h2&gt;
&lt;p&gt;Chef has some basic terminology that you need to understand before we get into things. I'm going to purposefully leave out a ton of stuff, because it isn't really important for me now.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cookbook (cookbooks/*)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A cookbook, not surprisingly, contains recipes. With my configuration, we're only going to use one cookbook, that has multiple recipes. This is probably wrong and horrible, but it's simple, which is the goal for now.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recipe (cookbooks/*/recipes/*.rb)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A recipe is the basic building block of Chef. It is what does the meat of the work that you want done.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A resources is an abstraction that defines a specific piece of your configuration. It can be a file, a user, or just about anything you want to talk about on your system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Attributes (node.json)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Attributes are just a blob of JSON that Chef uses to pass around data. It will be (slightly) different for each server that you want to set up. I really like this approach, because I think a data-driven approach is the correct way to solve this problem.&lt;/p&gt;
&lt;h2&gt;Bootstrapping chef&lt;/h2&gt;
&lt;p&gt;We'll be running what is called &lt;a href="http://wiki.opscode.com/display/chef/Chef+Solo"&gt;chef solo&lt;/a&gt;. This means that chef will be run independently of a server, and just execute on our one host. This seems to be the easiest way to get things running.&lt;/p&gt;
&lt;p&gt;When you first run &lt;code&gt;chef-solo&lt;/code&gt;, it will look for a &lt;code&gt;solo.rb&lt;/code&gt; file. The &lt;a href="http://wiki.opscode.com/display/chef/Chef+Solo#ChefSolo-ConfigureChefSolo"&gt;documentation about configuring chef-solo&lt;/a&gt; does a decent job of explaining this. By default it looks in /etc/chef/solo.rb, so lets go ahead and use that. It has 2 required fields, a &lt;code&gt;cookbook_path&lt;/code&gt; and &lt;code&gt;json_attribs&lt;/code&gt;. &lt;code&gt;cookbook_path&lt;/code&gt; tells chef where to find the "cookbooks" it uses to run your code, and &lt;code&gt;json_attribs&lt;/code&gt; tells it where to load in your data dictionary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The docs say that &lt;code&gt;file_cache_path&lt;/code&gt; is required, but it just defaults to &lt;code&gt;/var/chef/cache&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For simplicity, we're going to keep our cookbooks and json data file in &lt;code&gt;/etc/chef&lt;/code&gt;. On my local machine I keep everything in a &lt;code&gt;~/projects/chef&lt;/code&gt; directory, and then sync that to the remote box. &lt;/p&gt;
&lt;p&gt;In production you'll probably want to set up your remote server to pull from a repository somewhere, so that you can have a stable deployment base, but again, syncing from the local filesystem is simple and works.&lt;/p&gt;
&lt;h2&gt;Bootstrapping your new server&lt;/h2&gt;
&lt;p&gt;I'll be using a &lt;a href="http://docs.fabfile.org/"&gt;fabric&lt;/a&gt; script to run the commands on a remote server, which also allows me to run them again later on a new machine in an automated fashion. So this is the first simple script that we'll use to bootstrap our new server, &lt;code&gt;fabfile.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sudo&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;root&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;204.232.205.196&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chef_executable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/var/lib/gems/1.8/bin/chef-solo&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;install_chef&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;apt-get update&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;apt-get install -y git-core rubygems ruby ruby-dev&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;gem install chef&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;sync_config&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;rsync -av . %s@%s:/etc/chef&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;sync_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cd /etc/chef &amp;&amp; %s&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chef_executable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A couple notes&lt;/strong&gt;: the chef executable is version-dependent, but that's because I don't know enough ruby to query it dynamically. You will also need to change the value of the env.hosts to a server that you actually control.&lt;/p&gt;
&lt;p&gt;This will install the ruby dependencies, and get chef up and running for you. You'll need to install fabric (&lt;code&gt;pip install fabric&lt;/code&gt;, presumably in a virtualenv), and then run &lt;code&gt;fab install_chef&lt;/code&gt; to get it up and running. Then you can go ahead and run &lt;code&gt;fab sync_config&lt;/code&gt; to get your chef configuration onto the remote server.&lt;/p&gt;
&lt;p&gt;Now we need to go ahead and figure out how to make chef actually do something. You'll see in the &lt;code&gt;cookbooks/main/recipes/default.rb&lt;/code&gt; file we have a simple &lt;code&gt;package&lt;/code&gt; declaration. This simply means to make sure that the package is installed on the remote system. This is as simple as it gets, so lets go ahead and run it. With the &lt;code&gt;fab update&lt;/code&gt; command, it will sync your local directory, and run chef on the remote server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fab&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;run:&lt;/span&gt; &lt;span class="n"&gt;rsync&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;av&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sr"&gt;/etc/c&lt;/span&gt;&lt;span class="n"&gt;hef&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;sudo:&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="sr"&gt;/etc/c&lt;/span&gt;&lt;span class="n"&gt;hef&lt;/span&gt; &lt;span class="o"&gt;&amp;&amp;&lt;/span&gt; &lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="sr"&gt;/gems/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="sr"&gt;/gems/c&lt;/span&gt;&lt;span class="n"&gt;hef&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="sr"&gt;/bin/c&lt;/span&gt;&lt;span class="n"&gt;hef&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;solo&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;err:&lt;/span&gt; &lt;span class="n"&gt;stdin:&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tty&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;Setting&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;run_list&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;main::default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;Chef&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;Installing&lt;/span&gt; &lt;span class="nb"&gt;package&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;ubuntu1&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;Chef&lt;/span&gt; &lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;574963&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;cleaning&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;checksum&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;232&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;196&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;out:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt; &lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mo"&gt;0000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;INFO:&lt;/span&gt; &lt;span class="n"&gt;Report&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;You now have Chef running on your server&lt;/strong&gt;. That was pretty easy, eh? For tomorrow's lesson, we'll be making it actually do something, like installed nginx and gunicorn, and keeping track of config files.&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2010/nov/8/building-django-app-server-chef/</guid></item><item><title>Celery Tips</title><link>http://ericholscher.com/blog/2010/nov/2/celery-tips/</link><description>


&lt;p&gt;Following on yesterday's post about &lt;a href="http://ericholscher.com/blog/2010/nov/1/virtualenv-tips/"&gt;Virtualenv Tips&lt;/a&gt;, I will be talking about &lt;a href="http://celeryproject.org/"&gt;celery&lt;/a&gt; tips. Yesterday I talked about how to run celery with upstart easily, and today I'll be expanding on that below as well as talking about how to set it up using supervisord.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Also interesting, I wrote a &lt;a href="http://ericholscher.com/blog/2008/oct/5/django-tips/"&gt;Big list of django tips&lt;/a&gt; back in 2008, that still has a lot of good information.&lt;/p&gt;
&lt;h2&gt;Running celery in development&lt;/h2&gt;
&lt;p&gt;When you run celery in production, you should be using a queue on the backend. However, when you're running celery in development, it's nice to execute the code paths, but not actually need a queue. This is where the &lt;a href="http://celeryq.org/docs/configuration.html#celery-always-eager"&gt;CELERY_ALWAYS_EAGER setting&lt;/a&gt; comes in handy. It makes celery run the code in process, but will make sure your code paths work correctly.&lt;/p&gt;
&lt;p&gt;I talk about this and more in &lt;a href="http://ericholscher.com/blog/2010/sep/10/djangocon-talk/"&gt;my djangocon talk&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Killing long running tasks&lt;/h2&gt;
&lt;p&gt;On &lt;a href="http://readthedocs.org"&gt;ReadTheDocs&lt;/a&gt; I would run into problems with celery tasks never returning. Luckily, celery has a way to handle this. The &lt;a href="http://ask.github.com/celery/configuration.html#celeryd-task-time-limit"&gt;CELERYD_TASK_TIME_LIMIT&lt;/a&gt; setting lets you set the number of seconds that a task can run until it is killed. This is nice to make sure that a run-away task won't take down all your backend processing.&lt;/p&gt;
&lt;h2&gt;Use the JSON serializer for interoperability&lt;/h2&gt;
&lt;p&gt;I was talking on IRC to &lt;a href="http://www.eflorenzano.com/"&gt;Eric Florenzano&lt;/a&gt; and he mentioned that you should use the &lt;a href="http://celeryq.org/docs/userguide/executing.html#executing-serializers"&gt;json serializer&lt;/a&gt; if you want to be able to add celery tasks from other languages. &lt;/p&gt;
&lt;p&gt;This allows you to use another language to put a message that looks like a &lt;a href="http://ask.github.com/celery/internals/protocol.html#example-message"&gt;celery task&lt;/a&gt; in the queue, and it should just work.&lt;/p&gt;
&lt;h2&gt;Explictly set the number of clients&lt;/h2&gt;
&lt;p&gt;When you run celery, it defaults to having the number of workers equal to the number of cores the machine has. If you are running multiple queue workers on the same machine, it is a good idea to use less. You can set this with the &lt;a href="http://ask.github.com/celery/reference/celery.conf.html#celery.conf.CELERYD_CONCURRENCY"&gt;CELERYD_CONCURRENCY&lt;/a&gt; setting, or passing &lt;code&gt;-c&lt;num&gt;&lt;/code&gt; on the command line.&lt;/p&gt;
&lt;h2&gt;Running against multiple databases with supervisord&lt;/h2&gt;
&lt;p&gt;At work we run a bunch of different sites on multiple databases. When we were figuring out how to deploy celery, we needed a good way to make sure that celeryd was always running, and we needed multiple celery daemons for each of our databases. We have written our tasks to run against multiple sites on the same database in order to reduce the number of daemons we had to use. &lt;/p&gt;
&lt;p&gt;Celery ships with a couple of daemon configurations out of the box, support for init.d style init scripts, and support for &lt;a href="http://supervisord.org/"&gt;supervisord&lt;/a&gt;. We first looked at the init.d approach, but there didn't seem to be a good way to have it run multiple settings files without creating multiple scripts, which seemed hacky. So we went with superisord for the task. Below is our configuration, if you are curious.&lt;/p&gt;
&lt;h5&gt;/etc/supervisord.conf&lt;/h5&gt;
&lt;p&gt;By default, the conf file is in the top-level /etc/ directory. We kept it this way, but I kind of wish it was in it's own directory. This is basically the exact script that &lt;a href="http://github.com/ask/celery/blob/master/contrib/supervisord/supervisord.conf"&gt;celery ships with&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;unix_http_server&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/tmp/s&lt;/span&gt;&lt;span class="n"&gt;upervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="nb"&gt;socket&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="sr"&gt;/supervisord/s&lt;/span&gt;&lt;span class="n"&gt;upervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;supervisord&lt;/span&gt; &lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
&lt;span class="n"&gt;logfile_maxbytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="n"&gt;MB&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;maximum&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;logfile&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt;
&lt;span class="n"&gt;logfile_backups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;backed&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;logfiles&lt;/span&gt;
&lt;span class="n"&gt;loglevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;
&lt;span class="n"&gt;pidfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pidfile&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;
&lt;span class="n"&gt;nodaemon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;supervisord&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;daemon&lt;/span&gt;
&lt;span class="n"&gt;minfds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;startup&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;descriptors&lt;/span&gt;
&lt;span class="n"&gt;minprocs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="n"&gt;descriptors&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="n"&gt;childlogdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="sr"&gt;/supervisord/&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="n"&gt;live&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rpcinterface:supervisor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpcinterface_factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpcinterface:make_main_rpcinterface&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;supervisorctl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;serverurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;unix:&lt;/span&gt;&lt;span class="sr"&gt;///tmp/s&lt;/span&gt;&lt;span class="n"&gt;upervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;unix:&lt;/span&gt;&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;schem&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt; &lt;span class="n"&gt;sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;celeryd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then we created a supervisord directory which we included in the above file (in the last line) that contains the celery specific configuration. On this machine the only thing that supervisord is watching is celery, so that has kept our configuration simple.&lt;/p&gt;
&lt;h5&gt;/etc/supervisord/celeryd.conf&lt;/h5&gt;
&lt;p&gt;Inside of our celeryd specific configuration we went with mostly stock options except how we are setting up the DJANGO_SETTINGS_MODULE. We need to change the environment in which we are deploying, so that the celery daemon runs against the correct database.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;program:celery&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cms&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/home/code&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ljworld.standard&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/home/co&lt;/span&gt;&lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="sr"&gt;/django/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;celeryd&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;loglevel&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nobody&lt;/span&gt;
    &lt;span class="n"&gt;numprocs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;stdout_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="sr"&gt;/celery/cms&lt;/span&gt;&lt;span class="n"&gt;_supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;
    &lt;span class="n"&gt;stderr_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="sr"&gt;/celery/cms&lt;/span&gt;&lt;span class="n"&gt;_supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="n"&gt;autostart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;autorestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;startsecs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;program:celery&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;weeklies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/home/code&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;desotoexplorer.settings&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/home/co&lt;/span&gt;&lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="sr"&gt;/django/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;celeryd&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;loglevel&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nobody&lt;/span&gt;
    &lt;span class="n"&gt;numprocs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;stdout_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="sr"&gt;/celery/&lt;/span&gt;&lt;span class="n"&gt;weeklies_supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;
    &lt;span class="n"&gt;stderr_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/var/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="sr"&gt;/celery/&lt;/span&gt;&lt;span class="n"&gt;weeklies_supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="n"&gt;autostart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;autorestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;startsecs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The really nice part about using supervisord is that our fabric script for deploying changes to celery is just deploying the code and then running &lt;code&gt;supervisorctl restart celery-cms&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I hope today's post was useful, and I'm again curious for any other awesome celery tips!&lt;/p&gt;

</description><guid>http://ericholscher.com/blog/2010/nov/2/celery-tips/</guid></item></channel></rss>

