Literate DevOps
howardism.org
howardism.org
You may want to instead do:
vagrant ssh-config --host clientvm > $HOME/.ssh/config.vagrant
and add: Include config.vagrant
to ~/.ssh/config. Otherwise if you delete your VM and make a new one, the entry from the old one is going to stay in your main ssh config, so it's going to fill up with cruft over time. Include config.d/*The setup described in this post looks fragile, regardless of whether it's literate. I mean, we used to do these things, but we've moved on.
Inspired by https://github.blog/2015-06-30-scripts-to-rule-them-all/
Is the playbook always called "play.yml"?
And how do you handle multiple repositories (or is there only a sinlge one?)?
And no, it is not always called play.yml but according to its function, e.g. "install_foo.yml".
We typically have all our code for one customer in one repository.
And yes, one repository per configuration (per customer code).
Is it that you have one inventory per customer configuration? Or how do you manage multiple inventories in this setup?
We have different customers with different servers and applications. Every customer has its own repository with one or more inventories.
And how do you share the context / documentation which yaml file is intended to be called by which ansible utility with which utility parameters and switches?
I ask b/c this is normally the problem I run into when things grow and I found the suggestion in the OP interesting to this regard as it adds the context while implementing, not documenting afterwards (and such documentation often becomes stale). Also documentation is often declarative (like do this, then that etc.) and it does not show the original thoughts/ideas behind a certain utility invocation.
I'd be interested to learn a bit more here, so if you could share a bit of context from your end would be nice.
Like I said: Ideally there are no switches and parameters - the playbook should work as-is. If there are switches, we document them in our internal confluence. And yes, our docs get stale, too. That is a problem that we did not fix yet. :)
Many playbooks also get executed automatically, so there'S no need to remember parameters for them. You just start the CI-job that then runs the playbook.
Current services don't evwn allow for some of the bad practices of the past.
If it's just a few lines can you give an example?
- name: install the latest version of Apache and MariaDB
package:
name:
- httpd
- mariadb-server
state: latest package %w(httpd mariadb-server)Things that you haven't done before are harder than things you do all the time, and I would've reckoned trying to do them in ansible is much harder than doing it interactively, but I'd be interested in learning.
If I find an article like this[1], how do you translate it into the artefact I would want to keep in source control? In org-babel it's this[2], so what would that look like in ansible?
[1]: https://www.digitalocean.com/community/tutorials/how-to-set-...
[2]: http://www.howardism.org/Technical/Emacs/linux-iptables.org....
Once you've done it and you've documented it, I can see how you might translate that into ansible (or chef or whatever), but that's a different thing.
Using an iptables module for Ansible it is straightforward to write the rules as yaml. The documentation has a clear example.
However, having used several of these languages for over a decade and realizing this is a contentious issue within the community, I would actually avoid using these tools for individual rules. What I would do is dump the rules to a file, distribute the file, and let Ansible make sure the file matches what is running (by way of a regular rule load when necessary).
The idea here is that I am already familiar with iptables rules and how to write them, and would expect any other ops-ish person to be the same. The source file matches the output, and any historical diffs will be much more straightforward to read, as there are no intermediary source formats that can change.
Also, there is one less Ansible dependency involved, and less syntax to learn (given that one can already read iptables rules).
Are you saying that everything (or almost everything) worth automating is already automated by someone else so learning how to do new things isn't important?
That's not a name, that an intention describing comment, why isn't it called that way (or for brevity's sake, just "comment")? That might be nit-picking, but IMHO such misnomer cause unnecessary confusion and at the very least make the tool harder to learn.
This looks to be a step-up from just manually tinkering with commands in some shell to figure out or explore what it is you need to declare.
Sometimes it's a lot easier to stay closer to the metal for figuring out what you actually need, then break out the declarative automation to make it truly reproducible.
There's one thing ansible/salt are good at: make it easy for cheap replaceable devops to write tons of repetitive boilerplate yaml. Harder for them to shoot themselves in the foot than with a real programming language. But once you descend into jinja hell that's not very true either.
Also: newer isn't always better and all that.
The best example I’ve come across is building software from source. When there’s no package, you have to do this. Some may argue that this is not Ansible’s job, but I don’t see how it’s different from `apt install`, nor where else it would fit into the pipeline. Anyway, the way I solved this for example was by pinning the idempotency on the existence of build artefacts. Those are often software-specific, so it’s quite fiddly/non-general to find the right artefact.
https://github.com/wtsi-hgi/hgi-systems/blob/master/ansible/...
The replies to my original post say this is not something Ansible should be doing. Fair enough :P I was young!
Package it as a deb as part of the CI in say jenkins and then apt install it with ansible.
http://reclass.pantsfullofunix.net/index.html helped a lit but still not perfect
You can break it by app, like, having many git repos one per app and then use ansible galaxy to reuse code
Or you can try having a dynamic inventory that pulls metadata from say consul.
We’re in the ballpark of 2k machines and still happy !
Basically, it's better to stick with tasks that stand on their own. As soon as you stick complex task logic in it becomes kind of a Rube Goldberg machine. That being said, you can pull off quite a bit if you just test rigorously and write defensively.
On the other hand, in imperative world you have other difficulties, because you have to check the current state first and decide what to do in different edge cases. Not sure which one in the would be more complex.
Puppet lays down those things that the platform "promises"* to provide - syslog, time, auth, DNS, etc, and Ansible does application-specific things.
* - Not a strict promise in the Mark Burgess "Promise Theory" sense, but similar in thought.
Simple clean architectures can be deployed with anything. I judge a tool by it's ability to "make the easy things easy, and the hard things possible".
But you do have a very good point here:
> the old ways are largely pointless for most clients and developers who have long left behind artisan infrastructure
I don't like that it is this way, maybe I'm just old fashioned. But yes. This is the way it is.
Automation is the right course, but this takes a very roundabout way toward producing an artifact that can be distributed and applied repeatably, compared to using something like an Ansible playbook that is applied using Virtualbox's built-in Ansible provisioner.
I would recommend anyone reading this thread against recreating this, except as a proof of concept, and instead concentrate on a workflow more similar to this one:
https://medium.com/faun/building-repeatable-infrastructure-w...
Modern declarative automation is great for a lot of things, especially at work and for CI/CD pipelines, but it's no substitute for learning what you need to do to reproduce the same setup by hand.
Ansible was written before we realized immutable infrastructure is the best way to go. Configuration Management tools craft system state dynamically like a drunk sculpting a Roman bust with a Louisville Slugger. You can get them to do what you want, but it takes a lot of work, and even then the outcome is uncertain. CM tools are often complex because a system whose state is constantly shifting requires complexity to handle it.
If instead you make immutable artifacts, you don't need complexity. A simple series of straight-forward commands in a single version-controlled file does everything you need. Dockerfiles seem immature at first because of how simple they are, but in practice it's much more reliable than Ansible, not to mention easier to support. Thus, non-declarative automation, when used with immutable infrastructure as code, trumps declarative.
Ansible is also only optionally declarative, which people miss just because it has that bastardized form of YAML for a config file. The simplest tasks, roles and playbooks work when executed sequentially, but not necessarily when out of sequential order. When you do make it super-duper-declarative, it can involve tons of confusing logic that makes it nearly impossible to understand, and is much more verbose (and complex) than a simple script. As soon as automation is more work and cost than the alternative, it should be ditched.
Rather than embed snippets, write a single simple script that automates your steps, and create an immutable artifact. The simplest way to do this is to use Docker with a base image that mimics the target system, and performs all necessary steps in one go. This allows you to perform a 'test run' in an empty container to make sure the steps work. And you can embed in-line documentation and generate it with Doxygen or some other tool. (I highly recommend you get all your teams to standardize on a tool like this, and use it literally everywhere)
(Also, pet peeve, but what does DevOps have to do with this? Can we stop over-using this term please? Not everything that's "sysadminny" is DevOps)
Keeps comments close to the actual code, too.
Or better yet, run all your code in docker containers so you can run arbitrary slices of your production environment on your local machine.
You’ll eventually hit some host-OS network issue where you need to inspect the machine that’s running your containers, but for artifacts/RPMs as described in this doc you should be able to do everything locally.
I use Markdown notes files with quoted commands and outputs as first exploratory documentation and before it goes into Ansible/Dockerfile/Packer/Whatever. I can add links, images etc and anybody can easily follow and edit/PR since well, it's Markdown.
Seems to me what I'm doing is basically the same idea as this, with total flexibility (no schema), nothing new to learn only I can't run the .md file (how hard would it be to parse a md file and ignore everything except the triple ticks and execute that, in say Bash?).
```js
console.log('yo, sup')
```
or ```#!/usr/bin/env node
console.log('yo, sup')
```
That way, your md files could be both executable/interactive and be flexible in regards to their execution environment (as long as you are on a unix-like). Trying to resist the urge to go make a VSCode extension to do this right now.- you can weave this file to one or several code files, even export the plain text parts as comment on the language if you decide so.
- you can execute the code directly from your local machine into one, or different remote machines.
- you can use the output from a previous block inside another block, even preprocess it on another language before executing in another block.
- you can get the results recorded on the same file you're executing so you can also document what would the results be for executing x command.
- if you use Emacs as another tool, you can easily send the results for example on email, slack, irc or whatever system you can interact with on Emacs. I think confluence made it harder on recent versions, but previously I was able to generate documentation pages and easily upload them to confluence without worrying about format or even upload.
I never got good at tangling my blocks together in the end though which is what I think would have made this method really powerful.
I'm expressing the wish that were I better at organizing them better that I could have tangled all of those snippets into a working (if ugly) source file and work from there.
Does that make sense?
It is, but if the process and tooling isn't really polished and responsive it feels really bad.
When everything is smooth though, it might be ideal. Jury still out on that one.
Start every piece of work in a markdown with vim. and proceed to dump my thoughts, what I have tried, what did work and what didn't. Also any useful links that I've found useful.
And as they say "You only learn once you reflect", this proved invaluable for me going forward when faced with "ah. I've done that before but don't quite recall the details"
I move feel this to be like literate/log driven development
Filed under things that have been true since the 1970's if not before.
Most of my org-babel stuff is running AWS commands and collating the results into tables for more documentation. I write my Terraform code outside of org-babel.
https://github.com/joelmccracken/workstation/blob/master/wor...
https://github.com/fastai/nbdev
I think it's a very underrated way of programming that can have a great impact in the future.
It's both amazing and terrifying to think about.
Things are only as hard as we make them.
Doesn't it? I think that there are plenty of folks who want to make things more complicated, because complicated things are fun, while just making things work can be a pain but also boring.
The tool is excellent and extremely well-conceived. Being able to spin up a highly scalable fault-tolerant system with a few manifest files is impressive. The only frustration I ran into was trying to understand which files I needed when vs. what was unnecessary and intended for a special use case.
Edit: Be gentle on the downvotes ... I was clearly trying to be funny.
You don't have to use Emacs for everything just because you use a certain application of it.
I haven't tried this specifically with org-babel, and as other commenters in the thread state, Terraform/Ansible may not lend themselves to org-babel quite so easily, but it's certainly possible. Although maybe not worth the effort unless you really push hard for a team to document their infrastructure code in this way.
I've been using guile and noweb, with a tangle I wrote myself, for literate devops on top of Guix and it works better than anything else I've tried.
One of Org's unique advantages is the pliability it has by virtue of running in Emacs. I doubt whether someone who was forced to use it would have the intrinsic motivation to climb the notoriously steep learning curves for both Elisp and Emacs. Subtract Elisp (and with it, the ability to do any debugging or customization) and you're left with stock Org mode running in an inferior text-editor.
Maybe stock Org is still enough of an improvement over Markdown that it's worth the overhead of learning Emacs, but I'd have a hard time making that pitch to my coworkers.
Emacs is absolutely suitable for the use case of a professional developer writing these things.
The literate programming notebook approach is very interesting. Better IMO is to reverse it and write copious comments throughout an idempotent script that you can continually edit and re-execute.
In either case, the final product that you share with others are your notes and an actual deployed configuration for a real system, not a config file for an IDE.