Ansible Best Practices

Managing Heterogeneous environment without automation can be tricky. We are using Ansible as an automation tool for install, configure and manage DB infra for Mydbops. Using Ansible, we overcome a lot of obstacle’s and we would like to share our learning with the community ( Our Presentation on Ansible )

 

Best Practices to write Ansible Plays

best-practices

Use Native YAML Syntax

Ansible Tasks and Playbook are written in YAML. We can format YAML in different ways

  1. shorthand/one-line (=)
  2. structured map (:)
  3. folded scalars (>)

Shorthand/one-line (=)

e.g.:
- name: Installing Percona Server For RedHat Family.
  yum: "name={{ item }} state=present"
  with_items: "{{ percona_packages }}"
  when: ansible_os_family == "RedHat" and  'percona' in group_names
Structured map (:)
e.g.:
- name: Installing Percona Server For RedHat Family.
  yum:
     name: {{ item }}
     state: present
  with_items: "{{ percona_packages }}"
  when: ansible_os_family == "RedHat" and 'percona' in group_names
folded scalars (>)
e.g.:
- name: Installing Percona Server For RedHat Family.
  yum:>
      "name={{ item }} state=present"
  with_items: "{{ percona_packages }}"
  when: ansible_os_family == "RedHat" and 'percona' in group_names
We believe Shorthand/one-line (=) and Structured map (:) are the method we prefer to use to develop our Playbooks/Tasks. If a task required more then 2 parameters, we use only Structured map (:) format because it gives better readability, improving syntax highlighting and parameters are stacked.

Human Meaningful Names with Variables

Meaningful Task Name
A Task name should indicate its purpose very clearly. If someone else run our playbook, the name needs to indicates the purpose and the action of the task very clearly to the End Users.
- name: Installing Percona Server For RedHat Family.
  yum:
  name: {{ item }}
  state: present
  with_items: "{{ percona_packages }}"
  when: ansible_os_family == "RedHat" and 'percona' in group_names
Output:
TASK [mysql : Installing Percona Server For RedHat Family.] **********************************************************************************************************************
 changed: [v11] => (item=[u'Percona-Server-server-56', u'Percona-Server-client-56', u'Percona-Server-shared-56'])
Meaningful Variable Name
As a best practice, we recommend prefixing variables with the source or target of the Task/Play it represents.
__percona_packages:
    - Percona-Server-server-{{ mysql_version|string |replace(".", "") }}
    - Percona-Server-client-{{ mysql_version|string |replace(".", "") }}
    - Percona-Server-shared-{{ mysql_version|string |replace(".", "") }} 
You can also dramatically improve the readability of your plays for a bit of extra verbosity with using human-meaningful names that communicate their purpose and usage to others or even yourself at a later date.

Project Layout

As  The project grows larger, maintaining a clean and maintainable directory layout is important. There are many different best practices for project layout, but I personally follow the recommendation on Ansible official site. It’s very much suitable for our requirements.

layout

These two are my favorite tips:

  1. Have different inventory for different environments/Clients. (this is important for multiple environment configurations)
  2. Every Inventories directory have two folders(host_vars and group_vars) and one file(hosts).
  3. We group host,  based on the purpose e.g.: Mysql Installation, Linux tunning, PXC Configuration and etc.
  4. using this group name we create a file in targeted inventory group_vars directory, that will contain all variable required by the roles.
  5. roles folder contain all roles, this role developed a focus on single purpose.
  6. We have a playbook folder, which contains all plays files. each play file represents individual role and its hosts. This host will inventory group name, that we specified in 3rd point.
  7. In this design everything will be static, only inventories will vary for every execution.

We are fully utilizing this folder layout and using customized execution model. This approach will not help everyone requirement. But someone developing ansible for multi-environment, sure it will do the purpose.

Ansible Configuration

Ansible will read ansible.cfg configuration from  ANSIBLE_CONFIG environment variable, ansible.cfg in the current working directory, .ansible.cfg from the user home directory or /etc/ansible/ansible.cfg, whichever it finds first it will take high precedence over the other. so we need to aware before we execute our tasks.

ansible.cfg allow tuning many settings, its help to improve the performance and customize according to our need.

inventory = ./inventories/vagrant_testing
forks = 50
ansible_managed = Mydbops Automation managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}. Please dont change anything manually.
pipelining = True

inventory parameters help to define the default inventories hosts file. If the user, unfortunately, forgets to specify the -i <host file>, ansible automatically look for inventory key and value from the ansible.cfg and execute all the task on the particular host files if host group or host match from the host file.

fork parameter help to instruct  Ansible runs the command in parallel using multiple forks. You can increase this value depending on how much your system and network connection can handle. If you set the value to 1, the command gets executed serially, i.e one after the other. The default value is 5.

ansible_managed is a variable that can be inserted into files written by Ansible’s config templating system. this {{ ansible_managed }} variable automatically expand to something meaningful value that we defined in out ansible.cfg. This help user to know how who and what time it created.

pipelining reduces the number of network operations required to execute a module on the remote server, this will increase significant performance improvement. if we use privilege escalation (become) and ‘requiretty’ enabled in /etc/sudoers on all managed hosts, it will get conflict and ssh connection can’t get established. so we need to precise requiretty is disabled on all manage host.

Conclusion

I believe this blog may help everyone. If anyone needs more details about any specific ansible feature please comment below, we will try to write a blog on the same.

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s