Simple, reusable charms with Ansible
michael.nelson@canonical.com
The next 30 mins...
Along the way you can be deploying and investigating an example demo charm. If we’ve time at the end and any questions are exhausted, we can also have a quick discussion:
Deploying the example service
Ensure you’ve got a bootstrapped juju environment ready, then:
$ mkdir -p ~/charms/precise && cd ~/charms/precise�$ git clone https://github.com/absoludity/charm-bootstrap-wsgi�$ cd charm-bootstrap-wsgi && make deploy�
You might want to watch the juju-log, or see the README to see how you can do a rolling upgrade, or investigate the playbook.yml, hooks.py or roles.
A brief overview of Ansible for charmers
Ansible - an overview for charmers
From docs.ansible.com:
Ansible’s goals are foremost those of simplicity and maximum ease of use. It also has a strong focus on security and reliability, featuring a minimum of moving parts... and a language that is designed around auditability by humans – even those not familiar with the program.
One thing that stands out and is not mentioned there is in-built support for sharing and reuse of functionality.
A simple task using the ‘apt’ module
- name: Install application dependencies.
apt: pkg={{ item }}
with_items:
- python-oops-wsgi
- python-oops-datedir-repo
- python-oops-amqp
- python-mimeparse
- python-webob
Handling changes to machine state
Ansible modules (like apt, service) are written to be idempotent so that you can notify and handle some tasks only when things change:
handlers:� - name: restart apache� service: name=apache state=restarted
tasks:
- name: template configuration file� template: src=template.j2 dest=/etc/foo.conf� notify:� - restart apache
Other useful ansible features
There are a few other points which are useful when using ansible for Juju charms:
Using Ansible to declare your charm
Symlinking all hooks to hooks.py
Just like the standard charm-helpers python support, all hooks are symlinked to your hooks.py:
$ ls -l hooks
total 8
… charmhelpers
… config-changed -> hooks.py
… hooks.py
… install -> hooks.py
… nrpe-external-master-relation-changed -> hooks.py
… website-relation-changed -> hooks.py
… wsgi-file-relation-changed -> hooks.py
The hooks.py
#!/usr/bin/env python
import sys
import charmhelpers.contrib.ansible
hooks = charmhelpers.contrib.ansible.AnsibleHooks(
playbook_path='playbook.yml')
@hooks.hook('install', 'upgrade-charm')
def install():
"""Install ansible and let it handle the rest of the install/upgrade."""
charmhelpers.contrib.ansible.install_ansible_support(from_ppa=True)
# If you need custom functionality for other hooks which you can’t
# achieve with ansible alone, you can always add them here with the hook
# decorator, but generally there should be no need.
if __name__ == "__main__":
hooks.execute(sys.argv)
What happens when a charm hook is invoked
Whenever one of your hooks is invoked, the following happens for you automatically:
An example - the elasticsearch charm
The hooks.py for the elasticsearch charm is almost the same as the previous example (it has a litle extra functionality in the install hook). The playbook is as follows:
- hosts: localhost
roles:
- role: nrpe
check_name: check_http
check_params: -H localhost -u /_cluster/health -p 9200 -w 2 -c 3 -s green
service_description: "Verify the cluster health is green."
‘roles’ are a way to reuse existing playbook functionality (more on this later). This role ensures that a nagios check will be configured during the nrpe relation.
An example - the elasticsearch charm
handlers:
- name: Restart ElasticSearch
service: name=elasticsearch state=restarted
tasks:
- include: tasks/install-elasticsearch.yml
- include: tasks/peer-relations.yml
- name: Update configuration
tags:
- config-changed
template: src={{ charm_dir }}/templates/elasticsearch.yml
dest=/etc/elasticsearch/elasticsearch.yml
mode=0644 backup=yes
notify:
- Restart ElasticSearch
An example - the elasticsearch charm
- name: Start ElasticSearch
service: name=elasticsearch state=started
tags:
- start
- name: Stop ElasticSearch
service: name=elasticsearch state=stopped
tags:
- stop
- name: Relate the cluster name and host.
tags:
- client-relation-joined
command: >
relation-set
cluster-name={{ cluster_name }}
host={{ ansible_default_ipv4.address }}
port=9200
5 best things about charming with ansible
5 best things about charming with ansible
5. Simple to use and easy to read
4. Audibility of logs
Take a look at the juju log for the wsgi-example/0 unit of your example deployment.
You’ll see it starts with the default charm-helpers logging, but as soon as ansible takes over, you get a nice clean summary of exactly what changed for each hook. You can also ask ansible to give you a diff of what will change if you apply your playbook (great for auditing any cowboyed changes)
5 best things about charming with ansible
3. It’s declarative and has a simple serialization policy
… and tasks are (generally) idempotent.
2. It has an ever-growing library of built-in modules for doing nearly everything you need.
And if you find that what you need isn’t there, your quick solution is to use the command or shell module (which still provides you with idempotency if you’re careful), or to write and share your own module (it’s Python!)
5 best things about charming with ansible
And finally,
1. A great model for reuse and sharing of common tasks...
Reusable charm roles
Reusable charm roles: nrpe-external-master
An ansible role is just a reusable collection of tasks and handlers (and variables and defaults…) in a standard directory layout.
Open up charm-bootstrap-wsgi/playbook.yaml and you’ll see:
- role: nrpe-external-master
check_name: check_http
check_params: "-I 127.0.0.1 -p 8080 -e ' 200 OK' -s 'It works!'
service_description: "Verify wsgi-example is responding."
which means simply, “include the nrpe-external-master role and pass it the following values”. Note also that we can use the role multiple times with different values too (if we need multiple nagios checks)
Reusable charm roles: nrpe-external-master
The nrpe-external-master role has the following tasks:
- name: Write nagios check command config.
tags:
- nrpe-external-master-relation-changed
template:
src: "check_name.cfg.jinja2"
dest: "/etc/nagios/nrpe.d/{{ check_name }}.cfg"
- name: Write nagios check service definition for export.
tags:
- nrpe-external-master-relation-changed
template:
src: "check_name_service_export.cfg.jinja2"
dest: "/var/lib/nagios/export/service__{{ service_context }}-{{ unit_name }}_{{ check_name }}.cfg"
- name: Trigger nrpe-external-master-relation-changed to restart.
tags:
- nrpe-external-master-relation-changed
command: >
relation-set timestamp={{ ansible_date_time.iso8601_micro }}
Reusable charm roles: nrpe-external-master
We can now reuse that role for any charm that provides nagios checks to an nrpe-external-master subordinate.
That’s a small gain, but one which we already had with the charm-helpers python support.
Reusable charm roles: wsgi-app
We’re currently deploying quite a few python wsgi applications. For each deployment we need to:
But only 3, 4 and 5 are really specific to the application...
Reusable charm roles: wsgi-app
Open up the charm-bootstrap-wsgi/playbook.yml and you’ll see the role:
- role: wsgi-app
listen_port: 8080
wsgi_application: example_wsgi:application
code_archive: "{{ build_label }}/example-wsgi-app.tar.bzip2"
when: build_label != ''
That is, if the build_label config option is set, the wsgi-app role is included and it just works…. almost...
Reusable charm roles: wsgi-app
The one thing that the wsgi-app role can’t do for you…
- name: Write any custom configuration files
debug: msg="You'd write any custom config files here"
tags:
- config-changed
# Also any backend relation-changed events...
notify:
- Restart wsgi
...is update any custom configuration files that your code uses on config-changed or any backend relation-changed events.
Reusable charm roles: wsgi-app
You can look at the tasks of the wsgi-app role to see the details of everything that you’re getting for free:
Reusable charm roles: wsgi-app
If you haven’t already, deploy the charm-bootstrap-wsgi service and take a look at the README to see how you can do a rolling upgrade right now.
Let me know if there are any unanswered questions or we can hear about some of the not so great points people have experienced charming with ansible...
Contact details: