In the previous tutorial, we walked through setting up Ansible on both the control (master) node and on target nodes. Now let’s look at using Ansible capabilities.
Writing Playbooks
One cool thing about Ansible is that hosts can have various roles, and you can layer these roles. So for example you could have roles like this:
- a “common” role that contains tasks to be run on all hosts
- a “backup-client” role for hosts that are backed up
- a “db” role for hosts that operate as database servers
etc. In this tutorial, we’ll have a “common” role that we intend to run on all hosts. That we’ll create another role called ‘db’ for database servers.
In /ansible, create the following playbook file called db.yml:
--- - hosts: db roles: - common
Note that we are using YAML, which is very fussy about syntax, paticular spaces, so if you get an error, make sure it’s as show above.
Modules
Ansible comes with a wide variety of modules, which are packages that can be used to issue commands on target hosts. These cover a ton of common tasks. Some examples:
- the ‘apt’ module can be used to install and remove packages using apt on Debian. There are also ‘yum’, ‘pacman’ and other package manager modules.
- modules such as ‘user’, ‘group’, etc. can add/remove users and groups
- locale_gen, timezone, and other modules can be used for system configuration
- the ‘postgresql*’ set of modules and the ‘mysql*’ modules can be used to add/remove databases, add/remove users, etc.
Etcetera. So if you want to make sure that a certain package is available on your server, you don’t need to code dpkg, apt-get, etc. commands. You can just use the relevant Ansible module.
If something you need is not covered by a stock module, there are also modules for modifying files, making sure a line is present in a file, etc. which allow for configurations not covered by the Ansible distributed modules. And of course you can always copy a script from your control node and execute it.
See the docs for a full list of all modules and capabilities.
Creating a Playbook
Create the following directory structure:
mkdir -p /ansible/roles/common/tasks
Now we’ll create the actual tasks we’re going to execute for the ‘common’ role. In /ansible/roles/common/tasks, create the file main.yml as follows. You do not need to include the comments – they are explanatory text for purposes of this tutorial.
The ‘name’ portion of each task is whatever you choose to call that task. The following line contains the module name followed by a colon, and then specific arguments and options for that module.
--- # this task will run dpkg-reconfigure locales and ensure that # en_US.UTF-8 is present. Modify for your locale. - name: locale generation locale_gen: name=en_US.UTF-8 state=present # this task will run apt-get update - name: apt-get update apt: update_cache=yes # this task will run apt-get upgrade - name: apt-get upgrade apt: upgrade=dist # this task will install some packages we want on all hosts - name: basic packages apt: name=bsd-mailx,bzip2,cron,dnsutils,gpg,git,man,sqlite,unzip,vim,wget,whois,zip state=latest # this task will set the localtime to US/Pacific. Modify to taste. - name: set timezone timezone: name: US/Pacific # this task will make sure that cron is enable in systemd - name: cron enable service: name=cron enabled=yes state=started # this task ensures that root's .bash_profile exists - name: make sure /root/.bash_profile exists file: path: /root/.bash_profile state: touch # this task ensures that 'set -o vi' is in root's .bash_profile - name: set -o vi for root lineinfile: path: /root/.bash_profile state: present regexp: '^set -o vi' line: set -o vi
Then run the ansible-playbook file against db.yml:
root@master:/ansible# ansible-playbook db.yml PLAY [db] ********************************************************************** TASK [Gathering Facts] ********************************************************* ok: [target.example.com] TASK [common : locale generation] ********************************************** ok: [target.example.com] TASK [common : apt-get update] ************************************************* changed: [target.example.com] TASK [common : apt-get upgrade] ************************************************ ok: [target.example.com] TASK [common : basic packages] ************************************************* ok: [target.example.com] TASK [common : set timezone to US/Pacific] ************************************* ok: [target.example.com] TASK [common : cron enable] **************************************************** ok: [target.example.com] TASK [common : make sure /root/.bash_profile exists] *************************** changed: [target.example.com] TASK [common : set -o vi for root] ********************************************* changed: [target.example.com] PLAY RECAP ********************************************************************* target.example.com : ok=9 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Ansible’s Power
Now you may say to yourself “so you’ve set the locale and timezone and installed some packages – so what?” But the key points here are:
- You didn’t have to do it manually.
- Because it’s automated, it’s done identically every single time.
- You can do this as easily on one host as on a thousand hosts.
- Ansible allows you to set roles that baseline your environment. So every single host you use will be setup exactly as you wish.
- You can run these commands every night to make sure no host drifts from the configuration you want. Think of these as “policies” that enforce how you want each server to be configured.
Adding a Database Role
Modify your db.yml to add a db-server role:
--- - hosts: db roles: - common - db-server
Then:
mkdir -p /ansible/roles/db-server/tasks
And create a main.yml there with these tasks:
--- # make sure that postgres is installed - name: postgres package apt: name=postgresql-11,python-ipaddress state=latest # make sure postgresql is configured to start on boot - name: postgres enable service: name=postgresql enabled=yes state=started # enable md5 (password) connections locally - name: modify pg_hba.conf to allow md5 connections postgresql_pg_hba: dest: /etc/postgresql/11/main/pg_hba.conf contype: local users: all databases: all method: md5 state: present
Now run
ansible-playbook db.yml
You’ll see Ansible walk through all the ‘common’ tasks, and then:
TASK [db-server : postgres packages] ******************************************* changed: [target.example.com] TASK [db-server : postgres enable] ********************************************* ok: [target.example.com] TASK [db-server : modify pg_hba.conf to allow md5 connections] ***************** changed: [target.example.com]
Other PostgreSQL modules allow us to create databases, setup users, grant permissions, etc.
Copying Files and Using Templates
Ansible allows the easy distribution of files, either for configuration or application purposes. It also comes with a powerful templating package called Jinja2 that can modify these templates so that they are customized properly for each system.
Let’s add a mail-server role. We’ll use postfix.
Modify db.yml again:
--- - hosts: db roles: - common - db-server - mail-server
Then execute:
mkdir -p /ansible/roles/mail-server/tasks
And edit /ansible/roles/mail-server/tasks/main.yml:
--- # ensure postfix packages are installed - name: postfix package package apt: name=postfix state=latest # template /etc/mailname - name: /etc/mailname template: src=/ansible/src/mailname.j2 dest=/etc/mailname owner=root group=0 mode=0644 # copy /etc/postfix/main.cf - name: postfix main.cf template: src=/ansible/src/main.cf.j2 dest=/etc/postfix/main.cf owner=root group=0 mode=0644 # make sure postfix is configured to start on boot and restart it # in case main.cf is changed - name: postfix enable and restart service: name=postfix enabled=yes state=restarted # set the root: alias in /etc/aliases - name: root alias in /etc/aliases lineinfile: path: /etc/aliases state: present regexp: '^root:' line: 'root: someone@somewhere.com' # run newaliases - name: newaliases command: /usr/bin/newaliases
Now let’s create our templates. In /ansible/src/mailname.j2, enter this text:
{{ ansible_host }}
This is a Jinja2 template (hence the .j2 ending). Text in between the double braces will be replaced with variables. Ansible supports many different variables. In this case we are using ‘ansible_host’ which will be replaced with ‘target.example.com’.
Here is /ansible/src/main.cf.j2, our postfix configuration:
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) biff = no append_dot_mydomain = no readme_directory = no compatibility_level = 2 smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_use_tls=yes smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination myhostname = {{ ansible_node_name }} alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname mydestination = $myhostname, localhost.example.com , localhost relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all inet_protocols = all
Of course many Postfix variables could be set – this is a tutorial on Ansible not Postfix.
Now run:
ansible-playbook db.yml
And after the ‘common’ and ‘db-server’ sections, you’ll see the ‘mail-server’ tasks run:
TASK [mail-server : postfix package package] *********************************** changed: [target.example.com] TASK [mail-server : /etc/mailname] ********************************************* changed: [target.example.com] TASK [mail-server : postfix main.cf] ******************************************* changed: [target.example.com] TASK [mail-server : postfix enable and restart] ******************************** changed: [target.example.com] TASK [mail-server : root alias in /etc/aliases] ******************************** changed: [target.example.com] TASK [mail-server : newaliases] ************************************************ changed: [target.example.com]
And if we look on the system, we see that the proper template substitutions have been made:
root@master:/ansible# cat /etc/mailname master.example.com
Wrap Up
Although we’ve only scratched the surface of Ansible’s capabilities, hopefully you can see the tremendous power and flexibility of the product. With Ansible playbooks, you can both setup new systems quickly and enforce policies on a continuous basis.
Related Posts:
- MetWeb has a 30% Off Deal on Cheap VPS Offers in Utah for Our Readers! - December 21, 2024
- Is Your Soul as Dark as a Christmas Stocking’s Coal?Make Your Online World Match - December 20, 2024
- Hosteroid has a HOT, Limited Stock Offer in Vienna or Amsterdam! - December 19, 2024
Great work!
PS.: The link to the previous tutorial is broken.
Fixed! :)