Ansible
Directory Layout
inventories
dev
eu-lab-central-1 # inventory file for lab-eu dev servers
group_vars/
all.yaml # here we assign variables to all hosts
spc-scheduler.yaml # here we assign hosts and variables to particular groups
hosts.yaml # dynamically generated inventory file (do not edit)
stg
eu-lab-central-1 # inventory file for lab-eu stg servers
group_vars/
ppd
eu-lab-central-1 # inventory file for lab-eu ppd servers
group_vars/
prd
eu-central-1 # inventory file for lab-eu prd servers
group_vars/
roles/
common/ # this hierarchy represents a "role"
tasks/
main.yaml # tasks file can include smaller files if warranted
handlers/
main.yaml # handlers file
templates/ # files for use with the template resource
ntp.conf.j2 # templates end in .j2
files/
bar.txt # files for use with the copy resource
foo.sh # script files for use with the script resource
vars/
main.yaml # variables associated with this role
defaults/
main.yaml # default lower priority variables for this role
meta/
main.yaml # role dependencies
computed/
ec2_metadata/
iam/
nbs/
site.yaml # master playbook
computed.yaml # playbook for computed tier
kraken.yaml # playbook for kraken tier
infra.yaml # playbook for infra tier
platform.yaml # playbook for platform tier
- inventories
- layout : ${env}/${region}/
- group_vars
- all.yaml : Assign global variables for all hosts
- spc-scheduler.yaml
- Define Host groups for specific service (ex. spc-scheduler)
- Group variables for the service are in here
- roles
- There are two places this can be done with variables defined in either the ‘vars’ or the ‘defaults’ directory of the role. (See
Scoping variablesfor more information)
- There are two places this can be done with variables defined in either the ‘vars’ or the ‘defaults’ directory of the role. (See
- playbook
- site.yaml : Import a playbook that defines our entire infrastructure
- computed.yaml/kraken.yaml/infra.yaml/platform.yaml : Map the configuration of the specific group to the roles
- We dont use variables in the playbook itself
Variable precedence
Order of precedence
- If multiple variables of the same name are defined in different places, they get overwritten in a certain order.
- Here is the order of precedence from least to greatest (the last listed variables winning prioritization)
role defaults
inventory vars
inventory group_vars
inventory host_vars
playbook group_vars
playbook host_vars
host facts
play vars
play vars_prompt
play vars_files
registered vars
set_facts
role and include vars
block vars (only for tasks in block)
task vars (only for the task)
extra vars (always win precedence)
Scoping variables
The variables used by ‘spc-anible’ are below.
roles/*/defaults/main.yaml < group_vars/all < group_vars/* < host_vars/* < roles/*/vars/main.yaml- roles/*/defaults/main.yaml
- Variables that require deafult values within roles are roles/*/defaults/main.yaml
- Since it is the most default in Ansible, it can be overriding by everything else.
- group_vars/all
- Variables for all groups are stored in group_vars/all. Top-level variable
- group_vars/*
- Variables corresponding to a specific group can be saved as group_vars/region.
- Overriding variables of parent group including all
- host_vars/*
- Variables corresponding to a specific host can be saved as host_vars/xxx.yyy.zzz.
- Overriding the upper two
- roles/*/vars/main.yaml
- Use roles/*/vars/main.yaml if you want to fix the variable value to be used within role
- No overriding by external values! Except -e option
- Do not put variables that will allow overriding.
- roles/*/defaults/main.yaml
We don’t use variables in the playbook itself
Role dependencies
- Role dependencies allow you to automatically pull in other roles when using a role.
- Dependent roles are always executed before the roles that depend on them.
- Also, they are only executed once. If two roles state the same one as their dependency, it is only executed the first time. But when using
allow_duplicates: true, it is executed the several time.
case 1) allow_duplicates: false
- roles/example1/meta/main.yaml
---
dependencies:
- role: example_dep
vars:
some_parameter: 1
- roles/example2/meta/main.yaml
```
—
dependencies:
- role: example_dep vars: some_parameter: 2 ```
- roles/example_dep/meta/main.yaml
--- allow_duplicates: false - the execution order would be the following:
example_dep -> example1 -> example2
case 2) allow_duplicates: true
- roles/example1/meta/main.yaml
---
dependencies:
- role: example_dep
vars:
some_parameter: 1
- roles/example2/gmeta/main.yaml
```
—
dependencies:
- role: example_dep vars: some_parameter: 2 ```
- roles/example_dep/meta/main.yaml
--- allow_duplicates: true - the execution order would be the following:
example_dep -> example1 -> example_dep -> example2
Execution strategy with tags
- A tag is an attribute that you can set to an ansible structure (plays, roles, tasks)
When you apply tags attributes to structures other than tasks, ansible processes the tag attribute to apply only to the tasks they contain. Applying tags anywhere other than tasks is just a convenience so you don’t have to tag tasks individually.
Role-based execution
- To execute the whole task of the specific role, add tags to roles inside the play
Task-based execution
- To execute a specific task, add tags to tasks inside the roles/*/tasks/main.yaml
Sample of playbook command
- ansible-playbook ${playbook} -i ${inventory} -e ${extra_variables} -t ${tags}
ansible-playbook platform.yaml -i inventories/dev/lab-eu-central-1 -e ci_version=0.0.1 -t example
ansible-playbook platform.yaml -i inventories/dev/lab-eu-central-1 -e ci_version=0.0.1 -t example_start
ansible-playbook platform.yaml -i inventories/dev/lab-eu-central-1 -e ci_version=0.0.1 -t example_install,example_start
Best Practices
Groups
Do define the group for your service based on the primitive groups
- Primitive groups are defined in
hosts.yaml- for node type
infra,platform,compute- for node type by az:
- form:
${node_type}_az_${az} infra_az_a,infra_az_b,infra_az_cplatform_az_a,platform_az_b,platform_az_ccompute_az_a,compute_az_b,compute_az_c- for each node for infra/platform
- form:
${node_type}_az_${az}_${index} infra_az_a_1,infra_az_b_1,infra_az_b_1, …platform_az_a_1,platform_az_b_1,platform_az_c_1, …- not defined for compute hosts
Don’t edit the primitive group file - hosts.yaml
hosts.yamlfile will be dynamically generated whenever the inventory is changed on the Serengeti Platform- If you modify the primitive group, it will affect to all user and it will be overwritten in the next inventory generation
Variables
Don’t use host variables as much as possible
- Using host variables tend to make it hard for automation the provisioning process especially if the value can’t be evaluated automatically
- If you need to define some variables which need to be changed per each host, we strongly recommend to consider implementing the centralized configuration management mechanism instead of using host variables
- If it is unavoidable using host variables, you need to define them in the single file
host_vars/{ip_address}.yamlfor the visibility
Don’t define variables for the target host ip address
- Use domain or
inventory_hostnameinstead of defining variables
Do define group variables and host groups together
- Inventory file for your group:
inventories/${env}/${region}/${your_group}.yaml - It is good for the visibility because we can find all information for the group and the group-scope variable in a single group file
- We recommend that we keep only
all.yamlfile ininventories/${env}/${region}/group_varsto maintain the variables for all groups
Don’t define any credentials in plain text
- Encrypt the credentials with ansible vault at least for any password
Playbook
Don’t write tasks in the playbook
- Playbooks should do nothing more than include a list of roles except where pre_tasks and post_tasks are required
- Hide your task code under the roles to form clean, reusable abstractions
- Reuse the existing roles and playbooks for the larger-scoped playbook
Do provide the way of deploying service
- You should define the playbook as long as you provide a way of execution for the minimum deployment unit - we call it
servicein ci pipline- Separated playbook file for each
service - Larger-scoped playbook file with tag-based execution control for each
servicedeployment - Example
ansible-playbook -i inventories/prd/eu-central-1 your_service.yamlansible-playbook -i inventories/prd/eu-central-1 larger_playbook.yaml --tags your_service
- Separated playbook file for each
Do use the directory inventory in general deployment
ansible-playbook -i inventories/prd/eu-central-1 playbook.yaml- It would be also possible to give the required inventory files with multiple
-ioptions but you need to make it sure if all required inventory files are given as arguments
Naming conventions
Variables
- All variables should be snake_case
- Use jinja variable syntax over deprecated variable syntax {{ var }} not $var
- Use spaces around jinja variable names {{ var }} not {{var}}
- Prefix all variables defined in a role with the name of the role (example: myrole_foo)
- how-variables-are-merged
Roles and groups names
- Use underscores (e.g. my_role) not dashes (my-role)
- Roles and group names should be defined to avoid ambiguity or any potential conflicts
- We suggest that you consider using the proper prefix if there are any ambiguity or conflicts in role or group names
- Example for prefix
- product-based:
ec2_,iam_,cloudwatch_, … - project-based:
vm_,vpc_,nbs_, …
- It would prevent collisions among multiple role and group names and explicitly show which product/project a role/group belongs to
Tags
- We recommend that you use underscore in your tag names in order to avoid confusion
Last modified November 20, 2020: Update grafana guilde (3f24643)