Ansible: A Puppet User's Perspective (DevOps Sydney, 2014)

E34acb847338523dc088f03f0eedd1eb?s=47 Rob Howard
September 18, 2014

Ansible: A Puppet User's Perspective (DevOps Sydney, 2014)

A talk given at Devops Sydney in September 2014, explaining Ansible to Puppet users, with a divergence into patterns and problems you can expect to encounter with each.

E34acb847338523dc088f03f0eedd1eb?s=128

Rob Howard

September 18, 2014
Tweet

Transcript

  1. ANSIBLE A Puppet User's Perspective

  2. What Ansible is.

  3. Ansible is an
 open-source software platform for [remotely] configuring and

    managing computers. “
  4. Basic Bits

  5. Basic Bits • "Push" model;
 Control Machine
 talking to
 Dumb

    Clients
 via SSH.
  6. Basic Bits • "Push" model;
 Control Machine
 talking to
 Dumb

    Clients
 via SSH. ➡ "Pull" model; Puppet Agents
 talking back to
 Puppet Master
 via SSL.
  7. Basic Bits • Tasks • Inventories • Variables • Handlers

    • Playbooks • Roles
  8. Basic Bits • Tasks • Inventories • Variables • Handlers

    • Playbooks • Roles ➡ Tasks ➡ Nodes + Roles ➡ Variables (Hiera) ➡ notify  =>  ...   ➡ Manifests (ish) ➡ Classes + Profiles
  9. Tasks -­‐  apt:          pkg:  nginx  

           state:  present package  {  "nginx":
  ensure  =>  present,
 }
  10. Tasks -­‐  service:          name:  nginx  

           enabled:  yes          state:  started service  {  "nginx":
  ensure  =>  started,
  requires  =>
    Package["nginx"],
 }
  11. Tasks -­‐  authorized_key:      user:  "bert"      comment:

     "laptop"
    key:  >
      ssh-­‐rsa  AAA...== ssh_authorized_key{
  "laptop":
  type  =>  "ssh-­‐rsa",
  key    =>  "AAA...==",
  user  =>
      User["bert"],
  requires  =>
      User["bert"],
 }
  12. Inventories [nameservers]
 ns01
 ns02
 
 [apps]
 app01
 app02
 
 [staging:children]


    nameservers
 apps
  13. Inventories [nameservers]
 ns01
 ns02
 
 [apps]
 app01
 app02
 
 [staging:children]


    nameservers
 apps • Node definition blocks • Node classifiers / Roles • Node definitions + Environments
  14. Variables #  group_vars/all
 prompt_colour:  "black"
 
 #  group_vars/staging
 prompt_colour:  "green"


    
 #  group_vars/prod
 prompt_colour:  "red"
 
 #  host_vars/ns01
 prompt_colour:  >
    special  snowflake
  15. Variables #  group_vars/all
 prompt_colour:  "black"
 
 #  group_vars/staging
 prompt_colour:  "green"


    
 #  group_vars/prod
 prompt_colour:  "red"
 
 #  host_vars/ns01
 prompt_colour:  >
    special  snowflake #  hiera/data/global
 prompt::colour:  "black"
 
 #  hiera/data/env/staging
 prompt::colour:  "green"
 
 #  hiera/data/env/prod
 prompt::colour:  "red"
 
 #  hiera/data/hosts/ns01   prompt::colour:  >
    special  snowflake
  16. Handlers -­‐  template:        src:  "hosts.j2"    

       dest:  /etc/hosts"      notify:  >
        restart  dnsmasq   ! #  ...   ! -­‐  name:  restart  dnsmasq      service:        name:  dnsmasq
      state:  restarted file  {  "/etc/hosts":
  #  ...
  notify  =>
    Service['dnsmasq']
 }
  17. Playbooks -­‐  apt:          pkg:  nginx  

           state:  present
 
 -­‐  service:          name:  nginx          enabled:  yes          state:  started package  {  "nginx":
  ensure  =>  present,
 }
 
 service  {  "nginx":
  ensure  =>  started,
  requires  =>
    Package["nginx"],
 }
  18. Playbooks -­‐  apt:          pkg:  nginx  

           state:  present
 
 -­‐  service:          name:  nginx          enabled:  yes          state:  started package  {  "nginx":
  ensure  =>  present,
 }
 -­‐>
 service  {  "nginx":
  ensure  =>  started,
 }
  19. Playbooks -­‐  hosts:  all      roles:      -­‐

     common
 
 -­‐  include:  blah.yml
 
 -­‐  hosts:  nameservers
    roles:
    -­‐  nameserver node  /^ns\d+/  {
  include  common
  include
    profiles::nameservers
 }
  20. Roles roles/
  '-­‐nameserver/
        |-­‐tasks/
      

     |  |-­‐main.yml
        |  '-­‐dnsmasq.yml
        |-­‐handlers/
        |-­‐templates
        |-­‐files
        '-­‐meta
 
 -­‐  hosts:  ns01
    roles:
    -­‐  nameserver #  manifests/profiles/...
 class  profiles::nameserver  {
  include  foo::dnsmasq
 
  Blah  {  "etc":  ...  }
  #  ...
 }
 
 node  ns01  {
    include  profiles::nameserver
 }
  21. Stick it Together #  Vars
 my_keys:
 -­‐  "ssh-­‐rsa  AA..."
 -­‐

     "ssh-­‐rsa  BB..."
 
 #  Task
 -­‐  authorized_keys:        user:  "app"
      key:  "{{  item  }}"
    with_items:  my_keys
  22. Stick it Together #  Vars
 my_keys:
 -­‐  "ssh-­‐rsa  AA..."
 -­‐

     "ssh-­‐rsa  BB..."
 
 #  Task
 -­‐  authorized_keys:        user:  "app"
      key:  "{{  item  }}"
    with_items:  my_keys #  Hiera  Data
 my_keys:
 -­‐  "ssh-­‐rsa  AA..."
 -­‐  "ssh-­‐rsa  BB..."
 
 #  Manifest
 create_resources(
    'ssh_authorized_key',
    hiera('my_keys'),
    {  user  =>  'app'  }
 )
  23. Back to Playbooks -­‐  apt:          pkg:

     nginx          state:  present
 
 -­‐  service:          name:  nginx          enabled:  yes          state:  started package  {  "nginx":
  ensure  =>  present,
 }
 
 service  {  "nginx":
  ensure  =>  started,
  requires  =>
    Package["nginx"],
 }
  24. Back to Playbooks -­‐  apt:          pkg:

     nginx          state:  present
 
 -­‐  service:          name:  nginx          enabled:  yes          state:  started package  {  "nginx":
  ensure  =>  present,
 }
 -­‐>
 service  {  "nginx":
  ensure  =>  started,
 }
  25. Dependencies:
 Implicit vs Explicit

  26. Dependencies:
 Implicit vs Explicit (Ansible) (Puppet)

  27. Ordering:
 Implicit vs Explicit

  28. Ordering:
 Implicit vs Explicit (Puppet) (Ansible)

  29. What Ansible isn't.

  30. Ansible is simple because it uses YAML. “

  31. You may as well say: 
 C is simple
 because

    it uses
 letters, numbers and
 some symbols.
  32. Ansible is
 "Infrastructure as Data",
 not
 "Infrastructure as Code". “

  33. It's code! It's code even if you're writing it in

    a markup language; it's loops, variables, conditions, and functions. It's about as declarative as my mum's Shepherd's Pie recipe.
  34. Ansible Oddities

  35. Dumb Syntax Tricks -­‐  copy:  dest=/etc/motd  content="Yeah  nah"    

         
  36. Dumb Syntax Tricks -­‐  copy:  >
        dest=/etc/motd


           content="Yeah  nah"          
  37. Dumb Syntax Tricks -­‐  copy:  >
        dest=/etc/motd


           content={{  motd_message  }}
    vars:
        motd_message:  "Yeah  nah"   
 #  KABOOM
 #  Interpolated  as
 #      dest=/etc/motd  content=Yeah  nah
  38. Dumb Syntax Tricks -­‐  copy:  >
        dest=/etc/motd


           content="{{  motd_message  }}"
    vars:
        motd_message:  "Yeah  nah"
  39. Dumb Syntax Tricks -­‐  copy:  >
        dest=/etc/motd


           content="{{  motd_message  }}"
    vars:
        motd_message:  'Yeah,  "nah".'   ! #  KABOOM
 #  Interpolated  as
 #      dest=/etc/motd  content="Yeah,  "nah"."
  40. Dumb Syntax Tricks -­‐  copy:
        dest:  "/etc/motd"


           content:  "{{  motd_message  }}"
    vars:
        motd_message:  "Yeah,  nah"          
  41. Safety: with_items -­‐  copy:
        dest:  "/home/{{  item

     }}.ssh/ authorized_keys"
        content:  "{{  lookup('file',  "keys"  +   item)  }}"
    with_items:
    -­‐  "bert"
    -­‐  "missing"
  42. Safety: with_items -­‐  copy:
        dest:  "/home/{{  item

     }}.ssh/ authorized_keys"
        content:  "{{  lookup('file',  "keys"  +   item)  }}"
    with_items:
    -­‐  "bert"
    -­‐  "missing"
  43. Other Things • Reusing code is tough and inflexible; everything

    needs to be a role that you then stick together in the meta/main.yml
  44. Other Things • Reusing code is tough and inflexible; everything

    needs to be a role that you then stick together in the meta/main.yml • Third-party Roles via Ansible Galaxy is great, ... but a lot of it's new or naïve; unless you're lucky, you end up modifying a lot of it.
  45. Puppet Problems

  46. Looping #  Vars
 my_keys:
  -­‐  user:  app
      key:

     "..."
    
 #  Task
 -­‐  authorized_keys:        user:  "{{  item.user  }}"
      key:  "{{  item.key  }}"
    with_items:  my_keys
  47. Looping #  Vars
 my_keys:
  -­‐  user:  app
      key:

     "..."
    
 #  Task
 -­‐  authorized_keys:        user:  "{{  item.user  }}"
      key:  "{{  item.key  }}"
    with_items:  my_keys #  Hiera  Data
 my_keys:
 -­‐  "ssh-­‐rsa  AA..."
 -­‐  "ssh-­‐rsa  BB..."
 
 #  Manifest
 create_resources(
    'ssh_authorized_key',
    hiera('my_keys'),
    {  user  =>  'app'  }
 )
  48. Looping #  Vars
 my_keys:
  -­‐  user:  app
      key:

     "..."
    
 #  Task
 -­‐  authorized_keys:        user:  "{{  item.user  }}"
      key:  "{{  item.key  }}"
    with_items:  my_keys #  Hiera  Data
 my_keys:
 -­‐  "ssh-­‐rsa  AA..."
 -­‐  "ssh-­‐rsa  BB..."
 
 #  Manifest
 create_resources(
    'ssh_authorized_key',
    hiera('my_keys'),
    {  user  =>  'app'  }
 ) Structure of Heira data needs to match closely to what's consuming it (eg. SshAuthorizedKeys). Ansible is much more flexible (see above), and can avoid the massive single-resource-declaration problem lurking in the bottom-right. :-)
  49. Single Declaration Package  {  "ntp":      ensure  =>  present,

      }
 
 #  ...
 
 SomeThing  {  "foo":      #  ...
    requires  =>  Package['ntp'],
 }
  50. Single Declaration Package  {  "ntp":      ensure  =>  present,

      }
 
 #  ...
 
 SomeThing  {  "foo":      #  ...
    requires  =>  Package['ntp'],
 } Single-declaration-only for resources makes writing reusable third-party Puppet modules really hard. These (long) bug threads explain it well: • https://projects.puppetlabs.com/ issues/18490 • https://tickets.puppetlabs.com/ browse/PUP-1968
  51. • Ansible not as "simple" as touted. • Ansible has

    unsafe edge-cases to skirt around. • Ansible makes you remember the dependency ordering yourself, rather than encoding it. Summin' up.
  52. • Puppet has big problems it's going to find difficult

    to solve without breaking everything. (You can't silently merge two identical Service definitions if you have globals and could- change-on-each-call state hanging around.) • Puppet makes you write the dependency information, but then squanders it. • Using generic data is tough without custom mangling or the experimental syntax engine. Summin' up.
  53. Fin. 
 Rob Howard
 @damncabbage https://speakerdeck.com/damncabbage/