Although charms can be deployed in isolation, they are typically used alongside
other charms in order to implement more complex solutions, whether it be as
simple as MediaWiki and a database, or as complex as a full OpenStack cloud. A
charm bundle, or just bundle, is an encapsulation of such a compound
deployment and includes all the associated relations and configurations that
the deployment requires. A huge plus is that a bundle is installed exactly like
a charm is: with the
deploy command or via the GUI (see
Adding bundles with the GUI).
A bundle is defined with a file in YAML format and is often called the "bundle file". Here is a bundle file with "charm definitions" for MySQL and WordPress 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 cores=1 cpu-power=100 mem=1740 root-disk=8192" "1": series: trusty constraints: "arch=amd64 cores=1 cpu-power=100 mem=1740 root-disk=8192"
A Kubernetes bundle differs from a standard bundle in the following ways:
- key 'bundle' is given the value of 'kubernetes'
- key 'num_units' is replaced by key 'scale'
- key 'to' is replaced by key 'placement'
The value of 'placement' is a key=value pair and is used as a Kubernetes node selector.
bundle: kubernetes applications: mariadb: charm: cs:~wallyworld/mariadb-k8s scale: 2 constraints: mem=1G options: dataset-size: 70% storage: database: 20M,mariadb-pv gitlab: charm: cs:~wallyworld/gitlab-k8s placement: foo=bar scale: 1 relations: - - gitlab:mysql - mariadb:server
A bundle is deployed just like a regular charm is:
juju deploy wiki-simple
See the Deploying applications page for details on the
To get a summary of the deployment steps (without actually deploying) a dry run can be performed:
juju deploy --dry-run wiki-simple
--dry-run option works only with bundles, not with regular charms.
You can get the name of a bundle from the Juju Charm Store, just as you would a charm. There, you can see icons representing each separate application alongside the 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. A field will contain the Charm Store URL for the bundle, which you can also use to deploy:
juju deploy cs:bundle/wiki-simple-4
cs signifies "charm store".
Bundles can also be deployed by referring to a local bundle file (if it exists). We'll see this in the Creating bundles section.
Below we present two ways in which existing bundles can be tweaked for your environment:
- Setting charm constraints in a bundle
- Setting charm configuration options in a bundle
To make a bundle as reusable as possible, it's common to set minimum
constraints for its associated charms, much like you would when deploying
charms from the command line. This is done by including a
to a charm's definition.
For example, to add memory and CPU constraints to the 'mysql' charm:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 constraints: mem=2G cores=4 annotations: "gui-x": "139" "gui-y": "168"
Here we show how to colocate applications along with constrained LXD containers on a single machine:
applications: wordpress: charm: "cs:trusty/wordpress-5" num_units: 1 constraints: mem=1G cores=1 annotations: "gui-x": "339.5" "gui-y": "-171" to: - lxd:0 mysql: charm: "cs:trusty/mysql-57" num_units: 1 constraints: mem=2G cores=2 annotations: "gui-x": "79.5" "gui-y": "-142" to: - lxd:0 relations: - - "wordpress:db" - "mysql:db" machines: "0": series: xenial constraints: "arch=amd64 mem=4G cores=4"
Refer to the Using constraints page for in-depth coverage of constraints.
When deploying an application, the charm you use will often support or even
require specific configuration options to be set. This is done by including an
options field to a charm's definition.
For example, to set the 'flavor' of the MySQL charm to 'percona':
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
See section Discovering application configuration options to learn about a charm's options.
Bundles can continue to be modified to the point that you are effectively creating a new bundle. This section presents the following methods:
- Using local charms
- Overlay bundles
- Bundle placement directives
- Machine specifications in a bundle
- Recycling machines
- Binding endpoints within a bundle
- Bundles and charm resources
Note: Make sure you've added a brief explanation of what your bundle does within the
description field of your bundle's YAML file.
To integrate a local charm into a bundle a local bundle file, say
bundle.yaml, will be needed and where the
charm field points to the
directory of the charm in question. An absolute or a relative (to the bundle
file) path can be used. Here is an example:
series: xenial applications: mysql: charm: "/home/ubuntu/charms/mysql" num_units: 1 constraints: mem=2G cores=4
The bundle can then be deployed by using the file as the argument instead of a bundle name:
juju deploy bundle.yaml
--overlay option can be used when you want to use a standard bundle but
keep model-specific configuration in a separate file. The overlay files
constitute bundles in their own right. The "overlay bundle" can specify new
applications, change values, and also specify the removal of an application in
the base bundle.
An application is removed from the base bundle by defining the application name in the application section, but omitting any values. Removing an application also removes all the relations for that application.
If a machines section is specified in an overlay bundle it replaces the corresponding section of the base bundle. No merging of machine information is attempted. Multiple overlay bundles can be specified and they are processed in the order they appear on the command line.
juju deploy wiki-simple --overlay ~/model-a/wiki-simple.yaml
You can co-locate applications using the placement key
to in the charm's
definition. When LXD is supported by the backing cloud it is also possible to
isolate charms by including the container format in the placement directive.
mysql: charm: "cs:trusty/mysql-57" num_units: 1 to: - lxd:wordpress/0 annotations: "gui-x": "139" "gui-y": "168"
This will install the MySQL application into a 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 MySQL into a 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 application units on those machines
however you wish. This is done by including a
machines field at the root of
the bundle file and then defining machines based on an integer. These machines
are objects with three possible fields:
annotations. Finally, these machines are referred to in a charm's definition
by using the placement key
to. 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 cores=1 cpu-power=100 mem=1740 root-disk=8192"
This will install the MySQL application on machine '0', which has been assigned a specific series and a collection of constraints.
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 cores=1 cpu-power=100 mem=1740 root-disk=8192" "1": series: trusty constraints: "arch=amd64 cores=4 cpu-power=500 mem=4096 root-disk=8192"
This 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
To have a bundle use a model's existing machines, as opposed to creating new
--map-machines=existing option is used. In addition, to specify
particular machines for the mapping, comma-separated values of the form
'bundle-id=existing-id' can be passed where the bundle-id and the existing-id
refer to top level machine IDs.
For example, consider a bundle whose YAML file is configured with machines 1, 2, 3, and 4, and a model containing machines 1, 2, 3, 4, and 5. The following deployment would use existing machines 1 and 2 for bundle machines 1 and 2 but use existing machine 4 for bundle machine 3 and existing machine 5 for bundle machine 4:
juju deploy some-bundle --map-machines=existing,3=4,4=5
Generally, you can configure more complex networks using
Network spaces and deploy charms with a binding, as described
in Deploying to spaces. However, the same can
also be achieved with a bundle and is done by including a
bindings field to a
charm's definition. For example:
mysql: charm: "cs:trusty/mysql-57" num_units: 1 bindings: shared-db: database cluster: internal
This is equivalent to:
juju deploy cs:trusty/mysql-57 --bind "shared-db=database cluster=internal"
The following connects charm endpoints to specific spaces and includes a
default-space, for any interfaces not specified:
juju deploy --bind "default-space db=db-space db-admin=admin-space" mysql
Using a bundle file, the above deploy command can be mirrored 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.
Bundles support charm resources (see Using resources)
through the use of the
resources field. For example, consider the following
charm's metadata.yaml file that 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 use
a specific revision for the bundle. Revisions are specified in the bundle file
under the charm's field in the
applications: example-charm: charm: "cs:example-charm" series: trusty resources: example: 1
So the charm specifies that it requires a resource called
example and the
bundle stipulates a revision of '1' of that resource (from the Charm Store).
resources field can also specify a local path to a resource instead:
applications: example-charm: charm: "cs:example-charm" series: trusty resources: example: "./example.zip"
Local paths to resources can be useful, for example, in network restricted environments where a Juju controller is unable to contact the Charm Store.
To compare a model's configuration to that of a bundle the
command is used.
Consider, for example, a model that has the below output to the
Model Controller Cloud/Region Version SLA Timestamp docs lxd localhost/localhost 2.5-rc1 unsupported 05:22:22Z App Version Status Scale Charm Store Rev OS Notes haproxy unknown 1 haproxy jujucharms 46 ubuntu mariadb 10.1.37 active 1 mariadb jujucharms 7 ubuntu mediawiki 1.19.14 active 1 mediawiki jujucharms 19 ubuntu Unit Workload Agent Machine Public address Ports Message haproxy/0* unknown idle 2 10.86.33.28 80/tcp mariadb/0* active idle 1 10.86.33.192 ready mediawiki/0* active idle 0 10.86.33.19 80/tcp Ready Machine State DNS Inst id Series AZ Message 0 started 10.86.33.19 juju-dbf96b-0 trusty Running 1 started 10.86.33.192 juju-dbf96b-1 trusty Running 2 started 10.86.33.28 juju-dbf96b-2 bionic Running Relation provider Requirer Interface Type Message haproxy:peer haproxy:peer haproxy-peer peer mariadb:cluster mariadb:cluster mysql-ha peer mariadb:db mediawiki:db mysql regular mediawiki:website haproxy:reverseproxy http regular
Now say we have a bundle file
bundle.yaml with these contents:
applications: mediawiki: charm: "cs:trusty/mediawiki-5" num_units: 1 options: name: Central library mysql: charm: "cs:trusty/mysql-55" num_units: 1 options: "binlog-format": MIXED "block-size": 5 "dataset-size": "512M" flavor: distro "ha-bindiface": eth0 "ha-mcastport": 5411 "max-connections": -1 "preferred-storage-engine": InnoDB "query-cache-size": -1 "query-cache-type": "OFF" "rbd-name": mysql1 "tuning-level": safest vip_cidr: 24 vip_iface: eth0 relations: - - "mediawiki:db" - "mysql:db"
Comparison of the currently active model with the bundle can be achieved in this way:
juju diff-bundle bundle.yaml
This produces an output of:
applications: haproxy: missing: bundle mariadb: missing: bundle mediawiki: charm: bundle: cs:trusty/mediawiki-5 model: cs:mediawiki-19 series: bundle: "" model: trusty options: name: bundle: Central library model: null mysql: missing: model machines: "0": missing: bundle "1": missing: bundle "2": missing: bundle relations: bundle-additions: - - mediawiki:db - mysql:db model-additions: - - haproxy:reverseproxy - mediawiki:website - - mariadb:db - mediawiki:db
This informs us of the differences in terms of applications, machines, and
relations. For instance, compared to the model, the bundle is missing
applications 'haproxy' and 'mariadb', whereas the model is missing 'mysql'.
Both model and bundle utilise the 'mediawiki' application but they differ in
terms of configuration. There are also differences being reported in the
'machines' and 'relations' sections. We'll now focus on the 'machines' section
in order to demonstrate other features of the
We can extend the bundle by including a bundle overlay. Consider an overlay
changes.yaml with these machine related contents:
applications: mediawiki: to: - "2" mysql: to: - "3" machines: "2": series: trusty constraints: "arch=amd64 cores=1" "3": series: trusty constraints: "arch=amd64 cores=1"
Here, by means of the
--overlay option, we can add this extra information to
the comparison, effectively inflating the configuration of the bundle:
juju diff-bundle bundle.yaml --overlay changes.yaml
This changes the 'machines' section of the output to:
machines: "0": missing: bundle "1": missing: bundle "2": series: bundle: trusty model: bionic "3": missing: model
The initial comparison displayed a lack of all three machines in the bundle. By adding machines '2' and '3' in the overlay the output now shows machines '0' and '1' as missing in the bundle, machine '2' differs in configuration, and machine '3' is missing in the model.
As with the
deploy command, there is the ability to map machines in the
bundle to those in the model. Below, the addition of
makes, for the sake of the comparison, bundle machines 2 and 3 become model
machines 0 and 1, respectively:
juju diff-bundle bundle.yaml --overlay changes.yaml --map-machines=2=0,3=1
The 'machines' section now becomes:
machines: "2": missing: bundle
The bundle shows as only missing machine 2 now, which makes sense.
The target bundle can also reside within the online Charm Store. In that case you would simply reference the bundle name, such as 'wiki-simple':
juju diff-bundle wiki-simple
If you have created your own bundle you will probably want to save it. This is
done with the
export-bundle command, which exports a single model
For example, to export the currently active model into file
juju export-bundle --filename mymodel.yaml
You can also use the Juju GUI to save a bundle. See Adding bundles with the GUI for instructions.
Once the bundle is saved you can consider these Next steps.