Charms are seldom deployed in isolation. Even MediaWiki needs to be connected to a database. Instead, charms are mostly used to model more complex deployments, potentially including many different applications and connections. A Bundle is an encapsulation of this model, or an atomic self-contained part of it. It may be as simple as MediaWiki and a database, or as complex as a full OpenStack cloud. But a bundle encapsulates all the charms and their relationships and enables you to install an entire working deployment just as easily as installing a single charm, whether that's from the Juju Charm Store or by importing a previously exported deployment yourself.
Bundles are deployed on the command line exactly like
Charms, and use the same
deploy command and
juju deploy wiki-simple
You can get the name of a bundle from the Juju Charm Store, just as you would a charm. Unlike charms, bundles embed more than a single application, and you can see icons representing each separate application alongside a bundle's name. This gives you a quick overview of a bundle's complexity and potential resource requirements.
To get a bundle's name, select a bundle on the store and find the 'command
prompt' icon at the top of the pane. Alongside this will be a correctly
bundle command, including the correct Charm Store URL for the
bundle, which you can also run to deploy your chosen bundle:
juju deploy cs:bundle/wiki-simple-4
Bundles are just as easy to use and deploy within the Juju GUI, and the process of adding them from the Charm Store is almost identical to the way you add charms.
From the GUI, open the Store and select the bundle you're interested in. A new pane will display a preview of what the GUI's visual overview will look like with the bundle installed, showing applications and connections. Further details, such as how a bundle supports scaling, can be found below the preview.
Click 'Add to model-name', where model-name is your currently selected model. This will simply add the bundle to your currently selected model.
Before clicking on 'Commit changes' to activate your new bundle, review the configuration of each application by selecting them and making any necessary changes. Click on 'Commit changes' to review the deployment summary followed by 'Deploy' to set those changes in motion. Alternatively, select each application and click 'Destroy' to remove them from bundle prior to activation.
From the GUI, you can easily export and re-import the current model as a local
bundle, encapsulating your applications and connections into a single file. To
do this, click on the 'Export' button alongside your username and model name,
or use the keyboard shortcut “shift-d”. This results in the creation of a file
<model-name>-<year>-<month>-<date>.yaml that your browser will
typically prompt you to save or open.
You can import a saved bundle by either dragging the exported YAML file onto your browser canvas, or using the 'Import' button. After clicking 'Import' your browser will prompt you to select a bundle file.
After a file has been added, the GUI will briefly report
ChangeSet process started followed by
ChangeSet import complete. As with
adding bundles from the store, you may want to review the applications,
connections and various configuration options before clicking on 'Commit
changes' and 'Deploy' to activate your bundle.
After exporting a bundle from the GUI, you can also
deploy the saved bundle
from the command line:
juju deploy bundle.yaml
Unlike when you import and deploy a bundle with the Juju GUI, running
deploy on the command line will not attempt to rename a new application if an
application with the same name already exists.
From the command line, you can also check for errors in a bundle before
deploying it. Bundles downloaded from the Juju store need to be unzipped into
their own directory, and your own YAML files will need to be accompanied by a
README.md text file (although this file can be empty for testing purposes).
You can then check for possible errors with the following command:
charm proof directory-of-bundle/
Note that if no directory is given, the command defaults to the current directory.
If no errors are detected, there will be no output from
charm proof and you
can safely deploy your bundle.
A bundle is a set of applications with a specific configuration and their corresponding relations that can be deployed together in a single step. Instead of deploying a single application, they can be used to deploy an entire workload, with working relations and configuration. The use of bundles allows for easy repeatability and for sharing of complex, multi-application deployments.
As an example, here is a bundle file with a MySQL application and a WordPress application with a relation between the two:
series: xenial description: "A simple WordPress deployment." applications: wordpress: charm: "cs:trusty/wordpress-5" num_units: 1 annotations: "gui-x": "339.5" "gui-y": "-171" to: - "0" mysql: charm: "cs:trusty/mysql-57" num_units: 1 annotations: "gui-x": "79.5" "gui-y": "-142" to: - "1" relations: - - "wordpress:db" - "mysql:db" machines: "0": series: trusty constraints: "arch=amd64 cpu-cores=1 cpu-power=100 mem=1740 root-disk=8192" "1": series: trusty constraints: "arch=amd64 cpu-cores=1 cpu-power=100 mem=1740 root-disk=8192"
To make your bundle as reusable as possible, it's common to set minimum constraints against a charmed application, much like you would when deploying charms from the command line. This is a simple key addition to the application definition, using the proper constraint key/value pair as outlined in the 'constraints' documentation.
For example, to add memory and CPU constraints to a charm in a bundle, the
bundle file would have an additional
constraints field with specific values:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 constraints: mem=2G cpu-cores=4 annotations: "gui-x": "139" "gui-y": "168"
When deploying an application, the charm you use will often support or even require specific configuration options to be set. These options can be set in a bundle as a simple key addition to the application definition, using the configuration key/value pair. See the documentation on application configuration to discover which options are available for the different charms.
For example, to set the flavor of the MySQL charm to Percona in a bundle, the
bundle file would have an additional
options field with specific value:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 options: flavor : percona annotations: "gui-x": "139" "gui-y": "168"
Values for options and annotations can also be read from a file. For binary files, such as binary certificates, there is an option to base64-encode the contents. A file location can be expressed with an absolute or relative (to the bundle file) path. For example:
applications: my-app: charm: some-charm options: config: include-file://my-config.yaml cert: include-base64://my-cert.crt
--bundle-config option can be used when you want to use a
standard bundle but keep model-specific configuration in a separate file. Any
values specified for an application in the file override those values defined
in the bundle, with the exception of the map type values, where the maps are
merged with preference given to the bundle-config file. The file can make use
include-base64:// directives mentioned above for
local bundles (for options and annotation values). A file location can be
expressed with an absolute or relative (to the bundle-config file) path. For
juju deploy wiki-simple --bundle-config ~/model-a/wiki-simple.yaml
Where the contents of
~/model-a/wiki-simple.yaml could look like:
applications: wiki: options: name: "The model-a wiki" mysql: options: "dataset-size": "768M" "rbd-name": mysql-model-a
Note: The bundle-config file currently only supports 'applications' as a top level key.
You can co-locate applications using the placement directive key in the bundle.
Much like application constraints, it requires adding the placement key
the application definition. Where supported by the cloud provider, it is also
possible to isolate charms by including the container format in the placement
directive. Some clouds support LXD.
mysql: charm: "cs:trusty/mysql-57" num_units: 1 to: - lxd:wordpress/0 annotations: "gui-x": "139" "gui-y": "168"
which will install the MySQL application into an LXD container on the same
machine as the wordpress/0 unit. You can check the output from
juju status to
see where each application has been deployed:
Unit Workload Agent Machine Public address Ports Message mysql/0 waiting allocating 0/lxd/0 waiting for machine wordpress/0 waiting allocating 0 10.1.110.193 waiting for machine
Alternatively, to install the MySQL application into an LXD container on machine '1', use the following syntax:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 to: - lxd:1 annotations: "gui-x": "139" "gui-y": "168"
Bundles may optionally include a machine specification, which allows you to set
up specific machines and then to place units of your applications on those
machines however you wish. A machine specification is a YAML object with named
machines (integers are always used for names). These machines are objects with
three possible fields:
Note that the machine spec is optional. Leaving the machine spec out of your bundle tells Juju to place units on new machines if no placement directives are given.
With machines specified, you can place and co-locate applications onto specific machines using the placement key to in the application definition. For example:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 to: - "0 annotations: "gui-x": "139" "gui-y": "168" machines: "0": series: trusty constraints: "arch=amd64 cpu-cores=1 cpu-power=100 mem=1740 root-disk=8192"
which will install the MySQL application on machine 0. You may also specify multiple machines for placing multiple units of an application. For example:
mysql: charm: "cs:trusty/mysql-57" num_units: 2 to: - "0" - "1" annotations: "gui-x": "139" "gui-y": "168" machines: "0": series: trusty constraints: "arch=amd64 cpu-cores=1 cpu-power=100 mem=1740 root-disk=8192" "1": series: trusty constraints: "arch=amd64 cpu-cores=4 cpu-power=500 mem=4096 root-disk=8192"
which will install one unit of the MySQL application on machine 0 and the other on machine 1.
The output from
juju status will show this deployment as follows:
Unit Workload Agent Machine Public address Ports Message mysql/0 waiting allocating 0 waiting for machine mysql/1 waiting allocating 1 waiting for machine wordpress/0 waiting allocating 1 waiting for machine
You can configure more complex networks using spaces
and deploy charms with binding, as described in Deploying applications.
Bindings can also be specified for applications within a bundle. To do so,
add a section to the bundle's YAML file called
bindings. For example:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 bindings: shared-db: database cluster: internal
This is the equivalent of deploying with:
juju deploy cs:trusty/mysql-57 --bind "shared-db=database cluster=internal"
Note: Spaces must be configured in the underlying cloud before attempting to use them.
Prior to Juju 2.1, all deployed machines were regarded as potential hosts for containers, and as a result, all network interfaces connected to those machines were bridged by default. Consequently, bundles created before the release of Juju 2.1 might assume that a container has access to all of the same spaces as the host machine and may no longer deploy cleanly with more recent versions of Juju.
These bundles will need to be updated to be more specific about the bindings required, allowing the operator to specify exactly which charm-defined endpoints should end up in specific places.
deploy command connects charm endpoints to specific spaces and
includes a default space,
default-space, for any interfaces not specified:
juju deploy --bind "default-space db=db-space db-admin=admin-space" mysql
binding section in the bundle's YAML file, the above deploy
command can be mirrored in bundle format with the following:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 bindings: "": default-space db: db-space db-admin: admin-space
It is not currently possible to declare a default space in the bundle for all application endpoints. The workaround is to list all endpoints explicitly.
Charms can define resources; bundles can be used to constrain those resources to specific revisions or to specify local paths to those resources. For example, the following charm's metadata.yaml file specifies a resource:
name: example-charm summary: "example charm." description: This is an example charm. resources: example: type: file filename: example.zip description: "This charm needs example.zip to operate"
If this charm were to be used as part of a bundle, it might be desirable to specify a revision that is specific to the bundle. Revisions are specified in the bundle's YAML file in the corresponding "applications" section.
applications: example-charm: charm: "cs:example-charm" series: trusty resources: example: 1
example-charm charm specifies that it requires a resource called
to operate; the bundle's "applications" section shown above specifies that its
bundle requires, from the charm store, revision 1 of that resource.
The resources section can also specify a local path to a resource:
applications: example-charm: charm: "cs:example-charm" series: trusty resources: example: "./example.zip"
Local paths to resources can be useful in network restricted environments where a Juju controller can not contact the charm store, for example.
After you have tested and deployed your bundle you need to release it to share it with people, this is covered in the charm store documentation.
Someone will come along and review your bundle for inclusion. If you need to speak to a human, there are patch pilots in the Juju IRC channel (#juju on Freenode) who can assist. You can also use the Juju mailing list.
Make sure you've added a brief explanation of what your bundle does within
description field of your bundle's YAML file.