Slide 1

Slide 1 text

ANSIBLE A Puppet User's Perspective

Slide 2

Slide 2 text

What Ansible is.

Slide 3

Slide 3 text

Ansible is an
 open-source software platform for [remotely] configuring and managing computers. “

Slide 4

Slide 4 text

Basic Bits

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Basic Bits • "Push" model;
 Control Machine
 talking to
 Dumb Clients
 via SSH. ➡ "Pull" model; Puppet Agents
 talking back to
 Puppet Master
 via SSL.

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Basic Bits • Tasks • Inventories • Variables • Handlers • Playbooks • Roles ➡ Tasks ➡ Nodes + Roles ➡ Variables (Hiera) ➡ notify  =>  ...   ➡ Manifests (ish) ➡ Classes + Profiles

Slide 9

Slide 9 text

Tasks -­‐  apt:          pkg:  nginx          state:  present package  {  "nginx":
  ensure  =>  present,
 }

Slide 10

Slide 10 text

Tasks -­‐  service:          name:  nginx          enabled:  yes          state:  started service  {  "nginx":
  ensure  =>  started,
  requires  =>
    Package["nginx"],
 }

Slide 11

Slide 11 text

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"],
 }

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Inventories [nameservers]
 ns01
 ns02
 
 [apps]
 app01
 app02
 
 [staging:children]
 nameservers
 apps • Node definition blocks • Node classifiers / Roles • Node definitions + Environments

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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']
 }

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Playbooks -­‐  apt:          pkg:  nginx          state:  present
 
 -­‐  service:          name:  nginx          enabled:  yes          state:  started package  {  "nginx":
  ensure  =>  present,
 }
 -­‐>
 service  {  "nginx":
  ensure  =>  started,
 }

Slide 19

Slide 19 text

Playbooks -­‐  hosts:  all      roles:      -­‐  common
 
 -­‐  include:  blah.yml
 
 -­‐  hosts:  nameservers
    roles:
    -­‐  nameserver node  /^ns\d+/  {
  include  common
  include
    profiles::nameservers
 }

Slide 20

Slide 20 text

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
 }

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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'  }
 )

Slide 23

Slide 23 text

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"],
 }

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Dependencies:
 Implicit vs Explicit

Slide 26

Slide 26 text

Dependencies:
 Implicit vs Explicit (Ansible) (Puppet)

Slide 27

Slide 27 text

Ordering:
 Implicit vs Explicit

Slide 28

Slide 28 text

Ordering:
 Implicit vs Explicit (Puppet) (Ansible)

Slide 29

Slide 29 text

What Ansible isn't.

Slide 30

Slide 30 text

Ansible is simple because it uses YAML. “

Slide 31

Slide 31 text

You may as well say: 
 C is simple
 because it uses
 letters, numbers and
 some symbols.

Slide 32

Slide 32 text

Ansible is
 "Infrastructure as Data",
 not
 "Infrastructure as Code". “

Slide 33

Slide 33 text

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.

Slide 34

Slide 34 text

Ansible Oddities

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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.

Slide 45

Slide 45 text

Puppet Problems

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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'  }
 )

Slide 48

Slide 48 text

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. :-)

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

• 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.

Slide 52

Slide 52 text

• 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.

Slide 53

Slide 53 text

Fin. 
 Rob Howard
 @damncabbage https://speakerdeck.com/damncabbage/