{"id":1763,"date":"2010-09-18T16:35:08","date_gmt":"2010-09-18T15:35:08","guid":{"rendered":"http:\/\/www.devco.net\/?p=1763"},"modified":"2010-09-19T08:45:29","modified_gmt":"2010-09-19T07:45:29","slug":"puppet_search_engine_with_mcollective","status":"publish","type":"post","link":"https:\/\/www.devco.net\/archives\/2010\/09\/18\/puppet_search_engine_with_mcollective.php","title":{"rendered":"Puppet Search Engine with MCollective"},"content":{"rendered":"
The current supported way of making a Puppet node aware of the state of other nodes is Exported Resources<\/a>, there’s some movement in this space see for example a recent thread on the developers list<\/a>.<\/p>\n I’ve personally never found exported resources to be a good fit for me, I have many masters spread over long network distances and they often operate in a split brain scenario where the masters couldn’t see each other. This makes using something like MySQL as a database hard since you’d effectively need a single write point and replication is point to point.<\/p>\n There are other problems too, consider a use case where you have modules for phpbb, wordpress, rails apps, phpmyadmin and several others that need to talk to the master database. Maybe you also want to show in the motd a resource list for customers. <\/p>\n Every one of these modules have configuration parameters that differ in some way or the other. The exported resources model kind of expect you to export from the MySQL Master the configs for all the apps using the database, this isn’t viable when you don’t know what all will talk to this MySQL server and the resource abstraction doesn’t really work well when the resulting config might need to look like this:<\/p>\n <\/code><\/p>\n Filling in that array with exported resources is pretty hard.<\/p>\n So I favor searching nodes and walking the results. I tend to use extlookup<\/a> for this and just maintain the hosts lists by hand, this works but it’s maintained by hand, there has to be a better way.<\/p>\n I wrote a mcollective registration system<\/a> that puts all node data in MongoDB<\/a> instances. <\/p>\n Simply using mcollective to manage the data store has several advantages:<\/p>\n And storing the data in a document database – and specifically Mongo over some of the other contenders:<\/p>\n The mcollective registration system will store per node the following data:<\/p>\n You can now use this in Puppet manifests as follows to fill some of our stated needs, the code below use the search engine to find all the machines with class “mysql::server” for a specific customer and then display this data in the MOTD:<\/p>\n <\/code><\/p>\n Here is a template that does a search using this too, it makes a comma seperate list of mysql slave ip addresses for a customer:<\/p>\n <\/code><\/p>\n This code can probably be cleaned up a bit, would be helpful if Puppet gave us better ways to extend it to enable this kind of thing.<\/p>\n I find this approach much less restrictive and easier to get my head around than exported resources. I obviously see the need for tracking actual state across a infrastructure and exported resources will solve that in future when you have the ability to require a database on another node to be up before starting a webserver process, but that seems to be long way off for Puppet. And I think even then a combination system like this will have value.<\/p>\n A note about the code and how to install it. Puppet has a very limited extension system at present, you cannot add Puppet::Util<\/em> classes via pluginsync and there’s no support for any kind of pooling of resources from plugins. So this code needs to be installed into your Ruby libdir directly and I use a Singleton to maintain a database pool per Puppetmaster process. This is not ideal, I’ve filed feature enhancement requests with Puppet Labs, hopefully things will become easier in time.<\/p>\n<\/p>\n
\r\n$write_database = \"db1.your.net\";\r\n$read_databases = \"db2.your.net\", \"db3.your.net\", \"db4.your.net\";\r\n<\/pre>\n
\n
\n
\n
<\/p>\n
\r\n# Connect to the database, generally do this in site.pp\r\nsearch_setup(\"localhost\", \"puppet\", \"nodes\")\r\n\r\n# A define to add the hostname and ip address of the MySQL server to motd\r\ndefine motd::register_mysql {\r\n $node = load_node($name)\r\n\r\n motd::register{\"MySQL Server: ${node['fqdn']} \/ ${node['facts']['ipaddress']}\"\r\n}\r\n\r\n$nodes = search_nodes(\"{'facts.customer' => '$customer', 'classes' => 'mysql::server'}\")\r\n\r\nmotd::register_mysql{$nodes: }\r\n<\/pre>\n
<\/p>\n
\r\n$read_databases = <%= \r\nnodes = Puppet::Util::MongoQuery.instance.find_nodes({\"facts.customer\" => \"#{customer}\", \"classes\" => \"mysql::slave\"})\r\n\r\nnodes.map {|n| '\"' + n[\"facts\"][\"ipaddress\"] + '\"' }.join(\", \")\r\n%>\r\n<\/pre>\n