0
votes

I'm prototyping Ansible using Vagrant and am using "vagrant provision" to build my stack. I've defined roles to build a core system, a webserver, a database server, and an application server. (The core system would be the roles that would be required for any system type, such as installing system packages and users).

I'd like to configure Vagrant to execute all the roles (to install everything on a single vm), but in a production environment, I might want to install only certain roles on certain machines. What would be the best way to structure my playbook and inventory file such that I can accommodate the single server Vagrant machine and a multi-server production machine group?

Would that involve creating multiple playbooks? One for Vagrant and one for each other configuration? If so, that might lead to a lot of duplication of code.

Here is what I have so far, but it's not working...

Here's the full project: https://github.com/JoeJasinski/docker-ansible-web

Vagrantfile

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.provision :ansible do |ansible|
    ansible.playbook = "ansible/site.yml"
    ansible.verbose = "vvv"
    ansible.extra_vars = {}
    ansible.inventory_path = "ansible/inventory.ini"
    ansible.sudo = true
    ansible.groups = {
      "nginxServer" => ["nginxServer",],
      "djangoServer" => ["djangoServer",],
      "postgresServer" => ["postgresServer",]
   }
end

inventory.ini

[nginxServer]
127.0.0.1

[djangoServer]
127.0.0.1

[postgresServer]
127.0.0.1

site.yml

---
- name: Base states
  hosts: all
  vars:
    - update_apt_cache: yes
  vars_files:
    - env_vars/base.yml
  roles:
    - role: base
    - { role: users, tags: ['users'] }
    - { role: sites, tags: ['sites'] }

- name: Nginx Host
  hosts: nginxServer
  vars:
    - update_apt_cache: yes
  vars_files:
    - env_vars/base.yml
  roles:
    - { role: supervisor, tags: ['supervisor'] }
    - { role: nginx, tags: ['nginx'] }

- name: Django Host
  hosts: djangoServer
  vars:
    - update_apt_cache: yes
  vars_files:
    - env_vars/base.yml
  roles:
    - { role: supervisor, tags: ['supervisor'] }
    - { role: django, tags: ['django'] }

- name: Postgres Host
  hosts: postgresServer
  vars:
    - update_apt_cache: yes
  vars_files:
    - env_vars/base.yml
  roles:
    - { role: postgres, tags: ['postgres'],}
    - { role: postgis, tags: ['postgis'], }
2

2 Answers

1
votes

Would that involve creating multiple playbooks? One for Vagrant and one for each other configuration? If so, that might lead to a lot of duplication of code.

You do not need to duplicate code for this. Playbooks can include other playbooks. So you could separate your playbooks per type, and then for vagrant have a top-level playbook which includes the other playbooks while for production servers these playbooks are executed separately.

- include: webserver.yml
- include: database.yml
- include: application.yml

Another idea is to simply have conditional roles. If I understand you correctly, you have the groups nginxServer, djangoServer and postgresServer and your testing VM's are member of all 3 groups, while your production hosts only are member of one ore two groups. If that's correct, your can simply add conditions to the roles.

For example:

  roles:
    - role: supervisor
      tags: supervisor
      when: "nginxServer" in group_names

    - role: nginx
      tags: nginx
      when: "nginxServer" in group_names

Execution time though would be longer, since Ansible still would process all roles. The conditions actually are passed on to each task inside the roles, so you'd have a lot of skipped tasks then.

1
votes

That playbook format you already have will work fine for when you decide to go to multiple hosts and splitting different roles to different hosts simply by the inventory file and the hosts that each playbook targets.

For example, if you had a setup with a single web server, a single application server and a single database server then your inventory would look like this:

[nginxServer]
web.example.org

[djangoServer]
app.example.org

[postgresServer]
database.example.org

Or if you only wanted to split the database off from the combined web/app box you'd have something like:

[nginxServer]
app.example.org

[djangoServer]
app.example.org

[postgresServer]
database.example.org

Which would run your nginx and application plays against the same app.example.org host.

Equally you can easily extend things out to a larger environment too:

[nginxServer]
web-1.example.org
web-2.example.org

[djangoServer]
app-1.example.org
app-2.example.org
app-3.example.org
app-4.example.org

[postgresServer:children]
postgresMaster
postgresSlave

[postgresMaster]
database-1.example.org

[postgresSlave]
database-2.example.org

The only addition you have in the above example is that the postgres servers have been split out into a master-slave configuration. As is, that will setup postgres as you use it now on both boxes and then you can write further plays to more specifically target the child groups to setup replication between the two.

If you want the ability to target just single groups of hosts (such as nginxServers) then you can do this simply by using --limit with your original playbook or you can write more specific playbooks that only target that host group for the role and then have a top level playbook that runs all the other playbooks for when you want to provision everything:

site.yml

- include: webserver.yml
- include: database.yml
- include: application.yml

webserver.yml

- name: Nginx Host
  hosts: nginxServer
  vars:
    - update_apt_cache: yes
  vars_files:
    - env_vars/base.yml
  roles:
    - { role: supervisor, tags: ['supervisor'] }
    - { role: nginx, tags: ['nginx'] }

As for the base play, I'd be inclined to make that role a dependency as all of your other servers should always have the base role ran against them no matter how that role is called. You can do that by creating a meta folder under each of your 3 main roles and including a main.yml file there with the following contents:

dependencies:
    -   role: base
    - { role: users, tags: ['users'] }
    - { role: sites, tags: ['sites'] }