Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cross node orchestration with Chef and Noah

John Vincent
May 16, 2012
1.8k

Cross node orchestration with Chef and Noah

long-form of my talk at #chefconf

John Vincent

May 16, 2012
Tweet

Transcript

  1. We've got this part down { "name": "es_dispatcher_lb", "description": "",

    "json_class": "Chef::Role", "default_attributes": { }, "override_attributes": { "postfix": { "myhostname": "dispatcher-lb.enstratus.com" } }, "chef_type": "role", "run_list": [ "role[es_loadbalancer]", "recipe[enstratus::dispatcher_lb]", "role[monitored_host]" ], "env_run_lists": { } } {
  2. We've got this part down { "name": "es_dispatcher_lb", "description": "",

    "json_class": "Chef::Role", "default_attributes": { }, "override_attributes": { "postfix": { "myhostname": "dispatcher-lb.enstratus.com" } }, "chef_type": "role", "run_list": [ "role[es_loadbalancer]", "recipe[enstratus::dispatcher_lb]", "role[monitored_host]" ], "env_run_lists": { } } Do this Then this Then this
  3. The deep Chefster backends = search(:node, "role:#{node[:enstratus] [:dispatcher_lb][:dispatcher_server_role]} AND chef_environment:#{node.chef_environment}")

    + <% @backends.each do |be| -%> server <%= be.name %> <%= be.ipaddress %>:2023 check inter 5000 <% end -%> = "recipe[enstratus::dispatcher_lb]"
  4. The deep Chefster backends = search(:node, "role:#{node[:enstratus] [:dispatcher_lb][:dispatcher_server_role]} AND chef_environment:#{node.chef_environment}")

    + <% @backends.each do |be| -%> server <%= be.name %> <%= be.ipaddress %>:2023 check inter 5000 <% end -%> = "recipe[enstratus::dispatcher_lb]"
  5. You want to... • Have confguration changes on one system

    affect change on another system • Gate confguration changes on one system based on state of another system • Establish relationships between systems
  6. But... • Should adding a new backend node cause a

    chef- client run on the haproxy nodes? • If the chef-client run on the haproxy node changes a different setting, what node(s) needs to know that? • What if THOSE nodes change something? NOW who do we tell?
  7. Be careful what you wish for • Circular dependencies •

    Transitive dependencies • Version conflicts • Computational cost
  8. What is it? • Sinatra + Redis app • HTTP

    + JSON • RESTish • Watches (callbacks) • Inspired by, but no replacement for, Apache Zookeeper
  9. Data goes in... curl -X PUT -d '{"name":"fooapp"}' \ http://noahserverhost:5678/applications/fooapp

    {"result":"success", "id":"0d52d942-4580-f30e-45154edda2a2fc3d", "action":"create", "name":"fooapp" }
  10. Data comes out... curl -X GET \ http://noahserverhost:5678/applications/fooapp { "id":"0d52d942-4580-f30e-4515-4edda2a2fc3d",

    "tags":[], "links":[], "name":"fooapp", "created_at":"Thu May 10 02:22:53 UTC 2012", "updated_at":"Thu May 10 02:22:53 UTC 2012", "configurations":{} }
  11. Ephemerals • Arbitrary paths in the URL scheme namespaced under

    /ephemerals • Key is path name /ephemerals/foo/bar/chefconf/ • Value is...whatever Doesn't have to be JSON. Could be a blob. • No relation in between keys /foo/bar is unrelated to /foo
  12. Watches • “Pluggable” callbacks • Think triggers • Register against

    any path in the system • Changes at that path (or below it) execute the registered callback • Sends JSON dump of the state of change
  13. Watch Example (part 1) • Add some data curl -XPUT

    -d '1' http://localhost:5678/ephemerals/chef/notify/localhost • Get a response {"action":"create", "result":"success", "id":"7e743edd-a40f-5062-a5a0-70a8b4df7721", "path":"/chef/notify/localhost", "data":"1", "created_at":"Wed May 09 03:56:25 UTC 2012", "updated_at":"Wed May 09 03:56:25 UTC 2012"}
  14. Watch Example (part 2) • Register a watch curl -XPUT

    -d '{"pattern":"//noah/ephemerals/chef/notify/localhost", "endpoint":"http://localhost:8080"}' http://localhost:5678/watches/ • Get a response {"action":"create","result":"success","id":"51828298-ad79-26c3-459b- fd757b85db75","pattern":"//noah/ephemerals/chef/notify/localhost","name" :"Ly9ub2FoL2VwaGVtZXJhbHMvY2hlZi9ub3RpZnkvbG9jYWxob3N0fGh0dHA6Ly9sb2NhbG hvc3Q6ODA4MQ==","endpoint":"http://localhost:8080,"created_at":"Thu May 10 03:02:25 UTC 2012","updated_at":"Thu May 10 03:02:25 UTC 2012"}
  15. What just happened? • We told Noah that when a

    change happens to /ephemerals/chef/notify/localhost • send an HTTP POST to http://localhost:8080/
  16. Now what? • Change that data curl -XPUT -d '2'

    http://localhost:5678/ephemerals/chef/notify/localhost Get a response {"action":"update", "result":"success", "id":"7e743edd-a40f-5062-a5a0-70a8b4df7721", "path":"/chef/notify/localhost", "data":"2", "created_at":"Wed May 09 03:56:25 UTC 2012", "updated_at":"Wed May 09 04:00:00 UTC 2012"}
  17. But something else happened this time... I, [2012-05-09T00:01:00.395491 #36180] INFO

    -- Noah::Agent: Found new watches D, [2012-05-09T00:01:00.395611 #36180] DEBUG -- Noah::Agent: Current watch count: 0 D, [2012-05-09T00:01:00.396681 #36180] DEBUG -- Noah::Agent: New watch count: 1 I, [2012-05-09T00:01:00.396850 #36180] INFO -- Noah::Agents::HttpAgent: Noah::Agents::HttpAgent worker initiated I, [2012-05-09T23:10:36.438180 #38052] INFO -- Noah::Agents::HttpAgent: Message posted to http://localhost:8080 successfully
  18. And what did localhost:8080 see? { "id"=>"7e743edd-a40f-5062-a5a0-70a8b4df7721", "tags"=>[], "links"=>[], "path"=>"/ephemerals//chef/notify/localhost",

    "data"=>"1", "created_at"=>"Wed May 09 03:56:25 UTC 2012", "updated_at"=>"Thu May 10 03:10:36 UTC 2012", "action"=>"update", "pubcategory"=>"//noah/ephemerals/chef/notify/localhost" }
  19. What can we do with that? • Parse the data

    to effect some change? • Use it as a signaling mechanism? • Alert on it? • More importantly, how can we use it with Chef?
  20. Noah cookbook • http://github.com/lusis-cookbooks/noah • Installs Noah + Redis •

    Adds a noah_get method • Automatically registers node in Noah (noah::register recipe) • Also some new resources....
  21. Noah LWRP • Register any primitive • noah_application • noah_service

    • noah_configuration • noah_application • Dump some data in an ephemeral node • noah_ephemeral • Block the chef run until some data shows up in Noah • noah_block
  22. Current Solution • knife bootstrap -r “role[mysql]” • Wait •

    knife bootstrap -r “role[django1] • Wait • knife bootstrap -r “role[django2]” • Wait • knife bootstrap -r “role[haproxy]”
  23. noah_block haproxy django1 django2 mysql • Needs a DB •

    Loads DB Needs to wait for initial DB load Needs backends Needs a DB
  24. Triggering Chef Runs • Use noah_ephemeral at end of the

    dependency run_list (on the backend node) • Leverage string interpolation for path name • Data is unimportant, just the signal
  25. Small Solution • Small web app • listens for HTTP

    POST requests • runs chef-client
  26. Well that was fun • Obviously needs to be done

    “right” • Add security • Write your own! • Watches are pluggable. • AMQP • ZeroMQ • HTTPS + Token auth?