Please criticize my first playbook that supposed to add nginx vhosts from config.

Hey everybody,

I’m trying to learn Ansible. I created my first playbook.

It’s not finished yet. However, what I’ve tried to accomplish works fine.

I’m not that satisfied with the result because I feel I’m still not getting Ansible the way it’s creators want the community to get it.

Here is a link to the github:
https://github.com/tounano/nginx_vhosts

What I’m trying to accomplish is to be able to create nginx vhosts and php pools directly from config files.

I have several servers and each server hosts different websites. That’s why I need the vhosts to be loaded from the config.

What I did is I created a role called nginx_vhosts which loops through a list that is defined in the config which contains the vhost data. Overall, I have a feeling that it’s a really messy solution, because I believe that the roles should be responsible for single operations (single vhost in that case).

Anyway, it’s my first playbook, so I don’t know…

Please give me as much feedback and critique as you can.

Thanks.

I am a newbie so i can not comment much, but it looks very neat, and impressive to me as a first attempt.

A few comments.

Storing things in host_vars not group_vars seems a little curious, are you expecting that much variance from host to host?

A good practice would be to define things in group_vars/ as much as you can and then override per host only as needed.

I’m not sure why the variable “server_name” contains a variable that is not a server name, ex: " server_name: test-site1.com www.test-site1.com"

Perhaps you would like to use both variables in a template instead.

You include a role named ‘test’ but in your repository the only role is ‘nginx_vhosts’

Some stray comments here: https://github.com/tounano/nginx_vhosts/blob/master/roles/nginx_vhosts/tasks/main.yml

In https://github.com/tounano/nginx_vhosts/blob/master/roles/nginx_vhosts/templates/nginx_site.j2, you’re doing some weird things with calling template subsitution inside template calls.

Hey Michael,

Thanks for commenting, I appreciate it a lot.

I included the configuration on host config files because each host/server will have different websites on it.

The role test is a mistake, it’s my playground where i test stuff, but I didn’t upload it. I guess I forgot to change.

The weird template substitution is because I didn’t know how to merge the default vars data with the given vhost data.
“listen” refers to the port. This is nginx vocabulary.

Anyway, I feel that my playbook smells… I hope to keep improving it :slight_smile:

Maybe the right way for me would be to go with a common play for all the webservers and then a unique play for each webserver. This unique play will contain only the vhosts data. What do you think about such approach? (we have about 10 servers and an average of 10 websites per server.

Thanks again :slight_smile:

I’d probably avoid that – though you have a small number of servers and it wouldn’t bite you if you did.

Playbooks should ideally be data-driven and definining different variables per host would be better than a different play for host, and would also allow configuration in parallel.

Hi All,

I have the same issue with vhost creation and I have very similar playbook.
I don’t like, that I need to use within_items for each task in role because I have around 20 tasks, like user creation, creating carious folders and configs.
I see that Dan is using the same style, but maybe somebody know a better way?

It would be perfect to use have something like within_items for roles.

Hey Rolands,

Thanks for your comment.

Actually, I came up with several ways of doing it. Not sure which one is the best.

  1. To have a play for each host. It’s not such a good idea when you have a lot of servers. But mine is manageable. I have roles that should perform on each vhost separately.

  2. It’s possible to create hosts on the fly with add_host. So basically what’s possible to do is to have 2 playbooks:

  • webservers.yml
  • vhosts.yml
    Now, you can set the vhosts for each host in the host_vars. And afterwards do add_host hostname=vhost.com ansible_ssh_hostname={{ ansible_ssh_hostname} ansible_ssh_port={{ ansible_ssh_port }}

Now, what you want is doable :slight_smile:

I tried both, but I decided to go with 1. Especially with includes…

I’ll push the playbook to github next week.

Anyway, I don’t know if I’m competent enough to give advice.

Hope it helped.

Hi Dan,
I think there is one more possibility, don’t use roles and try to use includes with parameters http://www.ansibleworks.com/docs/playbooks.html#id9But in this case we lose best practice and some of features what’s included :slight_smile:

Hey,

I do use them and also roles. However, there are scopes for the includes.
For exmple, something like that will work:

include + with_items is not documented for good reason and will be removed in the near future.

Please loop inside the task.

Here's another one: write a script to generate your playbook from your config (which I presume is another yaml file). I've been idly contemplating doing this kind of thing in the hope it might avoid the headaches mentioned below, but haven't spent lot of time pursuing it so far.

My own most recent shot at Apache with vhosts has been to define a role for the common stuff, and another for the vhost specifics which depends on the former (with no parameters, except via the back-door, i.e. setting global vars).

These roles (aim to) have sensible defaults - currently defined in vars/main.yml (since defaults/main.yml seemed to do odd things I didn't have time to resolve [1]).

I then invoke the latter once for each vhost I need, varying the parameters. A working example of this sort of thing (installs Drupal sites onto Debian):

https://github.com/wu-lee/test-playbooks/tree/drupal-playbook-with-roles

Constructive criticism welcome, this mostly works but it does seem to have problems.

Without going to to too much detail, my main headaches seem to arise from Ansible's var scoping and referencing semantics, which clearly I don't understand fully. Vars frequently baffle and confound me. For example, dollar references and Jinja2 expressions evidently have different semantics, and whilst the former are discouraged and don't seem to be undef checked, the latter have a tendency to stringify references and booleans, or throw "UndefinedError" despite my efforts to define them. Tracing these problems between roles can be tricky, since undefined variables may be caused by some upstream role. Inspecting the parameters passed to each role requires judiciously inserting debug actions - and even these can throw an error without indicating what the precise cause is.

Cheers,

N

1. IIRC, the supplying parameters to the role clobbers sibling parameters defined in defaults/, which are merged more normally in when they're in vars/, resulting in unexpanded variable references.

A hand-waving example: if defaults/main.yml said:

     stuff:
       default_foo: x
       default_bar: y

And my invocation said:

     roles:
       - role: rolename
         stuff:
            default_foo: z

Then stuff.default_bar seemed to get clobbered, despite hash_behaviour = merge.

An easier way to use defaults is to just use the role defaults feature in 1.3, and then both parameterized role values as well as inventory values will override them.

hash_behavior merge, I understand why we did it at the time, but it’s not something I’d encourage.

It makes you think too much about things versus simplifying things IMHO.