Ansible best current practices
-
Not SyncedThank you everyone for coming.
-
Not SyncedIf you were expecting the Postgres talk,
that was the one before, so -
Not Syncedyou might need to watch the video stream.
-
Not SyncedSo, Ansible best practices,
-
Not SyncedI thought about calling it "Ansible,
my best practices", -
Not Syncedso, just warning ahead, this is things
I stumbled on using Ansible -
Not Syncedfor the last 2-3 years and
-
Not Syncedthose are very specific things I found
that worked very well for me. -
Not SyncedAbout me, I do also freelance work,
do a lot of Ansible in there, -
Not SyncedI'm also the Debian maintainer for
Ansible with Harlan Lieberman-Berg -
Not SyncedIf there are any bugs in the package,
just report them. -
Not SyncedThe talk will be roughly divided into
4 parts. -
Not SyncedThe first part will be about why you
actually want to use config management -
Not Syncedand why you specifically want to use
Ansible. -
Not SyncedSo, if you're still SSHing into machines
and editing config files, -
Not Syncedyou're probably a good candidate
for using Ansible. -
Not SyncedThen, the second part will be about good
role and playbook patterns -
Not Syncedthat I have found that work really well
for me. -
Not SyncedThe third chapter will be about typical
antipatterns I've stumbled upon, -
Not Syncedeither in my work with other people
using Ansible, -
Not Syncedor the IRC support channel, for example.
-
Not SyncedThe fourth part will be like advanced
tips and tricks you can use -
Not Syncedlike fun things you can do with Ansible.
-
Not SyncedQuick elevator pitch, what makes config
management good? -
Not SyncedIt actually also serves as a documentation
of changes on your servers over time -
Not Syncedso if you just put the whole config
management in a git repo -
Not Syncedand just regularly commit,
-
Not Syncedyou will actually be able to say
-
Not Synced"Why doesn't this work? It used to work
a year ago" -
Not SyncedYou can actually check why.
-
Not SyncedAlso, most config management tools have
a lot better error reporting than -
Not Syncedyour self-written bash scripts that do
whatever. -
Not SyncedAnd usually, you have a very good
reproducibility with config management -
Not Syncedand also idempotency, meaning that if you
run, for example, a playbook several times -
Not Syncedyou will always get the same result.
-
Not SyncedAlso, it's great if you work in small team
or you admin ??? in the company -
Not Syncedand you have some people working
on a few things too. -
Not SyncedIt makes team work a lot easier and
you will save a lot of time actually -
Not Synceddebugging things when things break.
-
Not SyncedWhat makes Ansible good?
-
Not SyncedComparing it to Chef or Puppet for example
it's really easy to set up, -
Not Syncedyou start with two config files, you have
it installed and you're ready to go. -
Not SyncedIt's also agentless, so whatever machines
you actually want to control, -
Not Syncedthe only thing you they really need to have
is an SSH daemon and Python 2.6+ -
Not Syncedso that's virtually any Debian machine
you have installed and -
Not Syncedthat ???
-
Not SyncedAnsible also supports configuration
of many things like -
Not Syncednetworking equipment or even Windows
machines, -
Not Syncedthey don't need SSH but they use the
WinRM -
Not Syncedbut Ansible came a bit late to the game
so Ansible's still not as good -
Not Syncedin coverage like for example Puppet,
-
Not Syncedwhich literally, you can configure any
machine on the planet with that, -
Not Syncedas long as it has a CPU.
-
Not SyncedNext step, I will talk about good
role patterns. -
Not SyncedIf you've never worked with Ansible
before, -
Not Syncedthis is the point when you watch
the video stream, -
Not Syncedthat you pause it and start working
a few weeks with it -
Not Syncedand then unpause the actual video.
-
Not SyncedA good role should ideally have
the following layout. -
Not SyncedSo, in the "roles" directory, you have
the name of the role and task/main.yml -
Not SyncedYou have the following rough layout.
-
Not SyncedAt the beginning of the role, you check
for various conditions, -
Not Syncedfor example using the "assert" task to
for example check that -
Not Syncedcertain variables are defined, things
are set, -
Not Syncedthat it's maybe part of a group, things
like that you actually want to check. -
Not SyncedThen, usually, you install packages, you
can use apt on CentOS machines, -
Not Syncedyum, or you can do a git checkout or
whatever, -
Not Syncedthen usually you do some templating of
files where you have certain abstraction -
Not Syncedand the variables are actually put into
the template and -
Not Syncedmake the actual config file.
-
Not SyncedThere's also good to point out that
the template module actually has -
Not Synceda "validate" parameter,
-
Not Syncedthat means you can actually use a command
to check your config files for syntax errors -
Not Syncedand if that fails, your playbook will fail
before actually deploying that config file -
Not Syncedso you can for example use Apache with
the right parameters to actually do -
Not Synceda check on the syntax of the file.
-
Not SyncedThat way, you never end up with a state
where there's a broken config. -
Not SyncedIn the end, you usually…
-
Not SyncedWhen you change things, you trigger
handlers to restart any ??? -
Not SyncedIf you use variables, I recommend putting
sensible defaults in -
Not Synceddefaults/main.yml
-
Not Syncedand then you only have to override
those variables on specific cases. -
Not SyncedIdeally, you should have sensible defaults
you want to have to get whatever things -
Not Syncedyou want to have running.
-
Not SyncedWhen you start working with it and do that
a bit more, -
Not Syncedyou notice a few things and that is
-
Not Syncedyour role should ideally run in "check mode".
-
Not Synced"ansible-playbook" has --check that
basically is just a dry run of -
Not Syncedyour complete playbook
-
Not Syncedand with --diff, it will actually show you
for example file changes, -
Not Syncedor file mode changes, stuff like that
-
Not Syncedand won't actually change anything.
-
Not SyncedSo if you end up editing a lot of stuff,
you can use that as a check. -
Not SyncedI'll later get to some antipatterns that
actually break that thing. -
Not SyncedAnd, ideally, the way you change files
and configs and states, -
Not Syncedyou should make sure that when the actual
changes are deployed, -
Not Syncedand you run it a second time,
-
Not Syncedthat Ansible doesn't report any changes
-
Not Syncedbecause if you end up writing your roles
fairly sloppy, you end up having -
Not Synceda lot of changes and then,
-
Not Syncedin the end of the report, you have like
20 changes reported and -
Not Syncedyou kind of then know those 18,
they're always there -
Not Syncedand you kind of miss the 2 that are
important, that actually broke your system -
Not SyncedIf you want to do it really well, you make
sure that it doesn't report any changes -
Not Syncedwhen you run it twice in a row.
-
Not SyncedAlso, a thing to consider is you can define
variables in the "defaults" folder -
Not Syncedand also in the "vars" folder,
-
Not Syncedbut if you look up how variables get
inherited, you'll notice that -
Not Syncedthe "vars" folder is really hard to
actually override, -
Not Syncedso you want to avoid that as much as
possible. -
Not SyncedThat much larger section will be about
typical anti-patterns I've noticed -
Not Syncedand I'll come to the first one now.
-
Not SyncedIt's the shell or command module.
-
Not SyncedWhen 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 Syncedand then they end up using the shell module
to do just that. -
Not SyncedIf you use the shell module or the command
module, you usually don't want to use that -
Not Syncedand that's for several reasons.
-
Not SyncedThere's currently, I think, 1300 different
modules in Ansible -
Not Syncedso there's likely a big chance that
whatever you want to do, -
Not Syncedthere's already a module for that, that
just does that thing. -
Not SyncedBut those two modules also have several
problems and that is -
Not Syncedthe shell module, of course, gets
interpreted by your actual shell, -
Not Syncedso if you have any special variables
in there, -
Not Syncedyou'd actually also have to take care of
any variables you interpret in the shell string. -
Not SyncedThen, one of the biggest problems is if
you run your playbook in check mode, -
Not Syncedthe shell and the command modules
won't get run. -
Not SyncedSo if you're actually doing anything
with that, they just get skipped -
Not Syncedand that would cause that your actual
check mode and the real mode, -
Not Syncedthey will start diverging if you use
a lot of shell module. -
Not SyncedThe worst, also, bad part about this
is that these two modules, -
Not Syncedthey'll always ??? changed
-
Not Syncedlike, you run a command and it exits 0
-
Not Syncedit's like "Oh, it changed"
-
Not SyncedTo get the reporting right on that module,
you'd actually have to define for yourself -
Not Syncedwhen this is actually a change or not.
-
Not SyncedSo you'd have to probably get the output
and then check, for example, -
Not Syncedif there's something on stderr or something
to report an actual error or change. -
Not SyncedThen I'll get to the actual examples.
-
Not SyncedThe left is a bad example for using
the shell module, -
Not SyncedI'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 Syncedthe register parameter to get the output".
-
Not SyncedThe actual output goes into the "shell_cmd"
and then -
Not Syncedwe want to copy it to some other file
somewhere else and -
Not Syncedso we use the Jinja "{{ }}" to define
the actual content of the file -
Not Syncedand then put it into that destination file
-
Not SyncedThat is problematic because, first of all
if you run it in check mode, -
Not Syncedthis gets skipped and then this variable
is undefined and -
Not SyncedAnsible will fail with an error, so you
won't be able to actually -
Not Syncedrun that in check mode.
-
Not SyncedThe other problem is this will always
??? -
Not Syncedso you'd probably have to…
-
Not Syncedthe most sensible thing would probably
be to say just "changed when false" -
Not Syncedand just acknowledge that that shell
command won't change anything on this system -
Not SyncedThe good example would be to use the
actual "slurp" module that will -
Not Syncedjust slurp the whole file and base64encode it
-
Not Syncedand you can access the actual content with
"path_file.contents" and you then just -
Not Syncedbase64decode it and write in there.
-
Not SyncedThe nice thing is slurp will never return
any change, so it won't say it changed -
Not Syncedand it also works great in check mode.
-
Not SyncedHere's an other quick example.
-
Not SyncedThe example on the left, oh yeah wget.
-
Not SyncedHere's the problem, every time your playbook
runs, this file will get downloaded -
Not Syncedand of course if the file can't be
retrieved from that URL -
Not Syncedit will throw an error and that will
happen all the time. -
Not SyncedThe right example is a more clean example
using the uri module. -
Not SyncedYou define a URL to retrieve a file from,
you define where you want to write it to -
Not Syncedand 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 Syncedset_facts is a module that allows you
to define variables -
Not Syncedduring your playbook run, so you can say
set_facts and then -
Not Syncedthis variable = that variable + a third
variable or whatever -
Not Syncedyou can do things with that.
-
Not SyncedIt's very problematic, though, because
you end up having your variables -
Not Syncedchanged during the playbook run
-
Not Syncedand that is a problem when you use
the "--start-at" parameter -
Not Syncedfrom ansible-playbook.
-
Not SyncedBecause this parameter allows you to
skip forward to a certain task ??? -
Not Syncedso it skips everything until that point
and then continues running there -
Not Syncedand that's really great for debugging
-
Not Syncedbut if you define a variable with set_facts
and you skip over it, -
Not Syncedthat variable would just not be defined.
-
Not SyncedIf you heavily use set_facts, that makes
prototyping really horrible. -
Not SyncedAnother point is that you can use
-
Not Synced"ansible -m setup" and then the hostname
to check what variables are actually defined -
Not Syncedfor a specific host and everything set
with set_facts is just not there. -
Not SyncedIn summary, avoid the shell module,
avoid the command module, -
Not Syncedavoid set_facts as much as you can,
-
Not Syncedand don't hide changes with "changed_when"
-
Not Syncedso the clean approach is always to use one
task to check something -
Not Syncedand then a second task to actually execute
something for example. -
Not SyncedAlso, a bad idea in my opinion is when
people say -
Not Synced"Oh well, it's not important if this ???
-
Not SyncedI'll just say 'fails when false'"
-
Not SyncedThat might work sometimes, but the problem
there is, if something really breaks, -
Not Syncedyou'll never find out.
-
Not SyncedAdvanced topics.
-
Not SyncedThis is about the templating.
-
Not SyncedThe usual approach, for example for
postfix role, -
Not Syncedwould be to do the following templating.
-
Not SyncedYou define certain variables in for example
group_vars/postfix_servers -
Not Syncedso any host in that group would inherit
these variables, -
Not Syncedso this is sort of a list of parameters
for stmp recipient restrictions -
Not Syncedand this is just the smtp helo required.
-
Not SyncedSo the usual approach would be to
define variables -
Not Syncedin the host_vars or group_vars, or even
in the defaults -
Not Syncedand then you have a template where
you just check every single variable -
Not SyncedIf it exists, you actually sort of put
the actual value there in place. -
Not SyncedHere, I check if this variable is set true
and if yes, put the string there -
Not Syncedelse, put this string there
-
Not Syncedand for example, smtpd_recipient_restrictions
I just iterate over this array -
Not Syncedand just output these values in order
in that list. -
Not SyncedThe problem here is that every time
upstream defines a new variable -
Not Syncedyou'll end up having to change the actual
template file and touch the actual variables -
Not Syncedso, I thought, "Well, you actually have
keys and values and strings and arrays -
Not Syncedand arrays on one side, and actually,
a config file is nothing else than that, -
Not Syncedjust in a different format".
-
Not SyncedSo I came up with…
-
Not SyncedWith Jinja2, you can also define functions
-
Not SyncedI'll have to cut short a little bit on
explaining it but -
Not Syncedbasically, up here, a function is defined
and it's called here in the bottom -
Not SyncedBasically, what it just does, it iterates
over the whole dictionary defined here, -
Not Synced"postfix.main", and it just goes…
-
Not SyncedIt iterates over all the keys and values
and it goes… -
Not SyncedIf the value is a string, I'll just put
"key = value" and -
Not Syncedif it's an array, I just iterate over it
and put it there in the format that -
Not Syncedpostfix actually wants.
-
Not SyncedBasically, you can do the same, for
example, for haproxy and -
Not Syncedyou can just deserialize all the variables
you actually defined. -
Not SyncedThe advantages of this is,
-
Not Syncedyour template file just stays the same
and it doesn't get messy -
Not Syncedif you start adding things.
-
Not SyncedYou have complete whitespace control,
usually if you edit stuff, -
Not Syncedyou kind of get an extra space, a new
line in there, and that changes -
Not Syncedthe template files for all machines.
-
Not SyncedYou have all the settings in alphabetical
order, so if you actually run it and -
Not Syncedyou see the diff, you don't end up having
things going back and forth. -
Not SyncedIf you get the syntax on the template file
right, you don't have to touch it after that -
Not Syncedand you also don't get any syntax errors
by editing them. -
Not SyncedThat follows to the next one.
-
Not SyncedYou can actually set a "hash_behaviour"
merge in the Ansible config and -
Not Syncedthat allows you to do the following.
-
Not SyncedOn the left here, you define for example
a dictionary and this is, like, in a group -
Not Syncedand then in a specific machine, you define
an other setting in this dictionary. -
Not SyncedIf you wouldn't use merge, the second
setting would just override the first one -
Not Syncedand you'd end up with that, but if you
actually do the merge, -
Not Syncedit does a deep merge of the hash.
-
Not SyncedSo the previous thing I showed would
actually benefit from that -
Not Syncedso the combination of both is really good.
-
Not SyncedI'll skip that.
-
Not SyncedFor the resources, Ansible has just
a really good documentation, -
Not Syncedthere's the IRC and there's also debops
which is a project that is -
Not Syncedspecific to Debian and derivatives.
-
Not SyncedThat's it.
-
Not Synced[Applause]
-
Not SyncedThank 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 - Video Language:
- English
- Team:
Debconf
- Project:
- 2018_mini-debconf-hamburg
- Duration:
- 23:46
![]() |
tvincent edited English subtitles for Ansible best current practices | |
![]() |
tvincent edited English subtitles for Ansible best current practices | |
![]() |
tvincent edited English subtitles for Ansible best current practices | |
![]() |
tvincent edited English subtitles for Ansible best current practices | |
![]() |
tvincent edited English subtitles for Ansible best current practices |