Imagine
- There is a critical openssl update that needs to be quickly deployed on all your 100 servers.
- easy if you try???
- You have to setup mysql master-slave on 10 servers(1 master, 9 slaves).
- isn't hard to do???
- Your servers have reached there resource limit and you quickly need to add 10 more servers to meet the demand.
- You may say I am a dreamer. But I am not the only one.
What is ansible and why use it?
A simple configuration management and orchestration tool.
- To have a adaptable and reproducible configuration managing thousands of servers.
- It is designed for cloud. Works out of the box with Amazon web services.
- Easily extendable, many times without requiring to patch core software.
- Its build on top of python, yaml and ssh. Doesn't require much setup to start using.
- Its declarative and idempotent.
Installing ansible on mac
brew install python
easy_install pip
pip install -Iv ansible==1.7.2
Quickly setup a test vagrant
-
Create a folder for your development environment
mkdir testansible cd testansible
-
Initialize Vagrant with the box you just imported
vagrant init ubuntu-12.04
-
Uncomment the private network line in Vagrantfile and change the ip
config.vm.network "private_network", ip: "192.168.33.13"
-
Start Vagrant Instance
vagrant up
Key Ansible Concepts
Inventory file
- Ansible works through an inventory file to know which hosts it can connect to
- Inventory can be either static or dynamic
- Today we will deal with just static inventory file.
- In production, we use dynamic inventory which uses cloud apis
An example Static Inventory
Ansible static inventory uses INI-like format.
-
It looks something like this
mail.example.coma ;<~host [webservers] foo.example.com bar.example.com [dbservers] ;<~group one.example.com id=10 ;<~host_vars two.example.com id=11 ;<~host_vars foo.example.com [ranged_hosts] www[01:50].example.com db-[a:f].example.com [webservers:vars] ; group_vars http_port=80
- A host in ansible jargon is a single target, which you can connect to and deploy on. You can define hosts using ranges (ranges are inclusive)
- You can organise hosts in a group to target multiple hosts simultaneously.
- host_vars have variables associated with a host, which you can use to manage your config. These variables/facts can either be fetched from a central inventory db or can be defined.
- group_vars are variables for an entire group. These can be fetched from inventory
Our own inventory file
Add this in hosts file inside the testansible folder
[servers]
server ansible_ssh_host=192.168.33.13 ansible_ssh_user=vagrant ansible_ssh_private_key_file=.vagrant/machines/default/virtualbox/private_key
[localhost]
127.0.0.1 ansible_connection=local ec2_tag_Name=localhost
Run ad-hoc command using ansible
Now you have inventory, you can execute your own ad-hoc commands
-
Run this on terminal:
ansible -i hosts server -m shell -a "uptime"
-
Lets target a group this time
ansible -i hosts servers -m ping
-
Let's fetch and use a hostvar this time
ansible -i hosts localhost -m shell -a "echo {{ec2_tag_Name}}"
Ansible Modules
Ansible modules is a logical unit in ansible, which when specified with required parameters, can perform certain action.
- shell: run adhoc shell commands
- ping: module to test connectivity to host
- copy: module to copy file from localhost to remote server
- ec2: launch an ec2 instance
Ansible Playbooks
Ansible playbooks is the language of ansible. It allows you to design plans for orchestration, config management, deployment.
- It's just a yaml file and has a very minimal syntax.
-
A simple playbook file looks like this. This one has a single play. Let's create a playbook.yml file in our testansible dir:
--- - name: playbook to setup apache hosts: server sudo: yes tasks: - name: ensure apache is at the latest version apt: name=apache2 state=latest update_cache=yes - name: ensure apache is running service: name=apache2 state=started
-
We can execute a playbook on a host like this:
ansible-playbook -i hosts playbook.yml
Ansible Playbook: An extended example
---
- name: playbook to setup apache
hosts: server
sudo: yes
vars:
http_port: 80
vars_files:
- vars_files/apache.yml
tasks:
- name: ensure apache is at the latest version
apt: name=apache2 state=latest update_cache=yes
tags: install_apache
- name: copy apache conf
template: src=site.conf.j2 dest=/etc/apache2/sites-enabled/site.conf mode=0640 owner=apache group=apache
notify: restart apache
- name: ensure apache is running
service: name=apache2 state=started
handlers:
- name: restart apache
service: name=apache2 state=restarted
- tasks are collection of steps to be executed one after another
- handlers are tasks which run only when triggered.
- vars are various variables in a playbook, used for templates etc. They can be passed at runtime to modify behaviour
- vars_files are collection of vars, stored in seperate yaml files
- tags are identifier for tasks. You can skip or run a task by using ansible-playbook cli options.
Ansible Playbook: Roles
Roles are abstracted logical grouping of tasks, handlers and vars.
---
- name: playbook to setup apache
hosts: server
sudo: yes
#pre_tasks:
roles:
- apache
#post_tasks:
- Roles can be created in a roles directory relative to your playbook path
-
Roles directory structure looks something like this
$ tree roles/apache roles/apache ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ └── main.yml ├── files │ └── apache2.conf ├── templates │ └── site.conf.j2 └── vars └── main.yml
A simple apache role
-
create roles directory inside your testansible
mkdir -p roles/apache/{defaults,tasks,handlers,templates}
-
add this to roles/apache/defaults/main.yml
http_name: john lennon
-
add this to roles/apache/tasks/main.yml
- name: ensure apache is at the latest version apt: name=apache2 state=latest update_cache=yes - name: copy test html page template: src=test.html.j2 dest=/var/www/test.html mode=0644 owner=root group=root notify: restart apache - name: ensure apache is running service: name=apache2 state=started
-
add this to roles/apache/handlers/main.yml
- name: restart apache service: name=apache2 state=restarted
-
add this to roles/apache/templates/test.html.j2
<html> <head> <title>{{http_name}}'s Page</title> </head> <body> <p>This is {{http_name}}'s webpage</p> </body> </html>
-
Once you have setup all your create a apache.yml playbook in your testansible directory
- name: playbook to setup apache hosts: server sudo: yes roles: - apache
-
Execute your playbook using:
ansible-playbook -i hosts apache.yml -e "http_name=ayush"
Extras
Extras: Ansible Doc
You can lookup documentation for modules using ansible-doc utility
-
list all modules
ansible-doc -l
-
check documentation of a module using:
ansible-doc apt
Extras: Loops and conditionals in ansible
Although not hard to do and explain, loops and conditionals can be setup in ansible playbooks using with_items and when keywords Checkout reference for ansible documentation link for it.
Extras: YAML and jinja
- Ansible uses yaml and jinja for its language syntax.
- Variables in playbook use jinja syntax. Templates also use jinja
- A good understanding of yaml and jinja is required to understand ansible really well.
- Checkout documentation resources on yaml and jinja at the end.
Extras: Ansible Vault
You can have encrypted vars files. There is a utility ansible-vault to encrypt a vars files
- If you encrypt vars files using ansible-vault, you need to provide the password when running playbook using ansible-playbook
- This is useful when you have publicly accesible library and you want to keep your parameters secret.
- Refer to links in references for more info
Extras: Vagrant Ansible Provisioner
Vagrant has an ansible provisioner facility, which can generate the ansible inventory file and run playbook with just vagrant up
- It requires some configuration to be done in Vagrantfile
-
Looks something like this:
config.vm.provision "ansible" do |ansible| ansible.playbook = "playbook.yml" ansible.tags = "logrotate" ansible.groups = { "group1" => ["machine1"], "group2" => ["machine2", "machine3"], "all_groups:children" => ["group1", "group2", "group3"], "group1:vars" => { "variable1" => 9, "variable2" => "example" } } end
What's next
World Peace. Maybe?
Exercise
- Create a new vagrant machine
- Write an ansible playbook which when executed:
- setup nginx, along with config to render php pages
- copy an html page in nginx document root which renders the current time
- This setup should work without manually doing anything just by running the playbook.
- Bonus task: set it up so that all of this gets done with just vagrant up after destroying the old machine
Resources
- http://docs.ansible.com/ansible/index.html
- http://docs.ansible.com/ansible/playbooks_loops.html
- http://docs.ansible.com/ansible/playbooks_conditionals.html
- http://docs.ansible.com/ansible/playbooks_vault.html
- http://docs.ansible.com/ansible/YAMLSyntax.html
- http://yaml.org/
- http://jinja.pocoo.org/docs/dev/
- https://helpshift.atlassian.net/wiki/display/OPS/Ansible+Gotchas
Ansible Basics
By Ayush Goyal
Ansible Basics
A presentation on ansible-docs
- 2,103