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

Ansible: A Puppet User's Perspective (DevOps Sy...

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.

Rob Howard

September 18, 2014
Tweet

More Decks by Rob Howard

Other Decks in Technology

Transcript

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

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

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

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

           enabled:  yes          state:  started service  {  "nginx":
  ensure  =>  started,
  requires  =>
    Package["nginx"],
 }
  5. 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"],
 }
  6. Inventories [nameservers]
 ns01
 ns02
 
 [apps]
 app01
 app02
 
 [staging:children]


    nameservers
 apps • Node definition blocks • Node classifiers / Roles • Node definitions + Environments
  7. 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
  8. 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
  9. 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']
 }
  10. Playbooks -­‐  apt:          pkg:  nginx  

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

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

     common
 
 -­‐  include:  blah.yml
 
 -­‐  hosts:  nameservers
    roles:
    -­‐  nameserver node  /^ns\d+/  {
  include  common
  include
    profiles::nameservers
 }
  13. 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
 }
  14. Stick it Together #  Vars
 my_keys:
 -­‐  "ssh-­‐rsa  AA..."
 -­‐

     "ssh-­‐rsa  BB..."
 
 #  Task
 -­‐  authorized_keys:        user:  "app"
      key:  "{{  item  }}"
    with_items:  my_keys
  15. 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'  }
 )
  16. 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"],
 }
  17. Back to Playbooks -­‐  apt:          pkg:

     nginx          state:  present
 
 -­‐  service:          name:  nginx          enabled:  yes          state:  started package  {  "nginx":
  ensure  =>  present,
 }
 -­‐>
 service  {  "nginx":
  ensure  =>  started,
 }
  18. You may as well say: 
 C is simple
 because

    it uses
 letters, numbers and
 some symbols.
  19. 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.
  20. Dumb Syntax Tricks -­‐  copy:  >
        dest=/etc/motd


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


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


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


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


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

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

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

    needs to be a role that you then stick together in the meta/main.yml
  28. 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.
  29. Looping #  Vars
 my_keys:
  -­‐  user:  app
      key:

     "..."
    
 #  Task
 -­‐  authorized_keys:        user:  "{{  item.user  }}"
      key:  "{{  item.key  }}"
    with_items:  my_keys
  30. 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'  }
 )
  31. 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. :-)
  32. Single Declaration Package  {  "ntp":      ensure  =>  present,

      }
 
 #  ...
 
 SomeThing  {  "foo":      #  ...
    requires  =>  Package['ntp'],
 }
  33. 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
  34. • 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.
  35. • 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.