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 httpd package
centrally manage httpd.conf and copy it to all nodes
start httpd service
whenever apache configs are updated, relevant service should be automatically restarted
Create a role for installing and configuration apache web server on CentOS 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 httpd
copy configs and html docs
start httpd service
yum or apt
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: httpd
state: installed
File: roles/apache/tasks/service.yml
---
- name: start apache webserver
service:
name: httpd
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 httpd status"
app2 | SUCCESS | rc=0 >>
httpd (pid 3565) is running...
app1 | SUCCESS | rc=0 >>
httpd (pid 3616) is running...
ansible app -b -a "which httpd"
root@control:/workspace/chap6# ansible app -b -a "which httpd"
app2 | SUCCESS | rc=0 >>
/usr/sbin/httpd
app1 | SUCCESS | rc=0 >>
/usr/sbin/httpd
ansible app -b -a "service httpd 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, httpd.conf has been given in the helper directory inside workspace
cp helper/httpd.conf roles/apache/files/
copy file to roles/apache/files
Lets now create config.yml to
---
- name: copy over httpd configs
copy:
src: httpd.conf
dest: /etc/httpd.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=httpd state=restarted
path: roles/apache/handlers/main.yml
To notification, update config.yml
---
- name: copy over httpd configs
copy:
src: httpd.conf
dest: /etc/httpd.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 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:91/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 httpd.conf and change some configuration parameters.
Validate the service restarts on configuration updates by applying the sitewide playbook.
Exercise : Update Apache Configs