I'm coming from a Puppet background using Vagrant and have some trouble making sense of Ansible and its differences.
My Puppet structure looked like this:
puppet
├── servers
│ └── Backend
│ └── Vagrantfile
└── src
├── manifests
│ └── nodes
│ └── development
│ └── backend.pp
└── modules
└── mysql
Setup was simple, cd to the Vagrantfile and fire up the VM with Vagrant.
Now this is my first draft of an Ansible folder structure:
ansible
├── servers
│ └── Backend
│ ├── Vagrantfile
│ └── ansible.cfg
└── sources
├── backend.yml
├── site.yml
├── inventories
│ └── development
│ ├── group_vars
│ │ ├── all
│ │ └── backend
│ └── hosts
├── playbooks
└── roles
└── mysql
Following questions now:
Is this best practise for Ansible or too close to Puppet?
Is it correct to treat backend.yml like a Puppet node manifest?
Where should I put site.yml and backend.yml? This example has them in the main directory while here it's in the 'plays' directory. What's the difference?
I think my group_vars in group_vars/backend aren't being used correctly, how do I access them?
Sources:
http://leucos.github.io/ansible-files-layout/
https://github.com/ansible/ansible-examples
https://github.com/enginyoyen/ansible-best-practises
In my case, I use following structures according environment complexity (check directory-layout) :
Simple environment
I use this structure when there is one environment or when I use playbooks in provision mode
ansible
├── inventory
│ ├── hosts
│ └── group_vars
│ └── my_group.yml
├── roles
│ └── mysql
├── ansible.cfg
├── README.md
├── playbook1.yml
└── playbook2.yml
In ansible.cfg, I use variable inventory = ./inventory in [default] in order to avoid setting inventory path with commands ansible-*.
Medium/Complex environment
I use this structure when there are more than one environment
ansible
├── inventories
│ ├── production
│ │ ├── hosts
│ │ └── group_vars
│ │ └── my_group.yml
│ └── development
│ ├── hosts
│ └── group_vars
│ └── my_group.yml
├── playbooks
│ ├── playbook1
│ │ ├── group_vars
│ │ │ └── my_group.yml
│ │ ├── roles
│ │ │ └── mysql
│ │ ├── README.md
│ │ └── site.yml
│ ...
├── README.md
└── ansible.cfg
In this case, there is a folder for each environments in ./inventories.
I prefer also to use a specific folder for each playbooks in order to be able to use easily a folder group_vars at playbook level as defined in variable precedence section. As environment become more complex, there are much more variables. A group_vars (and host_vars) in playbooks allows to defines common variables for all environment which makes there are less inventories variables.
Ultimate level environment
I already used Ansible to adresse systems with more than 5000 servers, here under some tips for adressing more complex environments :
Split inventory file
Use multiple files to define your inventory servers instead of a single hosts file. In this case hosts file contains only server's names, and other files contains groups with different perspectives :
└── production
├── hosts
├── middleware
└── trigram
middleware: Groups with mapping to used middlewares or other stuf. I use this file to map, for example, servers to tomcat, java, postgresql, etc. And I use it, for example, with playbooks that deploys monitoring agents : How to retrieve metrics, logs from tomcat, java, postgresql, etc.
trigram: On my project, I usually use codes with a fixed length (3 or 4) to identify my business components (ex. : 'STK' for stock management) then I create a group file to map a business component to my servers (which servers are used to deploy 'STK')
When you create a new playbook, choose your perspective to address different environments.
Caution : I think ansible load files with alphabetical name order, you can't define a group that refers a group not yet loaded.
Use folders for group_vars
In group_vars, instead of using files, you can use a folder with subfiles :
└── production
└── group_vars
└── my_group
├── vars1.yml
└── vars2.yml
This is usefull to split huge files or if you have tools that generate variables, in this case you have vars1.yml under git and vars2.yml is generated
Split git repo
When you are using ansible for huge system, there is a lot of commits and a question comes up often : How to split my huge git repo ?
I my case, I use one git repo for each folder in ./inventories with differents access rules. And a git repo for each folder in ./playbooks also with different access rules.
You found several examples of the recommended layout, but not the official one. That should hopefully answer many of your questions, but I'll try to address them here as well.
Is this best practise for Ansible or too close to Puppet?
It's definitely not the recommend layout for Ansible. In the best practices layout, there is no servers or sources - the things inside those all live at the top level (what does "servers" mean, anyways?).
Is it correct to treat backend.yml like a Puppet node manifest?
I'm not familiar with Puppet, and so can't answer this question.
Where should I put site.yml and backend.yml? This example has them in the main directory while here it's in the 'plays' directory. What's the difference?
The official recommendation has all playbooks splattered across the root directory. However, this gets a bit messy, so some people put them in a subdirectory (in your examples, plays). This works reasonably well, but you'll need to adjust the paths in your playbooks accordingly.
I think my group_vars in group_vars/backend aren't being used correctly, how do I access them?
You shouldn't put them in a subdirectory of the inventory folder, as they're not part of inventory. There are a number of places to define variables, and you should read the documentation to learn what they are and when you should use them, but group_vars should be at the root of the directory with everything else.
Related
Ι have the following inventory
[foo]
foosrv ansible_host=11.22.33.44
[bar]
barsrv ansible_host=44.11.22.33
[zoo]
zoosrv ansible_host=21.21.21.21
and the following file structure
.
├── ansible.cfg
├── files
│ └── aws.yaml
├── host_vars
│ ├── foosrv
│ │ ├── templates
│ │ │ └── repoConf.yaml.j2
│ │ └── vault
│ └── barsrv
│ ├── templates
│ │ └── repoConf.yaml.j2
│ └── vault
├── inventory
├── site.yaml
├── templates
│ ├── amend.py.j2
│ └── config.j2
└── vars
├── ansible_vars.yaml
└── vault
My problem is that any variables under ./vars/vault are not recognized by ansible.
Any hints about what might be the issue here?
Ansible doesn't automatically load variables from a vars directory.
You have several options for loading variables:
Ansible will load variables from a file (or directory) in host_vars matching the inventory hostname.
Ansible will load variables from a file (or directory) in group_vars matching the names of groups of which a host is a member.
You can use the include_vars task to explicitly load variables from a file.
If you want to load the variables for all hosts automatically, then place them in group_vars/all.yaml (or a subdirectory of group_vars/all).
Ansible will automatically detect (and decrypt, if you've provided a password) files that are vault encrypted.
I was wondering if anyone could advise on how I can manage multi environment variables using ansible. Imagine i have development and staging. I have the following structure:
.
├── inventories
│ ├── development
│ │ ├── group_vars
│ │ │ └── all.yml
│ │ └── hosts
│ └── staging
│ ├─── group_vars
│ │ └── all.yml
│ │ └── hosts
├── roles
└── test.yml
In each all.yml file, i have one variable like this:
vars:
name:"development"
I was wondering how is the best approach to load the values depending on a parameter to set for example on command line (-i env=development).
In test.yml, I have this:
- name: test playbook
hosts: localhost
connection: local
tasks:
- name:
debug:
var: name
When I execute, I get this:
ansible-playbook -i inventories/development/ test.yml -vvv
....
ok: [localhost] => {
"name": "VARIABLE IS NOT DEFINED!: 'name' is undefined"
}
I'm kind of new to ansible and I've found thousands of different ways and I was wondering what is the best approach? Because I'm kind of lost :( :(
Regards,
We have something similar to your needs on our project, I guess.
Organized in below maner:
.
group_vars folder
├── all.yml # variables common for all envs (creds for example)
├── development.yml # variables for development env
└── staging.yml # variables for staging env
|
invenory_file.yml # file which stoers your managed hosts list devided by groups
├── [development]
│ ├── dev.host1.name # development host#1 with vars applied from development.yml
│ ├── dev.host2.name # development host#2 with vars applied from development.yml
│ └── dev.host3.name # development host#3 with vars applied from development.yml
├── [staging]
│ ├── staging.host1.name # staging host#1 with vars applied from staging.yml
│ ├── staging.host2.name # staging host#2 with vars applied from staging.yml
│ └── staging.host3.name # staging host#3 with vars applied from staging.yml
|
roles
Main idea that you have separate files with vars for different envs, and you have your hosts organized in environments groups in inventory file. So variables from files in group_vars will applied to correct hosts grouped in Inventory.
Here is usuful link to check on how Ansible groups are organized:
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
I have a playbook like so:
- hosts: "{{env}}"
name: "REDIS Playbook"
sudo: no
vars:
product: redis
roles:
- redis
And I call it with: ansible-playbook pb_redis.yml -i inventory/redis -e env=qa -v
I have a directory structure like:
.
├── group_vars
│ ├── qa
│ │ ├── common
│ │ | └── redis.yml
│ │ ├── products
│ │ | └── abc-1.yml
│ │ | └── xyz-2.yml
│ ├── test
│ │ ├── common
│ │ | └── redis.yml
│ │ ├── products
│ │ | └── abc-1.yml
│ │ | └── xyz-2.yml
├── inventory
└── roles
└── redis
├── files
├── handlers
├── meta
├── tasks
├── templates
└── vars
And I have an inventory like:
[qa:children]
qa_redis
[qa_redis]
mybox.1.space
mybox.2.space
mybox.3.space
My Issue: When I run ansible-playbook pb_redis.yml -i inventory/redis -e env=qa -v, I'm still picking up group_vars defined in the ../test/common/redis.yml instead of ../qa/common/redis.yml -- am I misunderstanding how this should work? The correct hosts get picked up from the inventory file, but not the correct group_vars. Should I place the redis.yml under ../qa/products/ instead?
Inventory (host and group) variables in Ansible are bound to host. Group variables exist for convenience.
If a host is in multiple groups at the same time, all group variables are applied to that host.
If different groups have same variables, they overwrite each other during inventory load process.
So if you have mybox.1.space in groups qa and test, variables from groups qa and test are applied to this host.
Usually you want to use separate inventories to work with different deploy environments. And groups are used to separate different logical units inside inventory.
I am trying to figure out the best practice folder structure to organize my vault variables.
Currently it looks like this:
.
├── group_vars/
│ ├── group1.yml
│ └── group2.yml
├── host_vars/
│ ├── host1.yml
│ └── host2.yml
├── roles/
│ └── .../
└── vault/
├── enc-file1.yml
└── enc-file2.yml
However, this way I always have to use include_vars inside my role to source a specific encrypted file.
Is there any naming convention and folder structure I can apply that Ansible will automatically source the correct vaulted variable just as it does with host_vars and group_vars?
I had something like this in mind:
.
└── group_vars/
├── group1/
│ ├── main.yml
│ └── vault.yml
└── group2/
├── main.yml
└── vault.yml
Is there anything I can do, so I do not have to explicitly include vault variables?
Is there anything I can do, so I do not have to explicitly include vault variables?
In recent Ansible versions (since 2.3) you don't need to include separate files for vault-protected variables. Instead, you can encrypt individual variables in regular vars-files - see Single Encrypted Variable.
Is there any naming convention and folder structure [] that Ansible will automatically source the correct vaulted variable []?
No, there is no convention nor automatic mechanism.
#cytopia, what you have in mind works.
You can split encrypted/clear inventory variables in separate files. Ansible will sort out what is crypted and what is not.
I use the following layout and had no problems with it so far (1.9.x - 2.2.1.0):
group_vars/
all/
clear
secret
group1/
clear
secret
group2/
clear
secret
I need to set up quite few host specific variables. Up until now I was adding them to the hosts file but the list of variables is getting ridiculously long.
What I'd like to do is to tell ansible to load a host specific file that will contain all the necessary variables. Let's say my hosts are:
[webservers]
hydrogen
helium
lithium
And that variables for them are structured like this:
.
├── variables
│ ├── hydrogen.yml
│ ├── helium.yml
│ └── lithium.yml
└── blahblah
How can I load those variables for each host automatically?
Ansible has the concept of group and host vars. No need to manually tell it to load vars from a file, it's all built in. Simply create files relative to your playbook (or inventory file) in the folders group_vars and/or host_vars:
.
├── group_vars
│ └── webservers.yml
├── host_vars
│ ├── hydrogen.yml
│ ├── helium.yml
│ └── lithium.yml
└── playbook.yml
Also see Best Practices: Group And Host Variables