Creating Reusable Code
Chapter 5
Address Complexity
Reusability and Sharing
Modular Code
---
- name: Base Configurations for ALL hosts
hosts: prod:!app2
become: true
tasks:
- name: create admin user
user: name=admin state=present uid=5001
- name: remove dojo
user: name=dojo state=absent
- name: install tree
package: name=tree state=present
- name: install ntp
package: name=ntp state=present
- name: start ntp service
service: name=ntpd state=started enabled=yes
- name: App Server Configurations
hosts: app
become: true
tasks:
- name: create app user
user: name=app state=present uid=5003
- name: install git
package: name=git state=present
playbook.yml
hosts definitions, properties, tasks all in one place
specific to one environment, can not be shared
difficult to reuse
can quickly get out of control with increasing number of applications
---
- name: Base Configurations for ALL hosts
hosts: all
become: true
tasks:
- name: create admin user
user: name=admin state=present uid=5001
- name: remove dojo
user: name=dojo state=absent
- name: install tree
yum: name=tree state=present
- name: install ntp
yum: name=ntp state=present
- name: start ntp service
service: name=ntpd state=started enabled=yes
- name: App Server Configurations
hosts: app
become: true
tasks:
- name: create app user
user: name=app state=present uid=5003
- name: install git
yum: name=git state=present
roles
base
apache
php
playbook.yml
Strategy : Move code related to each application/feture to its own place, call it a role
roles
base
apache
---
- name: Base Configurations for ALL hosts
hosts: all
become: true
roles:
- base
- name: App Server Configurations
hosts: app
become: true
roles:
- apache
- php
php
playbook.yml
playbook is simplified
modular code
reusable and sharable
---
- name: Base Configurations for ALL hosts
hosts: all
become: true
tasks:
- name: create admin user
user: name=admin state=present uid=5001
- name: remove dojo
user: name=dojo state=absent
- name: install tree
yum: name=tree state=present
- name: install ntp
yum: name=ntp state=present
- name: start ntp service
service: name=ntpd state=started enabled=yes
- name: App Server Configurations
hosts: app
become: true
tasks:
- name: create app user
user: name=app state=present uid=5003
- name: install git
yum: name=git state=present
---
- name: Base Configurations for ALL hosts
hosts: all
become: true
roles:
- base
- name: App Server Configurations
hosts: app
become: true
roles:
- apache
- php
A Role is a package which contains tasks which take action along with supporting entities such as handlers, properties, files, tests and metadata
What is a Role ?
roles/
└── starter
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
tasks
defaults
vars
files
templates
handlers
tests
meta
actions to take , using modules
default properties/vars
role specific vars
static files to be copied to nodes
files generated dynamically
actions to take on events
ansible tests
role meta data
Role
App
Feature
When to create a a Role ?
Lets look at the strategies to organize the code better
load balancers
app servers
db servers
apache
php
wp
layers = inventory groups
apache
php
wp
roles
roles
create roles for each app
haproxy
postgres
roles
apache
php
wp
play
play
play
create plays for each inventory group/layer
apache
php
wp
play
play
play
combine plays into a site wide playbook
site.yml
+
+
=
There are two ways to create roles
ansible-galaxy
Roles can be created by simply creating directories and files in certain order
A utility which lets you generate role scaffolding
https://galaxy.ansible.com/
a library of community contributed roles
servers as a source of reference to learn best practices
no need to reinvent the wheel. Resue and share roles
# ansible-galaxy init --init-path roles/ apache
- apache was created successfully
[root@ansible chap5]# tree roles/apache/
roles/apache/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
8 directories, 8 files
ansible-galaxy init --init-path roles/ apache
ansible-galaxy --help
# ansible-galaxy --help
Usage: ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ...
Options:
-h, --help show this help message and exit
-v, --verbose verbose mode (-vvv for more, -vvvv to enable connection
debugging)
--version show program's version number and exit
install apache2 package
centrally manage apache2.conf and copy it to all nodes
start apache2 service
whenever apache configs are updated, relevant service should be automatically restarted
Create a role for installing and configuration apache web server on Ubuntu platform which will,
cd chap6
mkdir roles
ansible-galaxy init --offline --init-path=roles apache
Lets begin by creating a scaffolding (directory structure) for apache role
# ansible-galaxy init --init-path roles/ apache
- apache was created successfully
[root@ansible chap5]# tree roles/apache/
roles/apache/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
8 directories, 8 files
tree roles/
install apache2
copy configs and html docs
start apache2 service
package
service
copy
Lets now come up with the actions that we would take to achieve desired state, and start mapping it to ansible modules
roles
apache
tasks
main.yml
install.yml
config.yml
service.yml
File: roles/apache/tasks/install.yml
---
- name: install apache web server
yum:
name: apache2
state: present
File: roles/apache/tasks/service.yml
---
- name: start apache webserver
service:
name: apache2
state: started
enabled: true
File: roles/apache/tasks/main.yml
---
# tasks file for apache
- import_tasks: install.yml
- import_tasks: service.yml
when you call a role from playbook, it includes only main.yml task file
if you create additional task files, the only way to have those called is by including in main.yml
lets include install.yml and service.yml in main.yml for apache role
File: app.yml
---
- hosts: app
become: true
roles:
- apache
As per our strategy, we are going to create a playbook for each layer
lets begin by writing app.yml for app servers
app.yml maps app servers to apply apache role
apache role will in turns point to main.yml
main.yml will then execute all included tasks from install.yml and start.yml
roles
apache
tasks
main.yml
install.yml
service.yml
root@control:/workspace/chap6# ansible-playbook app.yml
PLAY [app] *********************************************************************
TASK [setup] *******************************************************************
ok: [app1]
ok: [app2]
TASK [apache : Install Apache...] **********************************************
changed: [app1]
changed: [app2]
TASK [apache : Starting Apache...] *********************************************
changed: [app2]
changed: [app1]
PLAY RECAP *********************************************************************
app1 : ok=3 changed=2 unreachable=0 failed=0
app2 : ok=3 changed=2 unreachable=0 failed=0
ansible-playbook app.yml
root@control:/workspace/chap6# ansible app -b -a "service apache2 status"
app2 | SUCCESS | rc=0 >>
apache2 (pid 3565) is running...
app1 | SUCCESS | rc=0 >>
apache2 (pid 3616) is running...
ansible app -b -a "which apache2"
root@control:/workspace/chap6# ansible app -b -a "which apache2"
app2 | SUCCESS | rc=0 >>
/usr/sbin/httpd
app1 | SUCCESS | rc=0 >>
/usr/sbin/httpd
ansible app -b -a "service apache2 status"
roles
apache
tasks
main.yml
install.yml
service.yml
files
httpd.conf
config.yml
in addition to installing and starting services, we also need to copy configuration file for apache
to manage centrally, files are stored in its own dedicated files directory inside roles
in addition, tasks to copy the files from inside roles to appropriate destination need to be created. Lets write config.yml to do this
which will then be added to main.yml just like other two task files we created earlier
To simplify your work, sample apache2.conf is provided below
download and copy content of above file to roles/apache/files/apache2.conf
Lets now create config.yml to
---
- name: copy over httpd configs
copy:
src: apache2.conf
dest: /etc/apache2/apache2.conf
owner: root
group: root
mode: 0644
path: roles/apache/tasks/config.yml
Config File
port = 80
port = 8080
Service
restart
notify
handler
roles
apache
tasks
main.yml
install.yml
start.yml
files
httpd.conf
index.html
config.yml
handlers
main.yml
just like tasks and files, handlers have their own dedicated directory
inside which you may already find main.yml. You could add handler to restart apache here
handler will get triggered only when notified
---
# handlers file for apache
- name: Restart apache service
service: name=apache2 state=restarted
path: roles/apache/handlers/main.yml
To notification, update config.yml
---
- name: copy over apache2 configs
copy:
src: apache2.conf
dest: /etc/apache2/apache2.conf
owner: root
group: root
mode: 0644
notify: Restart apache service
path: roles/apache/tasks/config.yml
root@control:/workspace/chap6# ansible-playbook app.yml
PLAY [app:!app2] **************************************************************
*
TASK [setup] ******************************************************************
*
ok: [app1]
TASK [apache : Install Apache...] *********************************************
*
ok: [app1]
TASK [apache : Starting Apache...] ********************************************
*
ok: [app1]
PLAY RECAP ********************************************************************
*
app1 : ok=3 changed=0 unreachable=0 failed=0
ansible-playbook app.yml
Were the files copied ?
Was the service restarted as a result of config file updates ?
Create a php role
add tasks to install php,
libapache2-mod-php and php-mysql packages
Exercise: Role to install PHP
copy following contents to /var/www/html/info.php
<?php
phpinfo();
?>
Send a notification to restart apache after installing the packages and copying the file
validate by visiting http://IPADDRESS:80/info.php
Create a Systems Role for common tasks
Move all tasks in the systems.yml to this role.
Define role nesting with dependencies/meta-data
Create Site Wide Playbook
Exercise : Systems Role and Sitewide Playbook
Update apache2.conf and change some configuration parameters.
Validate the service restarts on configuration updates by applying the sitewide playbook.
Exercise : Update Apache Configs