1.. _playbooks_best_practices: 2 3Best Practices 4============== 5 6Here are some tips for making the most of Ansible and Ansible playbooks. 7 8You can find some example playbooks illustrating these best practices in our `ansible-examples repository <https://github.com/ansible/ansible-examples>`_. (NOTE: These may not use all of the features in the latest release, but are still an excellent reference!). 9 10.. contents:: Topics 11 12.. _content_organization: 13 14Content Organization 15++++++++++++++++++++++ 16 17The following section shows one of many possible ways to organize playbook content. 18 19Your usage of Ansible should fit your needs, however, not ours, so feel free to modify this approach and organize as you see fit. 20 21One crucial way to organize your playbook content is Ansible's "roles" organization feature, which is documented as part 22of the main playbooks page. You should take the time to read and understand the roles documentation which is available here: :ref:`playbooks_reuse_roles`. 23 24.. _directory_layout: 25 26Directory Layout 27```````````````` 28 29The top level of the directory would contain files and directories like so:: 30 31 production # inventory file for production servers 32 staging # inventory file for staging environment 33 34 group_vars/ 35 group1.yml # here we assign variables to particular groups 36 group2.yml 37 host_vars/ 38 hostname1.yml # here we assign variables to particular systems 39 hostname2.yml 40 41 library/ # if any custom modules, put them here (optional) 42 module_utils/ # if any custom module_utils to support modules, put them here (optional) 43 filter_plugins/ # if any custom filter plugins, put them here (optional) 44 45 site.yml # master playbook 46 webservers.yml # playbook for webserver tier 47 dbservers.yml # playbook for dbserver tier 48 49 roles/ 50 common/ # this hierarchy represents a "role" 51 tasks/ # 52 main.yml # <-- tasks file can include smaller files if warranted 53 handlers/ # 54 main.yml # <-- handlers file 55 templates/ # <-- files for use with the template resource 56 ntp.conf.j2 # <------- templates end in .j2 57 files/ # 58 bar.txt # <-- files for use with the copy resource 59 foo.sh # <-- script files for use with the script resource 60 vars/ # 61 main.yml # <-- variables associated with this role 62 defaults/ # 63 main.yml # <-- default lower priority variables for this role 64 meta/ # 65 main.yml # <-- role dependencies 66 library/ # roles can also include custom modules 67 module_utils/ # roles can also include custom module_utils 68 lookup_plugins/ # or other types of plugins, like lookup in this case 69 70 webtier/ # same kind of structure as "common" was above, done for the webtier role 71 monitoring/ # "" 72 fooapp/ # "" 73 74.. note: If you find yourself having too many top level playbooks (for instance you have a playbook you wrote for a specific hotfix, etc), it may make sense to have a playbooks/ directory instead. This can be a good idea as you get larger. If you do this, configure your roles_path in ansible.cfg to find your roles location. 75 76.. _alternative_directory_layout: 77 78Alternative Directory Layout 79```````````````````````````` 80 81Alternatively you can put each inventory file with its ``group_vars``/``host_vars`` in a separate directory. This is particularly useful if your ``group_vars``/``host_vars`` don't have that much in common in different environments. The layout could look something like this:: 82 83 inventories/ 84 production/ 85 hosts # inventory file for production servers 86 group_vars/ 87 group1.yml # here we assign variables to particular groups 88 group2.yml 89 host_vars/ 90 hostname1.yml # here we assign variables to particular systems 91 hostname2.yml 92 93 staging/ 94 hosts # inventory file for staging environment 95 group_vars/ 96 group1.yml # here we assign variables to particular groups 97 group2.yml 98 host_vars/ 99 stagehost1.yml # here we assign variables to particular systems 100 stagehost2.yml 101 102 library/ 103 module_utils/ 104 filter_plugins/ 105 106 site.yml 107 webservers.yml 108 dbservers.yml 109 110 roles/ 111 common/ 112 webtier/ 113 monitoring/ 114 fooapp/ 115 116This layout gives you more flexibility for larger environments, as well as a total separation of inventory variables between different environments. The downside is that it is harder to maintain, because there are more files. 117 118.. _use_dynamic_inventory_with_clouds: 119 120Use Dynamic Inventory With Clouds 121````````````````````````````````` 122 123If you are using a cloud provider, you should not be managing your inventory in a static file. See :ref:`intro_dynamic_inventory`. 124 125This does not just apply to clouds -- If you have another system maintaining a canonical list of systems 126in your infrastructure, usage of dynamic inventory is a great idea in general. 127 128.. _staging_vs_prod: 129 130How to Differentiate Staging vs Production 131`````````````````````````````````````````` 132 133If managing static inventory, it is frequently asked how to differentiate different types of environments. The following example 134shows a good way to do this. Similar methods of grouping could be adapted to dynamic inventory (for instance, consider applying the AWS 135tag "environment:production", and you'll get a group of systems automatically discovered named "ec2_tag_environment_production". 136 137Let's show a static inventory example though. Below, the *production* file contains the inventory of all of your production hosts. 138 139It is suggested that you define groups based on purpose of the host (roles) and also geography or datacenter location (if applicable):: 140 141 # file: production 142 143 [atlanta_webservers] 144 www-atl-1.example.com 145 www-atl-2.example.com 146 147 [boston_webservers] 148 www-bos-1.example.com 149 www-bos-2.example.com 150 151 [atlanta_dbservers] 152 db-atl-1.example.com 153 db-atl-2.example.com 154 155 [boston_dbservers] 156 db-bos-1.example.com 157 158 # webservers in all geos 159 [webservers:children] 160 atlanta_webservers 161 boston_webservers 162 163 # dbservers in all geos 164 [dbservers:children] 165 atlanta_dbservers 166 boston_dbservers 167 168 # everything in the atlanta geo 169 [atlanta:children] 170 atlanta_webservers 171 atlanta_dbservers 172 173 # everything in the boston geo 174 [boston:children] 175 boston_webservers 176 boston_dbservers 177 178.. _groups_and_hosts: 179 180Group And Host Variables 181```````````````````````` 182 183This section extends on the previous example. 184 185Groups are nice for organization, but that's not all groups are good for. You can also assign variables to them! For instance, atlanta has its own NTP servers, so when setting up ntp.conf, we should use them. Let's set those now:: 186 187 --- 188 # file: group_vars/atlanta 189 ntp: ntp-atlanta.example.com 190 backup: backup-atlanta.example.com 191 192Variables aren't just for geographic information either! Maybe the webservers have some configuration that doesn't make sense for the database servers:: 193 194 --- 195 # file: group_vars/webservers 196 apacheMaxRequestsPerChild: 3000 197 apacheMaxClients: 900 198 199If we had any default values, or values that were universally true, we would put them in a file called group_vars/all:: 200 201 --- 202 # file: group_vars/all 203 ntp: ntp-boston.example.com 204 backup: backup-boston.example.com 205 206We can define specific hardware variance in systems in a host_vars file, but avoid doing this unless you need to:: 207 208 --- 209 # file: host_vars/db-bos-1.example.com 210 foo_agent_port: 86 211 bar_agent_port: 99 212 213Again, if we are using dynamic inventory sources, many dynamic groups are automatically created. So a tag like "class:webserver" would load in 214variables from the file "group_vars/ec2_tag_class_webserver" automatically. 215 216.. _split_by_role: 217 218Top Level Playbooks Are Separated By Role 219````````````````````````````````````````` 220 221In site.yml, we import a playbook that defines our entire infrastructure. This is a very short example, because it's just importing 222some other playbooks:: 223 224 --- 225 # file: site.yml 226 - import_playbook: webservers.yml 227 - import_playbook: dbservers.yml 228 229In a file like webservers.yml (also at the top level), we map the configuration of the webservers group to the roles performed by the webservers group:: 230 231 --- 232 # file: webservers.yml 233 - hosts: webservers 234 roles: 235 - common 236 - webtier 237 238The idea here is that we can choose to configure our whole infrastructure by "running" site.yml or we could just choose to run a subset by running 239webservers.yml. This is analogous to the "--limit" parameter to ansible but a little more explicit:: 240 241 ansible-playbook site.yml --limit webservers 242 ansible-playbook webservers.yml 243 244.. _role_organization: 245 246Task And Handler Organization For A Role 247```````````````````````````````````````` 248 249Below is an example tasks file that explains how a role works. Our common role here just sets up NTP, but it could do more if we wanted:: 250 251 --- 252 # file: roles/common/tasks/main.yml 253 254 - name: be sure ntp is installed 255 yum: 256 name: ntp 257 state: present 258 tags: ntp 259 260 - name: be sure ntp is configured 261 template: 262 src: ntp.conf.j2 263 dest: /etc/ntp.conf 264 notify: 265 - restart ntpd 266 tags: ntp 267 268 - name: be sure ntpd is running and enabled 269 service: 270 name: ntpd 271 state: started 272 enabled: yes 273 tags: ntp 274 275Here is an example handlers file. As a review, handlers are only fired when certain tasks report changes, and are run at the end 276of each play:: 277 278 --- 279 # file: roles/common/handlers/main.yml 280 - name: restart ntpd 281 service: 282 name: ntpd 283 state: restarted 284 285See :ref:`playbooks_reuse_roles` for more information. 286 287 288.. _organization_examples: 289 290What This Organization Enables (Examples) 291````````````````````````````````````````` 292 293Above we've shared our basic organizational structure. 294 295Now what sort of use cases does this layout enable? Lots! If I want to reconfigure my whole infrastructure, it's just:: 296 297 ansible-playbook -i production site.yml 298 299To reconfigure NTP on everything:: 300 301 ansible-playbook -i production site.yml --tags ntp 302 303To reconfigure just my webservers:: 304 305 ansible-playbook -i production webservers.yml 306 307For just my webservers in Boston:: 308 309 ansible-playbook -i production webservers.yml --limit boston 310 311For just the first 10, and then the next 10:: 312 313 ansible-playbook -i production webservers.yml --limit boston[0:9] 314 ansible-playbook -i production webservers.yml --limit boston[10:19] 315 316And of course just basic ad-hoc stuff is also possible:: 317 318 ansible boston -i production -m ping 319 ansible boston -i production -m command -a '/sbin/reboot' 320 321And there are some useful commands to know:: 322 323 # confirm what task names would be run if I ran this command and said "just ntp tasks" 324 ansible-playbook -i production webservers.yml --tags ntp --list-tasks 325 326 # confirm what hostnames might be communicated with if I said "limit to boston" 327 ansible-playbook -i production webservers.yml --limit boston --list-hosts 328 329.. _dep_vs_config: 330 331Deployment vs Configuration Organization 332```````````````````````````````````````` 333 334The above setup models a typical configuration topology. When doing multi-tier deployments, there are going 335to be some additional playbooks that hop between tiers to roll out an application. In this case, 'site.yml' 336may be augmented by playbooks like 'deploy_exampledotcom.yml' but the general concepts can still apply. 337 338Consider "playbooks" as a sports metaphor -- you don't have to just have one set of plays to use against your infrastructure 339all the time -- you can have situational plays that you use at different times and for different purposes. 340 341Ansible allows you to deploy and configure using the same tool, so you would likely reuse groups and just 342keep the OS configuration in separate playbooks from the app deployment. 343 344.. _staging_vs_production: 345 346Staging vs Production 347+++++++++++++++++++++ 348 349As also mentioned above, a good way to keep your staging (or testing) and production environments separate is to use a separate inventory file for staging and production. This way you pick with -i what you are targeting. Keeping them all in one file can lead to surprises! 350 351Testing things in a staging environment before trying in production is always a great idea. Your environments need not be the same 352size and you can use group variables to control the differences between those environments. 353 354.. _rolling_update: 355 356Rolling Updates 357+++++++++++++++ 358 359Understand the 'serial' keyword. If updating a webserver farm you really want to use it to control how many machines you are 360updating at once in the batch. 361 362See :ref:`playbooks_delegation`. 363 364.. _mention_the_state: 365 366Always Mention The State 367++++++++++++++++++++++++ 368 369The 'state' parameter is optional to a lot of modules. Whether 'state=present' or 'state=absent', it's always best to leave that 370parameter in your playbooks to make it clear, especially as some modules support additional states. 371 372.. _group_by_roles: 373 374Group By Roles 375++++++++++++++ 376 377We're somewhat repeating ourselves with this tip, but it's worth repeating. A system can be in multiple groups. See :ref:`intro_inventory` and :ref:`intro_patterns`. Having groups named after things like 378*webservers* and *dbservers* is repeated in the examples because it's a very powerful concept. 379 380This allows playbooks to target machines based on role, as well as to assign role specific variables 381using the group variable system. 382 383See :ref:`playbooks_reuse_roles`. 384 385.. _os_variance: 386 387Operating System and Distribution Variance 388++++++++++++++++++++++++++++++++++++++++++ 389 390When dealing with a parameter that is different between two different operating systems, a great way to handle this is 391by using the group_by module. 392 393This makes a dynamic group of hosts matching certain criteria, even if that group is not defined in the inventory file:: 394 395 --- 396 397 - name: talk to all hosts just so we can learn about them 398 hosts: all 399 tasks: 400 - name: Classify hosts depending on their OS distribution 401 group_by: 402 key: os_{{ ansible_facts['distribution'] }} 403 404 # now just on the CentOS hosts... 405 406 - hosts: os_CentOS 407 gather_facts: False 408 tasks: 409 - # tasks that only happen on CentOS go here 410 411This will throw all systems into a dynamic group based on the operating system name. 412 413If group-specific settings are needed, this can also be done. For example:: 414 415 --- 416 # file: group_vars/all 417 asdf: 10 418 419 --- 420 # file: group_vars/os_CentOS 421 asdf: 42 422 423In the above example, CentOS machines get the value of '42' for asdf, but other machines get '10'. 424This can be used not only to set variables, but also to apply certain roles to only certain systems. 425 426Alternatively, if only variables are needed:: 427 428 - hosts: all 429 tasks: 430 - name: Set OS distribution dependent variables 431 include_vars: "os_{{ ansible_facts['distribution'] }}.yml" 432 - debug: 433 var: asdf 434 435This will pull in variables based on the OS name. 436 437.. _ship_modules_with_playbooks: 438 439Bundling Ansible Modules With Playbooks 440+++++++++++++++++++++++++++++++++++++++ 441 442If a playbook has a :file:`./library` directory relative to its YAML file, this directory can be used to add ansible modules that will 443automatically be in the ansible module path. This is a great way to keep modules that go with a playbook together. This is shown 444in the directory structure example at the start of this section. 445 446.. _whitespace: 447 448Whitespace and Comments 449+++++++++++++++++++++++ 450 451Generous use of whitespace to break things up, and use of comments (which start with '#'), is encouraged. 452 453.. _name_tasks: 454 455Always Name Tasks 456+++++++++++++++++ 457 458It is possible to leave off the 'name' for a given task, though it is recommended to provide a description 459about why something is being done instead. This name is shown when the playbook is run. 460 461.. _keep_it_simple: 462 463Keep It Simple 464++++++++++++++ 465 466When you can do something simply, do something simply. Do not reach 467to use every feature of Ansible together, all at once. Use what works 468for you. For example, you will probably not need ``vars``, 469``vars_files``, ``vars_prompt`` and ``--extra-vars`` all at once, 470while also using an external inventory file. 471 472If something feels complicated, it probably is, and may be a good opportunity to simplify things. 473 474.. _version_control: 475 476Version Control 477+++++++++++++++ 478 479Use version control. Keep your playbooks and inventory file in git 480(or another version control system), and commit when you make changes 481to them. This way you have an audit trail describing when and why you 482changed the rules that are automating your infrastructure. 483 484.. _best_practices_for_variables_and_vaults: 485 486Variables and Vaults 487++++++++++++++++++++++++++++++++++++++++ 488 489For general maintenance, it is often easier to use ``grep``, or similar tools, to find variables in your Ansible setup. Since vaults obscure these variables, it is best to work with a layer of indirection. When running a playbook, Ansible finds the variables in the unencrypted file and all sensitive variables come from the encrypted file. 490 491A best practice approach for this is to start with a ``group_vars/`` subdirectory named after the group. Inside of this subdirectory, create two files named ``vars`` and ``vault``. Inside of the ``vars`` file, define all of the variables needed, including any sensitive ones. Next, copy all of the sensitive variables over to the ``vault`` file and prefix these variables with ``vault_``. You should adjust the variables in the ``vars`` file to point to the matching ``vault_`` variables using jinja2 syntax, and ensure that the ``vault`` file is vault encrypted. 492 493This best practice has no limit on the amount of variable and vault files or their names. 494 495 496.. seealso:: 497 498 :ref:`yaml_syntax` 499 Learn about YAML syntax 500 :ref:`working_with_playbooks` 501 Review the basic playbook features 502 :ref:`all_modules` 503 Learn about available modules 504 :ref:`developing_modules` 505 Learn how to extend Ansible by writing your own modules 506 :ref:`intro_patterns` 507 Learn about how to select hosts 508 `GitHub examples directory <https://github.com/ansible/ansible-examples>`_ 509 Complete playbook files from the github project source 510 `Mailing List <https://groups.google.com/group/ansible-project>`_ 511 Questions? Help? Ideas? Stop by the list on Google Groups 512