Ansible Techniques I Wish I’d Known Earlier
zwischenzugs.com
zwischenzugs.com
The tips mentioned in the article are helpful, but still beyond the initial development of roles, maintaining roles is a pain no one can alleviate. It is the perl of automation. I wish people would stop using it. I've yet to start a job where people haven't regretted going towards ansible.
Neither of those of idempotent.
It seems likely you are coming at this from the perspective of "when I find an ansible playbook on github its hard to follow whats going on".
There is a certain truth to that, but much of its complexity is probably related to said playbook developers trying to abstract away specifics and create a playbook that works on a variety of distributions / configuration options.
Ansible can be as simple as a single file that proceeds sequentially like a bash script.
Most of the time (i.e. if your Bash script generates a known file), Ansible also gives you idempotency for free, thanks to the ’creates’ parameter.
Even with the standard library of predefined task, most devs don't put in the effort to learn them since "ifconfig | grep" and other commands are baked into their muscle memory, they would rather make a quick and dirty bash-step than figure out what is the best practice ansible-equivalent. In the end the result is a sequential shell script written in yaml with no guard rails keeping it idempotent, plus riddled with jinja template-substitutions making it a nightmare to follow.
A truly declarative system would be designed more like makefiles, really enforcing idempotence. With steps in playbooks being executed in sequence it's too easy to fall back to script-thinking and side-effects. Sadly i'm not aware of any.
> Neither of those of idempotent.
There is nothing magical about Ansible that makes it idempotent by default either.
The simple stuff like having specific versions of a package installed that you might be thinking of is just using Python under the hood. If you get into more complicated stuff, especially around custom infrastructure abstractions you use internally, guess what, you will have to write a bunch of Python and effectively call it from Ansible. And you will have to put in extra effort to make sure it's idempotent.
At which point you start thinking...why not just write a Python script instead? At least that gives you all of the flexibility.
No, I've worked with ansible over 3 jobs, for the past 4 years. It hasn't been hard to convince colleagues to move away from Ansible once I show the competition. Sadly the migration process is rarely easy.
We have playbooks installing and managing our databases, web servers, VoIP PBX, DNS servers, backend services, and on and on. All completely idempotent and safe to run at any time against any host. No special effort whatsoever was required to make that safe to do.
I suggest looking into the alternative, such as chef, puppet, saltstack etc. They allow for declarative configurations and make it a lot easier to make your configurations idempotent. On top of that, do the infrastructure itself (aws, gcp etc) in terraform (or pulumi if you want it in a programming language). Terraform has its issues, but it is certainly a lot better at managing infrastructure than ansible.
You want to use tools like Puppet and Terraform to define the state of your systems, and Ansible to run operations on those systems, because not everything is stateless; trying to upgrade a database with Puppet or Terraform will be painful, but Ansible won't have trouble.
I do wish it had static typing (Puppet took 4 major releases to finally get it and its type system is its best feature over alternatives) and less YAML, but it is what it is...
I've found Ansible good for quick one-liners across multiple nodes :)
Having used Puppet, Chef, Ansible, custom shell and Python scripts, and Terraform provisioners, I'll say there aren't really any "good" automation tools.
They each have their pros and a long list of cons.
The good thing is that a lot of garbage that I used to do with those tools has been replaced with K8s. Some Terraform and Ansible still survive for self-managed VMs, but that's about it.
Having said that, in an ideal scenario, you use auto scaling combined with cloud init to avoid having to manage live servers. Cattle, not pets. That way you can provision images using chef/puppet, and then have them deploy and be automatically deployed. It gives also gives you the opportunity to create self healing architecture that requires less baby sitting.
K8s certainly forces you to think closer to that architecture and that is good.
I dislike yaml, but I like Ansible. I think it hits the sweet spot for small and medium size enterprises, who are looking for a middle point between manually provisioning servers and containerisation.
Don't look at kubernetes then...
I integrated it to the repo, where I store my code and it just runs all the test cases for my code. It saved me a bunch of hours of investigation in test environments before I even released the code. Highly recommend to try it even for a small project.
Apart of that play with ansible strategies if you have more than one server to apply the role. It really might save you some minutes of a runtime.
No. F that. Back to shell scripts. Git clone and run "./install.sh". Most of the things I do are containerized anyway, and the heavy lifting is done by Dockerfile not Ansible, so the install.sh isn't actually that involved.
Hey Ansible if you're reading this, head over to https://google.com/chrome/. Make your front page look like that with a big obvious download button and no popups.
PS. Just out of curiosity I just tried "ansible install" in google and ddg and the very first link is pointing to an installation guide [0]. The guide gives a very complete installation instructions with multiple options for various systems. You literally can choose whatever fits your environment more.
[0]: https://docs.ansible.com/ansible/latest/installation_guide/i...
FWIW: Works just fine here on Debian (using it since Debian 8, now on Debian 11).
Whoever prefers configuration files over environment variables can get the same effect by adding to their ~/.ansible.cfg:
[defaults]
stdout_callback = yamlIn my case I workaround it by _merging_ all potential config files into one at runtime using `crudini` as demonstrated by rsguhr [3].
[1] https://github.com/ansible/proposals/issues/35
[2] https://docs.ansible.com/ansible/2.4/intro_configuration.htm...
Ansible and Puppet claim to be declarative but it's a lie. Configuration management really needs a rethink. I know Nix exists but that has a huge barrier to entry and companies don't want to use something that isn't battle tested. In my view, the ideal config management tool would be fast, testable, and provably idempotent. It would also be as far away as possible from the Python/Ruby swamp that Ansible and Puppet are stuck in.
Ansible is not declarative at all though, and it's not even funny how almost every fan says it is. It's just a sequence of actions.
The documentation sucks though imo, every different guide you find will show a completely different way of doing it
I don't think Ansible even tries to hide the fact that it's just imperative sequence of actions. I wonder where the people saying opposite are coming from. It's just retarded imperative language.
I can think of another horrifying example. We extract archives on to the machine to a specific target directory, and all files in that directory should have a specific owner and group. So we have a File resource that states the target directory exists which is ordered before the Archive resource. But we need to recursively chown after extraction too because tarballs preserve the original owner and group on extraction. We can't add a File resource with the same path that states the owner and group after extraction because duplicate resources aren't allowed. The only solution in this case is a hacky Exec. And that isn't declarative either because you'd have to check the owner/group of all files recursively to know whether it's in the right state or not. We just have this Exec run if the Archive resource refreshes. What if something else adds a file to this directory with the wrong owner/group? It wouldn't be corrected. Again, not declarative.
And god help you if a machine in the fleet runs out of disk space and resources only partially apply. The state will be completely fucked up and you'll have to manually nuke directories to correct it. Because Puppet only pretends to be declarative.
/rant :(
My one tip I'll add is to use the `--diff` argument when running a playbook to print out the specific changes made for each step. For debugging, use `--diff --check` to see all the changes that will be made - useful if it's been a while since you last run your playbook over a host.
How so? Do you mean you had trouble remembering what arguments to pass to the copy module? (I'm not sure what other options there are to be a pain)
Using the "state" parameter for files and directories is also rather odd. You specify "state: absent" to delete, "state: directory" to create a directory. It makes sense considering the declarative nature of Ansible, but it's unfamiliar to those used to writing shell scripts.
https://twitter.com/search?q=from%3Amoreati%20ansible%20tip&... https://twitter.com/search?q=from%3Amoreati%20ansible%20gotc...
Another trick I use routinely is this, to display a given variable across all hosts:
ANSIBLE_STDOUT_CALLBACK=json poetry run ansible all -m debug -a var=vault_ssl_cert | jq ".plays[0].tasks[0].hosts"
I still lack something that will tell me where a variable is sourced from.
What is the best way to couple Ansible with some git repository to automatically apply changes while still being able to trace and debug?
[0]: https://tmate.io/
It’s a good tool to set up a server from scratch to a desired state, but not to go between two versions of desired state. So a git-based deployment workflow is doomed for failure as soon as you start actually using the history (or removing config).
In that case, say you are using Ansible to set up a daemon A on a server. Now you want to remove the setup for daemon A and move to daemon B. Just changing the configuration is not enough (since nothing stops daemon A and cleans up it’s configuration). And after you’re in the new state (let’s say daemon B was just an experiment), if you decide to revert the commit that added daemon B and push, nothing will stop daemon B.
In the end it's also usually just easier to use Ansible to bake a new VM or container configuration and deploy that instead of mutating an existing one over and over again.
I don’t think reversing what you just did is as trivial as it should be. For a git-based workflow to work well, it should be as simple as reverting a commit.
Setting up a VM from scratch is where Ansible is great. A CI/CD that recreates the VM and applies the config would work really well with automatic git deployment, but that’s generally not how people use it.
Tooling can assist process, provide guard rails and reminders, but it's never going to replace remembering some important things. Having layers of process that mean that a single person forgetting is not going to cause disaster are necessary no matter what. Maybe when general AI exists we won't have to worry about remembering things, but then at best most of us will be out of jobs, at worst we'll be running from kill bots.
Don't get me wrong, there are plenty of design decisions in Ansible that regularly annoy me, but I've never expected it to make me less forgetful or substitute for people cooperating and communicating, and I think expecting any tool to do that is a road to disappointment.
For more than a few servers, other answers are probably better
AKA ansible-pull, which can greatly simplify the auth story since the machine only needs outbound access and auth to the repo (if required): https://docs.ansible.com/ansible/2.10/cli/ansible-pull.html