This really culminates the articles "Making this site" (part 1, part 2 and part 3), surrounding the development of this blog. I've been terrible at getting round to doing this for a whole host of reasons, not least due to the onset of lockdown part two!
Ansible is a tool I've been using for years, almost as long as I've been doing devops style stuff over "plain old" software development. Alas, I've never been as involved as some in the development of it as it's moved from strength to strength. That said, one thing I've never seen a lot of is people exposing their ansible infrastructure or the methodologies behind it. I can, for sure, say that some of the influences for mine have come from implementations I've worked on for massive infrastructures, but I feel compelled to open mine up as an example people can use.
This too is true for this site, though the Lektor structure I have put together is very rudimentary from what I've seen around. There are much better examples, but it's a work in progress. Therefore this article focuses on the ansible setup I use to deploy the site, and from here on out it's just going to be an incremental process as the passage of time flows on!
The point of having a provisioning repository was to make it shareable. The repository is intended as a top level container that links together the full orchestration process via use of the Invoke tool. However, I've never quite finished automating this too well, but it does contain everything nicely.
Please do forgive how loosely everything is put together. If you follow instructions or inspect it, you should find some method to the madness, but this is very much just what was required for recreational usage...
We have the following:
The idea is that you always invoke tasks from the this base directory. The only time you need to go anywhere else is to copy kickstarts to an http server (you only do that to begin with) and to edit stuff.
Each of the following sections goes through these areas of functionality in order...
There were two originally drafted methods of deploying my online presence.
The first was originally to set up a libvirt host using an ansible role with a set of VMs. This used to make sense when I used a physical server in a data center, but it didn't work so well with a VPS since they didn't provide virtualisation extensions when I moved host (lesson learnt, but it's way cheaper.) In the end, I redeveloped the ansible to make both options available, though you should consider the vps approach the only mature one.
This is not very mature, but I use it as an easy proxy for running ansible-playbook. Basically, I can run any playbook with some structure via the following:
invoke ansible -e <env> [-t <tag(s)>] playbook
The point here is that it automates vault inclusion and saves me the hassle of doing too much manipulating the commands I want to run. "Is it worth it" I hear you ask? Well, it certainly is if you use this same approach on massive infrastructures, as you can automate injection of parameters via invoke to drive the ansible inventories and var lookups, but this isn't a great example of that...
I also use invoke for vault editing, there's just some more efficient command line-fu you can get too.
With the release of Fabric v2 I thought it would be easy to migrate, but I vaguely remember it wasn't as trivial as I hoped. Therefore, I just install invoke on its own. Though I love Fabric, Invoke and Paramiko, the developments and integrations within the ecosystem over the recent years have been a little hard to keep track of!
It really is just ansible-playbook initiating playbooks. However, it's worth noting that recently the focus under RedHat has seen the adoption of collections, which I'm yet to understand, as well as more complexity in the underlying system.
You'll notice at the end of playbooks I have the following snippet:
vars_files:
- ../environments/all.yml
- ../environments/data.yml
- "../environments/{{ env }}/all.yml"
- "../environments/{{ env }}/data.yml"
- "../environments/{{ env }}/vault.yml"
The reason I do this is to make very explicit the variable hierarchy that I'm working to. env
is provided by the invoke command line invocation (eg. staging or production at the moment, I've never bothered getting round to making docker work for my personal development process, as I've no requirement for it just yet, but there is something in the pipeline that would justify the effort...)
This is probably going to annoy ansible pro's, but I find it very important. A lot of the time with things like cloud-init, there's no reason to have host_vars. I consider it bad practice myself, for example web servers should be set up when scaling out horizontally and the orchestration process should provide host level variables, not the ansible repo.
I have in the past investigated the variable precedence in ansible and find it irritating to recall, especially as I seem to remember it changing or extending, or otherwise not suiting my group-oriented approach. By using var_files you can easily overcome the need to research this and limit the hierarchy to a set number of levels.
This is one thing I liked from Puppets Hiera (I don't like Puppet because of the DSL and it's huge amount of changes over the years), which asked you to DEFINE the hierarchy. When a data hierarchy is driving the configuration managements effect on deployment, it needs to be easily understood and obviously specified. Also, in my opinion, working with people of varying amounts of experience with such systems, a limit on the amount of levels is no bad thing.
Hopefully that explains the design a little. The data drives the code, which largely (there is an exception for an LetsEncrypt ACME deployment I've not yet moved into a role) is functionally defined by roles, included from playbooks...
Simples! ;-)
This is a private repository for me, but lives in the location mentioned above: ansible/environments
and is structured to meet the requirements of var_files
in the playbooks. Though I won't reveal the setup I have, it's up to the user of this repo to define what's necessary based on the knowledge of technologies contained therein to define the data in a way that deploys they way they want.
That would be a very large article, so for the moment I'll just show you the structure of my repo so you know how to start, then you can define based on identifying key variables from the underlying roles.
./data.yml
./development
./development/all.yml
./host.yml
./production
./production/all.yml
./production/data.yml
./production/proxy.yml
./production/vault.yml
./production/web.yml
./staging
./staging/data.yml
./staging/web.yml
./staging/host.yml
./staging/proxy.yml
./staging/vault.yml
./staging/all.yml
./web.yml
./proxy.yml
./all.yml
Ignore the development environment in my case, I never did anything with it, as I said before! ;-)
Here's a load of things I'd like to do, but never did:
The ansible I write is completely idempotent (as far as I've ever realised), so you should be safe to run over and over and over and over again... provided you configure environments properly...
Since we're in the middle of lockdown number two, I'm happy I've concluded this piece on building the VPS out with Lektor. I really hope people find things useful, but also welcome comments about what information I've left out.
There are plenty more articles on the way, but hopefully this has got me caught up now things are working properly!!!
Comments
Please do leave a comment. I'm moderating them manually for the moment and the Isso project I'm finding slightly experimental, but AMAZING nonetheless. I won't reset the comments database now though, so feedback will be valued!