A Puppet ApproachTo ApplicationDeployment AndAutomation In NokiaOliver HookinsPrincipal Engineer – Services & Developer Ex...
Who am I?- Rockstar SysAdmin
Who am I?- Puppet User since 0.23- Working in Nokias Location-Based  Services for 1.5 years- Previously at Anchor Systems ...
What do we do?- “Location Based Services”- Diverse collection of applications- Rapidly moving environment,  competing dire...
What do we do?
What do we do?
The Problem Domain- Deployment consistent across  environments- Reduce duplication of effort (and as  a by-product, human ...
The Legacy System
The 1.0 Puppet System
New Problems- Node definitions- Lack of trust built-in- Code constantly evolving- Not enough testing to counter poor  Pupp...
BDD
Goals- Approach more like traditional  software development- Better versioning of components- Testing, testing, testing!- ...
Testing
Jenkins- CI/build tool- Split testing into stages- simple tests (lint, --parseonly,  templates) – take < 2min to run- comp...
API Approach- Expose a well-defined interface- Remove host-, role-, DC- (etc)  specifics from modules- Drive configuration...
ENC- Write one!  … or try Dashboard- Seriously, it unlocks Puppets  potential
Why not use extlookup?- Equivalent of global variables in  programming, bad!- No well-defined interface to  classes/defin...
Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.pp...
Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.pp...
Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.pp...
Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.pp...
Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.pp...
Class IntrospectionNow you have a YAML interface definition:  class:     postfix:       root_email: /dev/null      relayho...
Class Introspection- “Canary” testing- Interface documentation (have a  look at the :doc string as well)- Generate web forms
Module Organisation“Dist” - Provides “platform” services, as   well as APIs for application   modules to use - Stable, def...
Module Organisation“Dist” - Pull in things from Moduleforge, or   just use Moduleforge (in future)
Module Organisation“App” - Blueprints for how to deploy   applications - Uses Dist APIs rather than   reimplement function...
Packaging
Packaginghttp://hunnur.com/blog/2010/10/dynamic-git-branch-puppet-environments/ - Modules under /etc/puppet/modules - Each...
Environment Changedef get_node_previous_environment(fqdn)  factsdir = File.join(get_node_yamldir(), facts)  File.directory...
Environment Changedef get_node_previous_environment(fqdn)  factsdir = File.join(get_node_yamldir(), facts)  File.directory...
Environment Changedef get_node_previous_environment(fqdn)  factsdir = File.join(get_node_yamldir(), facts)  File.directory...
Environment Changedef get_node_previous_environment(fqdn)  factsdir = File.join(get_node_yamldir(), facts)  File.directory...
Environment Changeif node_puppetenvironment != oldenv then  Puppet.notice("Changing environment from #{oldenv} to         ...
Environment Changeif node_puppetenvironment != oldenv then  Puppet.notice("Changing environment from #{oldenv} to         ...
Environment Changeif node_puppetenvironment != oldenv then  Puppet.notice("Changing environment from #{oldenv} to         ...
Our ENC
Our ENCvars:  puppet_version: 2.6.7  ntp_servers:    - 192.0.2.1    - 192.0.2.2    - 192.0.2.3  repo_server: repo.ny.examp...
Future
Future- Deployment strategy not entirely  worked out- Developer workflows still could be  improved (some promising work in...
Thank You!oliver.hookins@nokia.com   ohookins@gmail.com http://paperairoplane.net/   P.S. Were Hiring!
Attributions- Photo on slide 8 by dandeluca, available under a Creative Commons Attribution licence- Photo on slide 9 by g...
Upcoming SlideShare
Loading in...5
×

Oliver hookins puppetcamp2011

4,436

Published on

Watch along with the video at https://www.youtube.com/watch?v=_oP1pFsOyDw

Oliver Hookins on "A Puppet Approach to Application Deployment and Automation in Nokia" at PuppetCamp Europe '11. Learn more: http://www.puppetlabs.com

Amsterdam, Netherlands, 2011.

Puppet Labs

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,436
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
29
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Oliver hookins puppetcamp2011

  1. 1. A Puppet ApproachTo ApplicationDeployment AndAutomation In NokiaOliver HookinsPrincipal Engineer – Services & Developer Experience
  2. 2. Who am I?- Rockstar SysAdmin
  3. 3. Who am I?- Puppet User since 0.23- Working in Nokias Location-Based Services for 1.5 years- Previously at Anchor Systems (manages GitHub infrastructure with Puppet)- Love/Hate Relationship with Ruby
  4. 4. What do we do?- “Location Based Services”- Diverse collection of applications- Rapidly moving environment, competing directly with well-known giants in the services arena- Large, traditionally-organised company with heavy change control
  5. 5. What do we do?
  6. 6. What do we do?
  7. 7. The Problem Domain- Deployment consistent across environments- Reduce duplication of effort (and as a by-product, human errors)- Increase testing, feedback of errors, reliability.
  8. 8. The Legacy System
  9. 9. The 1.0 Puppet System
  10. 10. New Problems- Node definitions- Lack of trust built-in- Code constantly evolving- Not enough testing to counter poor Puppet knowledge- Victim of our own success
  11. 11. BDD
  12. 12. Goals- Approach more like traditional software development- Better versioning of components- Testing, testing, testing!- Automation using Jenkins- Easier deployments- Better developer tools
  13. 13. Testing
  14. 14. Jenkins- CI/build tool- Split testing into stages- simple tests (lint, --parseonly, templates) – take < 2min to run- compile tests (using Faces) – should take < 10min- integration (full-deploy) - ??- Use in production! (canary)- Eventually: continuous deployment
  15. 15. API Approach- Expose a well-defined interface- Remove host-, role-, DC- (etc) specifics from modules- Drive configuration “through the front door”- Remove need to look at the code (basically reverse engineering)
  16. 16. ENC- Write one! … or try Dashboard- Seriously, it unlocks Puppets potential
  17. 17. Why not use extlookup?- Equivalent of global variables in programming, bad!- No well-defined interface to classes/defines- Requires that you code-dive to find out how to use a class/define- Well-defined API and general documentation should be sufficient- Cant easily test code in isolation
  18. 18. Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.ppPuppet[:manifest] = fileFile.open(file, r).readlines().each do |l|  if /^(class|define)s+([^({s]+)/    tipe = $~[1] # capture the type of object we found    id = $~[2] # we want the class/define name    # Grab the arguments and class/define name    args = Puppet::Resource::Type.find(id).to_pson_data_hash[arguments]    klass = Puppet::Resource::Type.find(id).to_pson_data_hash[name]  endendh[tipe][klass] = args
  19. 19. Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.ppPuppet[:manifest] = fileFile.open(file, r).readlines().each do |l|  if /^(class|define)s+([^({s]+)/    tipe = $~[1] # capture the type of object we found    id = $~[2] # we want the class/define name    # Grab the arguments and class/define name    args = Puppet::Resource::Type.find(id).to_pson_data_hash[arguments]    klass = Puppet::Resource::Type.find(id).to_pson_data_hash[name]  endendh[tipe][klass] = args
  20. 20. Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.ppPuppet[:manifest] = fileFile.open(file, r).readlines().each do |l|  if /^(class|define)s+([^({s]+)/    tipe = $~[1] # capture the type of object we found    id = $~[2] # we want the class/define name    # Grab the arguments and class/define name    args = Puppet::Resource::Type.find(id).to_pson_data_hash[arguments]    klass = Puppet::Resource::Type.find(id).to_pson_data_hash[name]  endendh[tipe][klass] = args
  21. 21. Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.ppPuppet[:manifest] = fileFile.open(file, r).readlines().each do |l|  if /^(class|define)s+([^({s]+)/    tipe = $~[1] # capture the type of object we found    id = $~[2] # we want the class/define name    # Grab the arguments and class/define name    args = Puppet::Resource::Type.find(id).to_pson_data_hash[arguments]    klass = Puppet::Resource::Type.find(id).to_pson_data_hash[name]  endendh[tipe][klass] = args
  22. 22. Class Introspection# Prepare a hash to dump out the APIh = {}h[class] = {}h[define] = {}# Inspect the PP filefile = foo.ppPuppet[:manifest] = fileFile.open(file, r).readlines().each do |l|  if /^(class|define)s+([^({s]+)/    tipe = $~[1] # capture the type of object we found    id = $~[2] # we want the class/define name    # Grab the arguments and class/define name    args = Puppet::Resource::Type.find(id).to_pson_data_hash[arguments]    klass = Puppet::Resource::Type.find(id).to_pson_data_hash[name]  endendh[tipe][klass] = args
  23. 23. Class IntrospectionNow you have a YAML interface definition:  class:     postfix:       root_email: /dev/null      relayhost:       inet_interfaces: localhost      alias_maps: hash:/etc/aliases      mynetworks: ""
  24. 24. Class Introspection- “Canary” testing- Interface documentation (have a look at the :doc string as well)- Generate web forms
  25. 25. Module Organisation“Dist” - Provides “platform” services, as well as APIs for application modules to use - Stable, defined release cycle - Should be of sufficient quality that app teams cannot resist using it - Eventually delegate maintenance to specialist teams
  26. 26. Module Organisation“Dist” - Pull in things from Moduleforge, or just use Moduleforge (in future)
  27. 27. Module Organisation“App” - Blueprints for how to deploy applications - Uses Dist APIs rather than reimplement functionality - Sets up development environments for application – still unsure of correct approach for this
  28. 28. Packaging
  29. 29. Packaginghttp://hunnur.com/blog/2010/10/dynamic-git-branch-puppet-environments/ - Modules under /etc/puppet/modules - Each has metadata files (version of app, version of dist) - Module path:$confdir/environments/$environment/app:$confdir/environments/$environment/dist - Spec file creates symlinks fromactual app and dist to above locations
  30. 30. Environment Changedef get_node_previous_environment(fqdn) factsdir = File.join(get_node_yamldir(), facts) File.directory?(factsdir) or raise NoFactsDirectoryError,factsdir # Inspect the client machines facts factsfile = File.join(factsdir, "#{fqdn}.yaml") if [nil, ].include?fqdn or not File.exist?(factsfile) raise(NoClientFactsError,factsfile) end # Create a Puppet::Node object from the stored YAML object y = YAML.load_file(factsfile) p = Puppet::Node::Facts.new(y.name, y.values) p.values[environment]end
  31. 31. Environment Changedef get_node_previous_environment(fqdn) factsdir = File.join(get_node_yamldir(), facts) File.directory?(factsdir) or raise NoFactsDirectoryError,factsdir # Inspect the client machines facts factsfile = File.join(factsdir, "#{fqdn}.yaml") if [nil, ].include?fqdn or not File.exist?(factsfile) raise(NoClientFactsError,factsfile) end # Create a Puppet::Node object from the stored YAML object y = YAML.load_file(factsfile) p = Puppet::Node::Facts.new(y.name, y.values) p.values[environment]end
  32. 32. Environment Changedef get_node_previous_environment(fqdn) factsdir = File.join(get_node_yamldir(), facts) File.directory?(factsdir) or raise NoFactsDirectoryError,factsdir # Inspect the client machines facts factsfile = File.join(factsdir, "#{fqdn}.yaml") if [nil, ].include?fqdn or not File.exist?(factsfile) raise(NoClientFactsError,factsfile) end # Create a Puppet::Node object from the stored YAML object y = YAML.load_file(factsfile) p = Puppet::Node::Facts.new(y.name, y.values) p.values[environment]end
  33. 33. Environment Changedef get_node_previous_environment(fqdn) factsdir = File.join(get_node_yamldir(), facts) File.directory?(factsdir) or raise NoFactsDirectoryError,factsdir # Inspect the client machines facts factsfile = File.join(factsdir, "#{fqdn}.yaml") if [nil, ].include?fqdn or not File.exist?(factsfile) raise(NoClientFactsError,factsfile) end # Create a Puppet::Node object from the stored YAML object y = YAML.load_file(factsfile) p = Puppet::Node::Facts.new(y.name, y.values) p.values[environment]end
  34. 34. Environment Changeif node_puppetenvironment != oldenv then Puppet.notice("Changing environment from #{oldenv} to #{node_puppetenvironment} for node #{fqdn}") y = {} y[parameters] = {} y[environment] = node_puppetenvironment y[classes] = {} y[classes][puppet::client] = {} y[classes][puppet::client][puppet_environment] = node_puppetenvironment.clone # YAML catches duplicate references y[classes][puppet::client][puppetmaster] = node_puppetmasterend
  35. 35. Environment Changeif node_puppetenvironment != oldenv then Puppet.notice("Changing environment from #{oldenv} to #{node_puppetenvironment} for node #{fqdn}") y = {} y[parameters] = {} y[environment] = node_puppetenvironment y[classes] = {} y[classes][puppet::client] = {} y[classes][puppet::client][puppet_environment] = node_puppetenvironment.clone # YAML catches duplicate references y[classes][puppet::client][puppetmaster] = node_puppetmasterend
  36. 36. Environment Changeif node_puppetenvironment != oldenv then Puppet.notice("Changing environment from #{oldenv} to #{node_puppetenvironment} for node #{fqdn}") y = {} y[parameters] = {} y[environment] = node_puppetenvironment y[classes] = {} y[classes][puppet::client] = {} y[classes][puppet::client][puppet_environment] = node_puppetenvironment.clone # YAML catches duplicate references y[classes][puppet::client][puppetmaster] = node_puppetmasterend
  37. 37. Our ENC
  38. 38. Our ENCvars: puppet_version: 2.6.7 ntp_servers: - 192.0.2.1 - 192.0.2.2 - 192.0.2.3 repo_server: repo.ny.example.com accounts: - bofh - peon myapp_version: 3.0.0classes: platform: puppet_version: ${puppet_version} ntp_servers: ${ntp_servers} repo_server: ${repo_server} accounts: ${accounts} myapp: version: ${myapp_version}
  39. 39. Future
  40. 40. Future- Deployment strategy not entirely worked out- Developer workflows still could be improved (some promising work in this area, e.g. cloudsmith/geppetto (IDE integration))
  41. 41. Thank You!oliver.hookins@nokia.com ohookins@gmail.com http://paperairoplane.net/ P.S. Were Hiring!
  42. 42. Attributions- Photo on slide 8 by dandeluca, available under a Creative Commons Attribution licence- Photo on slide 9 by graemenewcomb, available under aCreative Commons Attribution licence- Photo on slide 11 by seenful, available under a Creative Commons Attribution licence- Photo on slide 13 by f-oxymoron, available under a Creative Commons Attribution licence- Photo on slide 28 by oskay, available under a Creative Commons Attribution licence- Photo on slide 39 by kirrilyrobert, available under a Creative Commons Attribution licence
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×