Commit 5a5a6ebf by Paul

blog: draft of ansible tips post

parent 35cc050d
Pipeline #5191035 passed with stage
in 4 minutes 31 seconds
---
title: "Ansible: a few tips from my experience"
date: 2016-11-29
tags: ansible, automation, configuration management
---
After using [Ansible](https://github.com/ansible/ansible) in a pretty big [production platform](https://www.captaintrain.com/) I wanted to try to list here a few tips worth sharing.
READMORE
If you don't know what Ansible is, the github description is pretty clear:
> Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems.
## 1. Divide your inventory by environment
This is actually explained in Ansible's [documentation](http://docs.ansible.com/ansible/playbooks_best_practices.html#alternative-directory-layout) but it is good to paraphrase.
**Separate your different environments into different "inventory directory"**.
~~~ yaml
test/
hosts # ← inventory file containing testing servers
integ/
hosts # ← inventory file containing integration servers
prod/
hosts # ← inventory file containing production servers
~~~
You will then be sure to never mix integration hosts into your production inventory. You will also have a great way to factorise some variables and keep specific env variables into each directory.
Imagine you have a `webserver` group. This group notion has nothing "environment" related, it's a group that exists for all of your envs. You will thus be able to do this in your directory structure:
~~~ yaml
test/
hosts # testing inventory
integ/
group_vars/
webserver/
vars.yml # ← Variables specific to the integration env
hosts # integration inventory
prod/
group_vars/
webserver/
vars.yml # ← Variables specific to the production env
hosts # production inventory
group_vars/
webserver/
vars.yml # ← Variables common to all envs
~~~
## 2. Tags + Groups for the win!
When using a `role` inside a playbook, **define a `group` condition and a `tag` name**.
The `group` condition gives a good way to limit the execution of certain roles. So you are sure the role is not applied if your server is not part of a certain `group`.
The `tag` name is a great way to test your playbooks. Indeed, when you want to apply only parts of it you will be happy to have tagged all your roles.
For instance,
~~~ yaml
# setup.yml playbook file
- roles:
- role: common
tags: [ common ]
- role: nginx
tags: [ webserver ]
when: "{{ 'webserver' in group_names }}"
- role: postgresql
tags: [ database ]
when: "{{ 'database' in group_names }}"
~~~
With this list of role inside your playbook, you will now easily be able to isolate the `database` related task by passing the `--tags` argument to Ansible.
`ansible-playbook --tags database setup.yml`
You have noticed the condition on both the `nginx` and `postgresql` roles it's a good way to ensure it is part of a certain group each time we run the playbook.
So if the server `db01` is not part of your `webserver` group inside your inventory but you still try to apply the related `webserver` tasks to it you will be sure not to do any mistakes (see the `skipping:` output):
~~~ yaml
# ~> ansible-playbook --limit db01 --tags webserver setup.yml
PLAY [all] ****************************************************
TASK [nginx : Include OS-specific variables.] *****************
skipping: [db01]
TASK [nginx : Define nginx_user.] *****************************
skipping: [db01]
TASK [nginx : Enable nginx repo.] *****************************
skipping: [db01]
TASK [nginx : Ensure nginx is installed.] *********************
skipping: [db01]
...
~~~
## 3. Simple interface for easy wide adoption
I will [again](paul.bonaud.fr/2016/09/Building-apps-with-docker.html#a-realistic-and-proven-use-case) thank my friend and ex-colleague [Pierre](https://twitter.com/pmorinerie) for his nice article about [standardizing projects with Makefiles](https://blog.trainline.eu/13439-standardizing-interfaces-across-projects-with-makefiles). If you have never read it you should take a look it is really great when working with multiple projects.
If we try to apply this to an ansible configuration project we can define a few tasks that we will do often:
* install dependencies
~~~ makefile
install: ## Install roles dependencies
ansible-galaxy install --roles-path vendor/ -r requirements.yml
~~~
* check playbook syntax
~~~ makefile
lint: ## Check syntax of a playbook
ansible-playbook --syntax-check $(opts) $(playbook).yml
~~~
* debug a host's variables
~~~ makefile
debug: ## Debug a host's variable
ansible $(opts) -m debug -a "var=hostvars[inventory_hostname]" -i hosts $(host)
~~~
* dry run a playbook
~~~ makefile
dry-run: ## Run a playbook in dry run mode
ansible-playbook --diff --check $(opts) $(playbook).yml
~~~
* run a playbook
~~~ makefile
run: ## Run a playbook
ansible-playbook $(opts) $(playbook).yml
~~~
Here is my current [Makefile](https://gitlab.com/paulrbr/ansible-owncloud-setup/blob/master/Makefile) for my ansible managed projects.
~~~ makefile
playbook ?= setup
opts ?= $(args) --vault-password-file=pass.sh
install: ## make install # Install roles dependencies
ansible-galaxy install --roles-path vendor/ -r requirements.yml
lint: ## make lint playbook=setup # Check syntax of a playbook
ansible-playbook --syntax-check $(opts) $(playbook).yml
debug: mandatory-host-param ## make debug host=myhost # Debug a host's variable
ansible $(opts) -m debug -a "var=hostvars[inventory_hostname]" $(host)
dry-run: ## make dry-run playbook=setup # Run a playbook in dry run mode
ansible-playbook --diff --check $(opts) $(playbook).yml
run: ## make run playbook=setup # Run a playbook
ansible-playbook $(opts) $(playbook).yml
list: ## make list # List hosts inventory
cat hosts
mandatory-host-param:
[ ! -z $(host) ]
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.DEFAULT_GOAL := help
.PHONY: install lint run dry-run debug list mandatory-host-param
~~~
It is now really easy to test one of my playbook,
~~~ bash
# ~> make dry-run playbook=deploy
ansible-playbook --diff --check --vault-password-file=pass.sh deploy.yml
~~~
I can obviously pass in ansible's arguments in the `args` parameter,
~~~ bash
# ~> make dry-run playbook=deploy args="--limit app1"
ansible-playbook --diff --check --limit app1 --vault-password-file=pass.sh deploy.yml
~~~
## 4. Use Ansible's vault to store encrypted variables
* Separate vault passphrase per environment
* prepend vaulted variables by `vault_`. Use these within your none vaulted ones `myvar: "{{ vault_myvar}"`
* prepend vaulted files by `vault_`.
* use a single entrypoint script to output the passphrase (pass.sh)
* Use `no_log: true` for every tasks that can output a vaulted variable
## 5. Agent-less tool: you need a good and automated build pipeline
CI \o/
## 6. Try to use community Roles from Galaxy
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment