This excerpt is from the book, Ubuntu Unleashed: 2013 Edition by Matthew Helmke, published by Pearson/SAMS, Dec 2012, ISBN 0672336243; copyright 2013 by Pearson Education, Inc. For more info please visit www.informit.com/title/0672336243
Juju has been described as APT for the cloud. APT does an amazing job of installing, configuring, and starting complicated software stacks and services, but only as long as all of that happens on only one system. Juju extends this ability across multiple machines. Often, Linux servers are set up for similar tasks. Multiple physical machines may be deployed with similar configurations to work with one another in a network, perhaps for load distribution or redundancy to prevent downtime in the event of one failing or being overloaded. Systems administrators are masters at creating and orchestrating these networks. However, doing so traditionally requires setting up each machine individually, configuring its software settings and so on.
Tools have appeared over the years to help with this great task, such as Chef and Puppet. Juju works to do for servers what package managers do for individual systems. It enables you to deploy services quickly and easily across multiple servers, simplifying the configuration process, and is particularly designed with cloud servers in mind. As with Chef's recipes, those services are deployed using formulas that standardize communication, for example, and which may have been written by different people.
What makes Juju different from Chef and Puppet is that the Juju formulas, called charms, encapsulate services, defining all the ways that services need to expose or consume configuration data to or from other services. This can be done many ways in the Juju charm, including via shell scripts or using Chef itself in solo mode. Also, Juju orchestrates provisioning by tracking its available resources (such as EC2, Eucalyptus, or OpenStack machines) and adding or removing them as appropriate.
Start by installing Juju on a server:
matthew@wolfram~$: sudo apt-get install juju
Next, you must bootstrap the system, configuring it to use either a cloud resource, like Amazon Web Services or EC2 or your local environment (if you are using a local machine for development and testing). The specific information you enter here will differ, but the initial command is always the same:
matthew@wolfram~$: juju bootstrap
The first time this is run, it creates a file,
~/.juju/environments/yaml, which looks something like the following:
default: sample environments: sample: type: ec2 control-bucket: juju-faefb490d69a41f0a3616a4808e0766b admin-secret: 81a1e7429e6847c4941fda7591246594 default-series: precise juju-origin: ppa ssl-hostname-verification: true
The preceding sample was taken directly from the official Juju documentation. Yours will look different in some places and also needs to be adjusted appropriately with your settings. For example, if you are using Amazon AWS, you will probably want to add lines to this file with your AWS access key and secret key so that Juju can access and use your Amazon AWS account. Because the typical Juju user is a DevOps or SysAdmin type who has been doing this sort of thing manually for a while, we will gloss over this step and move on.
Bootstrapping takes a few minutes. If you want to check on the status of your Juju deployment, enter
matthew@wolfram~$: juju status
You see something similar to the following (again from the official juju docs):
machines: 0: agent-state: running dns-name: ec2-50-16-107-102.compute-1.amazonaws.com instance-id: i-130c9168 instance-state: running services:
When the status shows the deployment up and running, it is a good idea to start a debug log session. This is not required, but makes troubleshooting much easier, should it be needed.
matthew@wolfram~$: juju debug-log
Now comes the fun part, deploying service units. We chose a simple one for our sample: deploying a Wordpress blog on our server with all needed services. This is done using charms, which are prepackaged installation and configuration details for specific services.
Here's how it works:
matthew@wolfram~$: juju deploy mysql matthew@wolfram~$: juju deploy wordpress
Now, your services are deployed, but they are not yet connected with each other. We do this by adding relations, in this case:
matthew@wolfram~$: juju add-relation wordpress mysql
Now, if you check your status as shown earlier, you see something like this:
machines: 0: agent-state: running dns-name: localhost instance-id: local instance-state: running services: mysql: charm: cs:precise/mysql-3 relations: db: - wordpress units: mysql/0: agent-state: started machine: 2 public-address: 192.168.122.165 wordpress: charm: cs:precise/wordpress-3 exposed: false relations: db: - mysql units: wordpress/0: agent-state: started machine: 1 public-address: 192.168.122.166
Now, expose your Wordpress service to the world so that you can connect with it from outside the server:
matthew@wolfram~$: juju expose wordpress
And as simple as that, your install is ready. Using the public-address shown earlier in the status message, open 192.168.122.166 in your browser and you should go to your Wordpress configuration page.
What happens if you get your Wordpress blog set up and running and then it suddenly gets popular? In a traditional setting, you would need to reinstall on heftier equipment and migrate the database over. Not here. Instead you just add units:
matthew@wolfram~$: juju add-unit wordpress
This creates a new Wordpress instance, joins the relation with the existing Wordpress instance, discovers in that configuration that it is related to a specific MySQL database, and also relates this one. That's it. One command and you are done!
When a juju-created environment is no longer needed, there is only one command to issue:
matthew@wolfram~$: juju destroy-environment
Beware, this command also destroys all service data, so if you are doing something that is long-term important, make sure you extract your data first.
Charms define how services are to be deployed and integrated, and how they react to events. Juju orchestrates all of this based on the instructions in charms. Charms are created using plain text metadata files. These files, with the extension .yaml, describe the details needed for deployment. These are the supported fields in a charm:
name - The name of the charm.
summary - A one line description.
maintainer - This must include an email address for the main point of contact.
description - A long description of the charm and its features.
provides - Relations that are made available from this charm.
requires - Relations that must already exist for this charm to work.
peers - Relations that work together with this charm.
This sounds complicated, and it is. But with a little study, anyone who knows enough about a service can write a charm for it. Here are example charms for the two services we deployed earlier. First, MySQL:
name: mysql summary: "A pretty popular database" maintainer: "Juju Charmers <firstname.lastname@example.org>" provides: db: mysql
name: wordpress summary: "A pretty popular blog engine" maintainer: "Juju Charmers <email@example.com>" provides: url: interface: http requires: db: interface: mysql
Probably the most confusing part of a charm to most newcomers are the relations. The preceding examples might help clear those up a little bit. As you can see, there are subfields used with relations that define how the relation will work. Here is a list of available subfields for relations:
interface - The type of relation, such as http or mysql. Services will only be permitted to use interfaces listed here to interact with other services.
limit - The maximum number of relations of this kind that will be established to other services.
optional - Denotes whether the relation is required. A value of false means it is not optional.
scope - Controls which units of related-to services can be communicated with via this relation, whether global or container. Container means restricted to units deployed in the same container, specifically subordinate services.
There is also a way to notify a service unit about changes happening in its lifecycle or the larger distributed environment. Called hooks, these are executable files that can query the environment, make desired changes on the local machine, and change relation settings. Hooks are implemented by placing the executable file in the hooks directory of the charm directory. Juju executes the hook based on its filename, when the corresponding event occurs. Hooks are optional. For example, a hook titled install would run just once during the service unit's life, when it was first set up, and it might check whether package dependencies are met. Hooks with titles like start or stop might run when the service is begun or ended. There are possibilities for creating hooks for relations, opening and closing ports, and more.
There are many charms already written and available from the Ubuntu Juju Charm Browser (the link is listed in Resources). You can quickly deploy a Jenkins build integration server or slave, a Hadoop database or node, a MediaWiki instance, a Minecraft game server, and tons more using already -- written and -- available charms. This is probably how most readers will interact with charms.
If you want to try your hand at writing and creating charms for services, you can. Much more detail is available at https://juju.ubuntu.com/docs/write-charm.html to help you learn the process, the semantics, and how to get your charm included in the Charm Store.