Management of FreeBSD jails through Ansible
!! Work in progress !! please come regularly for updates
Table of contents
- Management of FreeBSD jails through Ansible
- Table of contents
- TL; DR.
- Prerequisites
- Without the jail connector
- Enter the jail connector
- Hmm does a jail has a name?
- Next steps
- Local vs remote
- BSDPloy?
- Credits
TL; DR.
Ansible is Configuration Management software, similar in spirit to Chef and Puppet but way more lighter in requirements (no CouchDB like Chef) and in scalability (no agent running, ssh connection and Python installed).
I have become interested in Ansible for a few months now and I just recently found a way to deal with jails on FreeBSD, my operating system of choice for close to 20 years now.
Prerequisites
Version of Ansible should not really matter, anything from at least 1.4 has the jail
connector we will be using there. As of this writing, it is 1.7.0
. I do not think the version of FreeBSD should not matter much either, I have been playing with 8.2 and 9.2 mostly and planning to go to 10 soonish.
Reading Ansible documentation will help of course if you are not familiar with it although the jail
connector is really documented unfortunately, I found help on Twitter.
Knowledge of jails on FreeBSD is of course necessary.
Without the jail connector
Say you have several jails configured on your system, either manually or (better) through ezjail. I use ezjail
because it makes thing much easier to handle and save space by having a basejail
part shared between all jails on the system. Unfortunately, that also means that you can’t mix OS versions between jails (but it does not mater here).
jls
might give you something like this:
1273 [16:57] root@centre:local/etc# jls
JID IP Address Hostname Path
4 127.0.1.2 www.keltia.net /jails/www
1233 127.0.1.3 mail.keltia.net /jails/mail.keltia.net
1251 127.0.1.5 shell.keltia.net /jails/shell.keltia.net
Note that the JID
parameter will change everytime you start a jail so you can not rely on that for your Ansible host inventory so you need way to get the output of jls
and give it to Ansible (either statically or through its dynamic inventory feature (which Ihaven’t tested yet).
After you got that list written in LOCALBASE/etc/ansible/hosts
(the default), you could expect to issue a series of jexec(8)
commands to do things in the jails but as you can see, it is rather limited and you would need to quote all the special characters (for the shell) yadda yadda yadda.
Not particularly fun.
Enter the jail connector
With very (and I mean it) little documentation, if you happen to look into the Ansible sources on GitHub you will notice the connection_plugins
directory with some interesting names such as chroot
and jail
. Now, how to use that?
As I said, I finally asked several times on twitter and IRC (there is a #ansible
channel on Freenode) and a few days ago, I got an answer from Julien Dauphant aka @jdauphant leading to the solution.
One need to use the ansible_connection=jail
parameter in the hosts
file with the first parameter being the name of the jail. Then you can refer to each jail by name as target for an Ansible command or playbook.
Hmm does a jail has a name?
Well yes. You can give a name to each jail, either through /etc/jail.conf
(if you are on 9.2+) or the series of rc.conf(5)
variables (before 9.2) but, interestingly enough ezjail
does NOT give one (maybe I missed it - anyone?).
It is easy to correct that though:
jail -m jid=1251 name=shell_keltia_net
and tadaa! you can now refer JID
1251 as shell_keltia_net
in your hosts
file. You will need to do it once for each jail and fix the ezjail.conf
file.
Next steps
Now that every jail has a name, I can fill in my hosts
file:
[jails:children]
www
mail
shell
[www]
www_keltia_net ansible_connection=jail
[mail]
mail_keltia_net ansible_connection=jail
[shell]
shell_keltia_net ansible_connection=jail
If I want to ping
(the Ansible way, not the `ping(8) way) all jails, I just need to use
ansible jails -m ping
result will be similar to the following:
www_keltia_net | success >> {
"changed": false,
"ping": "pong"
}
mail_keltia_net | success >> {
"changed": false,
"ping": "pong"
}
shell_keltia_net | success >> {
"changed": false,
"ping": "pong"
}
Now, I can also use Ansible to upgrade all jails after my poudriere
run has finished with:
ansible jails -m shell -a 'pkg upgrade -y'
and so on…
More to come.
Local vs remote
Ansible is great to manage several machines in parallel and run things from remote places but there is something specific with the connection plugins: as you are telling Ansible to use something other than the default (ssh-based), you can not use the jail
connector from a remote machine. Which means that the playbooks have to be on the same machine as the jails.
There might be a way to workaround that with using the delegate_to
feature inside playbooks but I have not explored that way yet. You could of course use a playbook on your remote machine that runs another playbook (or just commands) on the jail host.
BSDPloy?
A few weeks ago I also became aware of BSDPloy, an ezjail/Ansible-based package created to automate whole hosts (and jails) provisioning. I shall explore it further.
Credits
Thanks to Julien Dauphant!