Return to Video

Ansible best current practices

  • Not Synced
    Thank you everyone for coming.
  • Not Synced
    If you were expecting the Postgres talk,
    that was the one before, so
  • Not Synced
    you might need to watch the video stream.
  • Not Synced
    So, Ansible best practices,
  • Not Synced
    I thought about calling it "Ansible,
    my best practices",
  • Not Synced
    so, just warning ahead, this is things
    I stumbled on using Ansible
  • Not Synced
    for the last 2-3 years and
  • Not Synced
    those are very specific things I found
    that worked very well for me.
  • Not Synced
    About me, I do also freelance work,
    do a lot of Ansible in there,
  • Not Synced
    I'm also the Debian maintainer for
    Ansible with Harlan Lieberman-Berg
  • Not Synced
    If there are any bugs in the package,
    just report them.
  • Not Synced
    The talk will be roughly divided into
    4 parts.
  • Not Synced
    The first part will be about why you
    actually want to use config management
  • Not Synced
    and why you specifically want to use
    Ansible.
  • Not Synced
    So, if you're still SSHing into machines
    and editing config files,
  • Not Synced
    you're probably a good candidate
    for using Ansible.
  • Not Synced
    Then, the second part will be about good
    role and playbook patterns
  • Not Synced
    that I have found that work really well
    for me.
  • Not Synced
    The third chapter will be about typical
    antipatterns I've stumbled upon,
  • Not Synced
    either in my work with other people
    using Ansible,
  • Not Synced
    or the IRC support channel, for example.
  • Not Synced
    The fourth part will be like advanced
    tips and tricks you can use
  • Not Synced
    like fun things you can do with Ansible.
  • Not Synced
    Quick elevator pitch, what makes config
    management good?
  • Not Synced
    It actually also serves as a documentation
    of changes on your servers over time
  • Not Synced
    so if you just put the whole config
    management in a git repo
  • Not Synced
    and just regularly commit,
  • Not Synced
    you will actually be able to say
  • Not Synced
    "Why doesn't this work? It used to work
    a year ago"
  • Not Synced
    You can actually check why.
  • Not Synced
    Also, most config management tools have
    a lot better error reporting than
  • Not Synced
    your self-written bash scripts that do
    whatever.
  • Not Synced
    And usually, you have a very good
    reproducibility with config management
  • Not Synced
    and also idempotency, meaning that if you
    run, for example, a playbook several times
  • Not Synced
    you will always get the same result.
  • Not Synced
    Also, it's great if you work in small team
    or you admin ??? in the company
  • Not Synced
    and you have some people working
    on a few things too.
  • Not Synced
    It makes team work a lot easier and
    you will save a lot of time actually
  • Not Synced
    debugging things when things break.
  • Not Synced
    What makes Ansible good?
  • Not Synced
    Comparing it to Chef or Puppet for example
    it's really easy to set up,
  • Not Synced
    you start with two config files, you have
    it installed and you're ready to go.
  • Not Synced
    It's also agentless, so whatever machines
    you actually want to control,
  • Not Synced
    the only thing you they really need to have
    is an SSH daemon and Python 2.6+
  • Not Synced
    so that's virtually any Debian machine
    you have installed and
  • Not Synced
    that ???
  • Not Synced
    Ansible also supports configuration
    of many things like
  • Not Synced
    networking equipment or even Windows
    machines,
  • Not Synced
    they don't need SSH but they use the
    WinRM
  • Not Synced
    but Ansible came a bit late to the game
    so Ansible's still not as good
  • Not Synced
    in coverage like for example Puppet,
  • Not Synced
    which literally, you can configure any
    machine on the planet with that,
  • Not Synced
    as long as it has a CPU.
  • Not Synced
    Next step, I will talk about good
    role patterns.
  • Not Synced
    If you've never worked with Ansible
    before,
  • Not Synced
    this is the point when you watch
    the video stream,
  • Not Synced
    that you pause it and start working
    a few weeks with it
  • Not Synced
    and then unpause the actual video.
  • Not Synced
    A good role should ideally have
    the following layout.
  • Not Synced
    So, in the "roles" directory, you have
    the name of the role and task/main.yml
  • Not Synced
    You have the following rough layout.
  • Not Synced
    At the beginning of the role, you check
    for various conditions,
  • Not Synced
    for example using the "assert" task to
    for example check that
  • Not Synced
    certain variables are defined, things
    are set,
  • Not Synced
    that it's maybe part of a group, things
    like that you actually want to check.
  • Not Synced
    Then, usually, you install packages, you
    can use apt on CentOS machines,
  • Not Synced
    yum, or you can do a git checkout or
    whatever,
  • Not Synced
    then usually you do some templating of
    files where you have certain abstraction
  • Not Synced
    and the variables are actually put into
    the template and
  • Not Synced
    make the actual config file.
  • Not Synced
    There's also good to point out that
    the template module actually has
  • Not Synced
    a "validate" parameter,
  • Not Synced
    that means you can actually use a command
    to check your config files for syntax errors
  • Not Synced
    and if that fails, your playbook will fail
    before actually deploying that config file
  • Not Synced
    so you can for example use Apache with
    the right parameters to actually do
  • Not Synced
    a check on the syntax of the file.
  • Not Synced
    That way, you never end up with a state
    where there's a broken config.
  • Not Synced
    In the end, you usually…
  • Not Synced
    When you change things, you trigger
    handlers to restart any ???
  • Not Synced
    If you use variables, I recommend putting
    sensible defaults in
  • Not Synced
    defaults/main.yml
  • Not Synced
    and then you only have to override
    those variables on specific cases.
  • Not Synced
    Ideally, you should have sensible defaults
    you want to have to get whatever things
  • Not Synced
    you want to have running.
  • Not Synced
    When you start working with it and do that
    a bit more,
  • Not Synced
    you notice a few things and that is
  • Not Synced
    your role should ideally run in "check mode".
  • Not Synced
    "ansible-playbook" has --check that
    basically is just a dry run of
  • Not Synced
    your complete playbook
  • Not Synced
    and with --diff, it will actually show you
    for example file changes,
  • Not Synced
    or file mode changes, stuff like that
  • Not Synced
    and won't actually change anything.
  • Not Synced
    So if you end up editing a lot of stuff,
    you can use that as a check.
  • Not Synced
    I'll later get to some antipatterns that
    actually break that thing.
  • Not Synced
    And, ideally, the way you change files
    and configs and states,
  • Not Synced
    you should make sure that when the actual
    changes are deployed,
  • Not Synced
    and you run it a second time,
  • Not Synced
    that Ansible doesn't report any changes
  • Not Synced
    because if you end up writing your roles
    fairly sloppy, you end up having
  • Not Synced
    a lot of changes and then,
  • Not Synced
    in the end of the report, you have like
    20 changes reported and
  • Not Synced
    you kind of then know those 18,
    they're always there
  • Not Synced
    and you kind of miss the 2 that are
    important, that actually broke your system
  • Not Synced
    If you want to do it really well, you make
    sure that it doesn't report any changes
  • Not Synced
    when you run it twice in a row.
  • Not Synced
    Also, a thing to consider is you can define
    variables in the "defaults" folder
  • Not Synced
    and also in the "vars" folder,
  • Not Synced
    but if you look up how variables get
    inherited, you'll notice that
  • Not Synced
    the "vars" folder is really hard to
    actually override,
  • Not Synced
    so you want to avoid that as much as
    possible.
  • Not Synced
    That much larger section will be about
    typical anti-patterns I've noticed
  • Not Synced
    and I'll come to the first one now.
  • Not Synced
    It's the shell or command module.
  • Not Synced
    When people start using Ansible, that's
    the first thing they go
  • Not Synced
    "Oh well, I know how to use wget or I know
    'apt-get install' "
  • Not Synced
    and then they end up using the shell module
    to do just that.
  • Not Synced
    If you use the shell module or the command
    module, you usually don't want to use that
  • Not Synced
    and that's for several reasons.
  • Not Synced
    There's currently, I think, 1300 different
    modules in Ansible
  • Not Synced
    so there's likely a big chance that
    whatever you want to do,
  • Not Synced
    there's already a module for that, that
    just does that thing.
  • Not Synced
    But those two modules also have several
    problems and that is
  • Not Synced
    the shell module, of course, gets
    interpreted by your actual shell,
  • Not Synced
    so if you have any special variables
    in there,
  • Not Synced
    you'd actually also have to take care of
    any variables you interpret in the shell string.
  • Not Synced
    Then, one of the biggest problems is if
    you run your playbook in check mode,
  • Not Synced
    the shell and the command modules
    won't get run.
  • Not Synced
    So if you're actually doing anything
    with that, they just get skipped
  • Not Synced
    and that would cause that your actual
    check mode and the real mode,
  • Not Synced
    they will start diverging if you use
    a lot of shell module.
  • Not Synced
    The worst, also, bad part about this
    is that these two modules,
  • Not Synced
    they'll always ??? changed
  • Not Synced
    like, you run a command and it exits 0
  • Not Synced
    it's like "Oh, it changed"
  • Not Synced
    To get the reporting right on that module,
    you'd actually have to define for yourself
  • Not Synced
    when this is actually a change or not.
  • Not Synced
    So you'd have to probably get the output
    and then check, for example,
  • Not Synced
    if there's something on stderr or something
    to report an actual error or change.
  • Not Synced
    Then I'll get to the actual examples.
  • Not Synced
    The left is a bad example for using
    the shell module,
  • Not Synced
    I've seen that a lot, it's basically
  • Not Synced
    "Yeah, I actually want this file, so just
    use 'cat /path/file' and I'll use
  • Not Synced
    the register parameter to get the output".
  • Not Synced
    The actual output goes into the "shell_cmd"
    and then
  • Not Synced
    we want to copy it to some other file
    somewhere else and
  • Not Synced
    so we use the Jinja "{{ }}" to define
    the actual content of the file
  • Not Synced
    and then put it into that destination file
  • Not Synced
    That is problematic because, first of all
    if you run it in check mode,
  • Not Synced
    this gets skipped and then this variable
    is undefined and
  • Not Synced
    Ansible will fail with an error, so you
    won't be able to actually
  • Not Synced
    run that in check mode.
  • Not Synced
    The other problem is this will always
    ???
  • Not Synced
    so you'd probably have to…
  • Not Synced
    the most sensible thing would probably
    be to say just "changed when false"
  • Not Synced
    and just acknowledge that that shell
    command won't change anything on this system
  • Not Synced
    The good example would be to use the
    actual "slurp" module that will
  • Not Synced
    just slurp the whole file and base64encode it
  • Not Synced
    and you can access the actual content with
    "path_file.contents" and you then just
  • Not Synced
    base64decode it and write in there.
  • Not Synced
    The nice thing is slurp will never return
    any change, so it won't say it changed
  • Not Synced
    and it also works great in check mode.
  • Not Synced
    Here's an other quick example.
  • Not Synced
    The example on the left, oh yeah wget.
  • Not Synced
    Here's the problem, every time your playbook
    runs, this file will get downloaded
  • Not Synced
    and of course if the file can't be
    retrieved from that URL
  • Not Synced
    it will throw an error and that will
    happen all the time.
  • Not Synced
    The right example is a more clean example
    using the uri module.
  • Not Synced
    You define a URL to retrieve a file from,
    you define where you want to write it to
  • Not Synced
    and you use the "creates" parameter to say
  • Not Synced
    "Just skip the whole thing if the file is
    already there".
  • Not Synced
    "set_facts", that's my pet peeve.
  • Not Synced
    set_facts is a module that allows you
    to define variables
  • Not Synced
    during your playbook run, so you can say
    set_facts and then
  • Not Synced
    this variable = that variable + a third
    variable or whatever
  • Not Synced
    you can do things with that.
  • Not Synced
    It's very problematic, though, because
    you end up having your variables
  • Not Synced
    changed during the playbook run
  • Not Synced
    and that is a problem when you use
    the "--start-at" parameter
  • Not Synced
    from ansible-playbook.
  • Not Synced
    Because this parameter allows you to
    skip forward to a certain task ???
  • Not Synced
    so it skips everything until that point
    and then continues running there
  • Not Synced
    and that's really great for debugging
  • Not Synced
    but if you define a variable with set_facts
    and you skip over it,
  • Not Synced
    that variable would just not be defined.
  • Not Synced
    If you heavily use set_facts, that makes
    prototyping really horrible.
  • Not Synced
    Another point is that you can use
  • Not Synced
    "ansible -m setup" and then the hostname
    to check what variables are actually defined
  • Not Synced
    for a specific host and everything set
    with set_facts is just not there.
  • Not Synced
    In summary, avoid the shell module,
    avoid the command module,
  • Not Synced
    avoid set_facts as much as you can,
  • Not Synced
    and don't hide changes with "changed_when"
  • Not Synced
    so the clean approach is always to use one
    task to check something
  • Not Synced
    and then a second task to actually execute
    something for example.
  • Not Synced
    Also, a bad idea in my opinion is when
    people say
  • Not Synced
    "Oh well, it's not important if this ???
  • Not Synced
    I'll just say 'fails when false'"
  • Not Synced
    That might work sometimes, but the problem
    there is, if something really breaks,
  • Not Synced
    you'll never find out.
  • Not Synced
    Advanced topics.
  • Not Synced
    This is about the templating.
  • Not Synced
    The usual approach, for example for
    postfix role,
  • Not Synced
    would be to do the following templating.
  • Not Synced
    You define certain variables in for example
    group_vars/postfix_servers
  • Not Synced
    so any host in that group would inherit
    these variables,
  • Not Synced
    so this is sort of a list of parameters
    for stmp recipient restrictions
  • Not Synced
    and this is just the smtp helo required.
  • Not Synced
    So the usual approach would be to
    define variables
  • Not Synced
    in the host_vars or group_vars, or even
    in the defaults
  • Not Synced
    and then you have a template where
    you just check every single variable
  • Not Synced
    If it exists, you actually sort of put
    the actual value there in place.
  • Not Synced
    Here, I check if this variable is set true
    and if yes, put the string there
  • Not Synced
    else, put this string there
  • Not Synced
    and for example, smtpd_recipient_restrictions
    I just iterate over this array
  • Not Synced
    and just output these values in order
    in that list.
  • Not Synced
    The problem here is that every time
    upstream defines a new variable
  • Not Synced
    you'll end up having to change the actual
    template file and touch the actual variables
  • Not Synced
    so, I thought, "Well, you actually have
    keys and values and strings and arrays
  • Not Synced
    and arrays on one side, and actually,
    a config file is nothing else than that,
  • Not Synced
    just in a different format".
  • Not Synced
    So I came up with…
  • Not Synced
    With Jinja2, you can also define functions
  • Not Synced
    I'll have to cut short a little bit on
    explaining it but
  • Not Synced
    basically, up here, a function is defined
    and it's called here in the bottom
  • Not Synced
    Basically, what it just does, it iterates
    over the whole dictionary defined here,
  • Not Synced
    "postfix.main", and it just goes…
  • Not Synced
    It iterates over all the keys and values
    and it goes…
  • Not Synced
    If the value is a string, I'll just put
    "key = value" and
  • Not Synced
    if it's an array, I just iterate over it
    and put it there in the format that
  • Not Synced
    postfix actually wants.
  • Not Synced
    Basically, you can do the same, for
    example, for haproxy and
  • Not Synced
    you can just deserialize all the variables
    you actually defined.
  • Not Synced
    The advantages of this is,
  • Not Synced
    your template file just stays the same
    and it doesn't get messy
  • Not Synced
    if you start adding things.
  • Not Synced
    You have complete whitespace control,
    usually if you edit stuff,
  • Not Synced
    you kind of get an extra space, a new
    line in there, and that changes
  • Not Synced
    the template files for all machines.
  • Not Synced
    You have all the settings in alphabetical
    order, so if you actually run it and
  • Not Synced
    you see the diff, you don't end up having
    things going back and forth.
  • Not Synced
    If you get the syntax on the template file
    right, you don't have to touch it after that
  • Not Synced
    and you also don't get any syntax errors
    by editing them.
  • Not Synced
    That follows to the next one.
  • Not Synced
    You can actually set a "hash_behaviour"
    merge in the Ansible config and
  • Not Synced
    that allows you to do the following.
  • Not Synced
    On the left here, you define for example
    a dictionary and this is, like, in a group
  • Not Synced
    and then in a specific machine, you define
    an other setting in this dictionary.
  • Not Synced
    If you wouldn't use merge, the second
    setting would just override the first one
  • Not Synced
    and you'd end up with that, but if you
    actually do the merge,
  • Not Synced
    it does a deep merge of the hash.
  • Not Synced
    So the previous thing I showed would
    actually benefit from that
  • Not Synced
    so the combination of both is really good.
  • Not Synced
    I'll skip that.
  • Not Synced
    For the resources, Ansible has just
    a really good documentation,
  • Not Synced
    there's the IRC and there's also debops
    which is a project that is
  • Not Synced
    specific to Debian and derivatives.
  • Not Synced
    That's it.
  • Not Synced
    [Applause]
  • Not Synced
    Thank you very much.
Title:
Ansible best current practices
Description:

Talk given by Lee Garrett at Minidebconf Hamburg 2018
https://meetings-archive.debian.net/pub/debian-meetings/2018/miniconf-hamburg/2018-05-20/ansible_bcp.webm

more » « less
Video Language:
English
Team:
Debconf
Project:
2018_mini-debconf-hamburg
Duration:
23:46

English subtitles

Incomplete

Revisions Compare revisions