I have a use case which is a bit different to most, but Ansible seems to do
a pretty good job.
I'm trying to figure out the best way to leverage Ansible's inherent
concurrency for multiple hosts, when deploying to serverless
infrastructure, which has no hosts. (Instead just using tasks which do API
calls on localhost.)
# The Task
I'm building, testing and deploying a project using Ansible.
My target infrastructure is all serverless. I'm deploying code to AWS
Lambda functions, with CloudFormation. So there are no 'hosts' for Ansible
to connect to.
I have something currently which works, but is slow and a bit messy.
I've got a big playbook, all connecting to one host, which is 'localhost'
(connection=local).
I've got a yaml file with a list of my lambda functions and relevant config
(e.g. timeout, name, comment, environment variables etc).
I've got one role for all my Lambda function stuff.
1. Use `include_vars` to load in aforementioned variable file
2. using `with_dict` and the variable from that file, `pip install` the
modules each one depends on,
3. copy over the code for each Lambda function to where those
dependencies where installed, also with_dict
4. run a unit test for each (shell: python main.py), also with_dict
5. zip up each folder, also with_dict
6. upload each zip to S3, using with_nested (for each lambda function,
for each region)
In particular the uploading is slow, because it's in series. I'd like to do
it in parallel.
Then I have other roles for other things which are not per-lambda. (e.g.
deploying cloudformation templates, configuring a firewall etc.)
# The problem
Inside that role for Lambda stuff, almost every task has with_dict or
with_items, or sometimes with_nested (e.g. upload each lambda zip for each
of multiple regions), in addition to extensive `when` conditions. (I made
it so I can pass in `-e only_lambda=x` to Ansible, to skip tasks for
Lambdas I haven't changed.)
This ends up quite messy.
Overall the whole thing is quite slow too, because it does everything in
series not parallel. Some things are I/O bound, and could be sped up a lot
by doing them concurrently.
e.g. I used `async` to do the upload in parallel.
Another thing that get's messy is that I want to keep that variable file
with lambda config minimal. I don't want to copy-paste boiler plate for
each one. (It's already too huge.)
So each lambda has a field called 'Name'.
I want to add another field like: `local_zip_name: "{{ build_dir }}/{{
self.cf_name }}.zip".
Modifying a dict in Ansible is surprisingly messy. Currently I do this
using `set_fact`.
Although it turns out that if you load the variable from a file with `-e
@file.yaml`, it seems that changes with `set_fact` don't take effect.
So I have to use an include_vars task instead or loading a var file with
command line arguments.
# Attempted solution
I'm trying to add each 'lambda function' to static inventory.
Each one being localhost, but having unique host variables equal to what I
had in that yaml file.
This way I can leverage all the work Ansible devs have already done to do
things concurrently.
It turns out you can omit the IP address, and it defaults to localhost
anyway, and Ansible will happily work with multiple hosts that are actually
the exact same host.
It all just works. Yay!
I can get rid of all the `with_items`, `with_dict` etc, and just run the
role against the group of hosts.
And I can reduce the number of tasks by moving set_fact definitions into
host definitions.
Then for other tasks which I only need to do once, I run those roles with a
different host group, that's one Ansible host connecting to localhost.
This host approach also makes it nice to set things like `local_zip_fname`
(mentioned above). I can set that variable once for the whole group, and
lazy variable evaluation means it's evaluated differently for each host.
This means I don't have to worry about dependency issues about which
variables are defined first, and there's one place for all the default
variables.
But, there are 2 issues.
1. I want to reference that `local_zip_fname` variable (defined using
Jinja2 templating for each Lambda host) from the other host (one host,
localhost). But `local_zip_fname` is not defined for that other host. So I
tried the thing where you dig into `host_vars['other_host']['my_var']` to
get it. But now lazy variable evaluation bites back. The variable I want
(`local_zip_fname`) includes Jinja2 templating for another variable
(cf_name), which was present on those other hosts (one per Lambda), but not
this host.
Question: How can I access variables for other hosts, forcing Jinja2
evaluation based on that other host's environment?
The only solution I can think of is to save the variables to disk for each
of those other hosts (using | to_yaml to force Jinja evaluation), and then
loading that file from the host I want to read the variables. Is there a
better way?
The other issue is that the speed improvement is no where near as good as I
hoped. If I deploy from a beefy machine with 8 CPUs instead of my usual 2,
it seems to speed up the relevant tasks by a factor of 2. (Not by a factor
of 8/2). If I try to deploy with my usual 2 CPU, there's no noticeable
difference in speed.
It seems that there is a lot of overhead per host, which is taking away
from the speed increase from converting sequential `with_items` to
concurrent hosts.
I've tried to optimise things with:
gathering=no
or using fact caching,
and I've already set forks=50 (More than the number of hosts).
And I enabled pipelining (which probably does nothing for connection=local)
Is there any other way to optimise this? Is there a way to tell Ansible
that these 30 hosts are actually all the same machine, so it only needs to
do some things once not 30 times?
Or is there some other approach to solve my problem? Is this the x y problem
<http://xyproblem.info/>?
Thanks,
Matt
(GH: matt-telstra and mlda065)
--
You received this message because you are subscribed to the Google Groups
"Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/ansible-project/2a2e3ce6-a540-450f-949f-69eff16ee190%40googlegroups.com.