Building and deploying a mini charm
A good number of tutorials and explanations about Charmed Operators (“charms”) exist already. And recently, Erik Lönroth has presented a workshop session about deploying a minimal Charm. This tutorial here is the “written down version” of his session.
In case you would also like to present a session or share your experience with juju and charms with the community, please do not hesitate to reach out either on Mattermost or Discourse!
The tutorial uses a very simple source code template to build a minimal charm that just performs an “all done” status message at the end of the installation step. The charm will then be deployed using the Charmed Operator Lifecycle Manager.
In order to deploy a Charm, the Charmed Operator Lifecycle Manager needs a deployment environment. For this purpose, in this tutorial we will use LXD, which is a very easy to use container management environment. More concretely, we will build a charm and deploy it using Juju in a container created using LXD.
Contents:
- Install prerequisites
- Set up a new charm project
- Strip down to mini size
- Test run
- Charm implementation and build
- Deploy the mini charm
- Resolving hiccups
- Wrapping up
- Next steps
Install prerequisites
Install LXD
In order to deploy a Charm, we first need to install the Charmed Operator Lifecycle Manager (OLM). To install the OLM, we need to install a package named juju
(which is why the OLM is often called “juju”). But, as its name indicates, the OLM is a lifecycle manager. That means we also need to configure it with something that it can manage. This can be a public cloud, a private cloud, or a virtualization environment. As mentioned above, in this tutorial we will configure it with LXD.
LXD is a very easy to use container management environment. It runs on many different Linux distributions and it can be used even on Mac and Windows.
The first step in setting up LXD is to install it. On Ubuntu, this can be done in one line, via snap
:
$ sudo snap install lxd
…
The second step in setting up LXD is to initialise it. This can be done very easily using the command lxd init. This brings up an interactive mode. For the purpose of this tutorial, for each question you can simply choose the default values:
$ lxd init
Finally, IPv6 needs to be disabled:
lxc network set lxdbr0 ipv6.address none
Do not execute lxd init on a already set up lxd installation. It will likely reset a number of things including setup networking.
Please note that the lxd package also contains lxc:. Lxd is the name for the management software while lxc is the name for the runtime. In fact you will see lxd and lxc in the remainder of the tutorial depending on which of the two is required at a particular step.
Install Juju
The second prerequisite step is to prepare the tool that the charm will be deployed with. This tool is the Charmed Operator Lifecycle Manager (OLM). Preparing the OLM basically entails installing it. More specifically, we will need to install a package named juju
(which is why the OLM is often called “Juju” or “juju”).
Just like LXD, juju is available on a number of Linux distributions, and can be even installed on other OSes And, just like LXD, on Ubuntu it can be installed very easily via snap
:
$ sudo snap install juju --classic
juju 2.9.19 from Canonical✓ installed
After it has been installed, you can verify the installation by using the juju version
command:
$ juju version
Since Juju 2 is being run for the first time, downloaded the latest public cloud information.
2.9.19-ubuntu-amd64
Now you can check what clouds are known out-of-the-box by juju by using the juju clouds
command:
$ juju clouds --all
You can bootstrap a new controller using one of these clouds...
Clouds available on the client:
Cloud Regions Default Type Credentials Source Description
aws 21 us-east-1 ec2 0 public Amazon Web Services
aws-china 2 cn-north-1 ec2 0 public Amazon China
aws-gov 2 us-gov-west-1 ec2 0 public Amazon (USA Government)
azure 40 centralus azure 0 public Microsoft Azure
azure-china 4 chinaeast azure 0 public Microsoft Azure China
equinix 25 px equinix 0 public
google 21 us-east1 gce 0 public Google Cloud Platform
localhost 1 localhost lxd 0 built-in LXD Container Hypervisor
oracle 4 us-phoenix-1 oci 0 public Oracle Compute Cloud Service
For the managed clouds, juju uses a controller and if the default controller initialization for lxd did not perform so far (as it can be seen with the message You can bootstrap...
), you can create a new controller with the juju bootstrap
command.
As with LXD, this will bring up an interactive mode and, for the purpose of this tutorial, it is fine to simply choose at every step the default value:
$ juju bootstrap
Clouds
aws
aws-china
aws-gov
azure
azure-china
equinix
google
localhost
oracle
Select a cloud [localhost]:
Enter a name for the Controller [localhost-localhost]:
Creating Juju controller "localhost-localhost" on localhost/localhost
Looking for packaged Juju agent version 2.9.19 for amd64
Located Juju agent version 2.9.19-ubuntu-amd64 at https://streams.canonical.com/juju/tools/agent/2.9.19/juju-2.9.19-ubuntu-amd64.tgz
To configure your system to better support LXD containers, please see: https://github.com/lxc/lxd/blob/master/doc/production-setup.md
Launching controller instance(s) on localhost/localhost...
- juju-2be1c8-0 (arch=amd64)
Installing Juju agent on bootstrap instance
Fetching Juju Dashboard 0.8.1
Waiting for address
Attempting to connect to 10.199.245.184:22
Connected to 10.199.245.184
Running machine configuration script...
Bootstrap agent now started
Contacting Juju controller at 10.199.245.184 to verify accessibility...
Bootstrap complete, controller "localhost-localhost" is now available
Controller machines are in the "controller" model
Initial model "default" added
You can check again, what juju currently supports. Now, a local cloud on localhost
with type lxd
should be present:
$ juju clouds --all
Clouds available on the controller:
Cloud Regions Default Type
localhost 1 localhost lxd
Clouds available on the client:
Cloud Regions Default Type Credentials Source Description
aws 21 us-east-1 ec2 0 public Amazon Web Services
aws-china 2 cn-north-1 ec2 0 public Amazon China
aws-gov 2 us-gov-west-1 ec2 0 public Amazon (USA Government)
azure 40 centralus azure 0 public Microsoft Azure
azure-china 4 chinaeast azure 0 public Microsoft Azure China
cloudsigma 12 dub cloudsigma 0 public CloudSigma Cloud
equinix 25 px equinix 0 public
google 21 us-east1 gce 0 public Google Cloud Platform
localhost 1 localhost lxd 1 built-in LXD Container Hypervisor
oracle 4 us-phoenix-1 oci 0 public Oracle Compute Cloud Service
rackspace 6 dfw rackspace 0 public Rackspace Cloud
As you can see, the cloud of type lxd is present in the output. Juju will coordinate with lxd for deploying charms and its applications.
Install Charmcraft
This brings us to our third prerequisite for this tutorial, namely, a tool for packaging applications as charms. There are multiple ways one can do this. In this tutorial we will do this in the simplest possible manner, using the Charmed Operator Software Development Kit (SDK), specifically, the tool named charmcraft
: It is used to build and package Charms. Just like LXD and the OLM, on Ubuntu the SDK’s charmcraft
tool can be installed very conveniently via snap:
$ sudo snap install charmcraft --classic
charmcraft 1.2.1 from Canonical✓ installed
Get Erik’s example code base
The last prerequisite is Erik’s git repository with example code. It contains one Charm implementation which will be used further on in this tutorial. For this tutorial it is required to check out this repository. It can be done with using git:
$ git clone https://github.com/erik78se/juju-operators-examples/
Alternatively, you can navigate to the Github repository in the Web browser and download the archive directly from the Web page. Git control is actually not required, thus a plain download works as well.
This is it for prerequisites, all should be set now to start with the tutorial.
Set up a new charm project
After the prerequisites have been set we can use the charmcraft tool to create an initial Charm with the built-in template. It starts with an creation of an empty folder and the execution the initialisation provided by the charmcraft tool inside this folder:
$ mkdir mini
$ cd mini
$ charmcraft init
Charm operator package file and directory tree initialized.
TODO:
CONTRIBUTING.md:
README.md: Describe your charm in a few paragraphs of Markdown
README.md: Provide high-level usage, such as required config or relations
README.md: Provide any relations which are provided or required by your charm
README.md: Include a link to the default image your charm uses
actions.yaml: change this example to suit your needs.
config.yaml: change this example to suit your needs.
metadata.yaml: fill out a display name for the Charmcraft store
metadata.yaml: fill out the charm's description
metadata.yaml: fill out the charm's summary
metadata.yaml: replace with containers for your workload (delete for non-k8s)
metadata.yaml: each container defined above must specify an oci-image resource
src/charm.py: change this example to suit your needs.
src/charm.py: change this example to suit your needs.
src/charm.py: change this example to suit your needs.
If the charmcraft init
returns the error Author not given, and nothing in GECOS field
, you could set the author information with using charmcraft:
§ charmcraft init --author yourname
Strip down to mini size
The result of the previous step should be a collection of files and folders. Remember, this tutorial has the goal to build a minimal Charm. Therefore, let’s consider deleting files which are not necessary for building a minimal Charm: actions.yaml
, config.yaml
and the folder tests
.
To be very clear: it is basic practise (not good, but basic practise!) to use actions, declare configuration values and write tests. The reason for this step in this tutorial is to make a very minimal setup for experimentation only.
As an overview, consider the following listing:
$ ls -al
-rw-rw-r-- 1 sam sam 99 Nov 12 10:19 .flake8
-rw-rw-r-- 1 sam sam 55 Nov 12 10:19 .gitignore
-rw-rw-r-- 1 sam sam 24 Nov 12 10:19 .jujuignore
-rw-rw-r-- 1 sam sam 910 Nov 12 10:19 CONTRIBUTING.md
-rw-rw-r-- 1 sam sam 11358 Nov 12 10:19 LICENSE
-rw-rw-r-- 1 sam sam 528 Nov 12 10:19 README.md
-rw-rw-r-- 1 sam sam 472 Nov 12 10:19 actions.yaml <-remove-this
-rw-rw-r-- 1 sam sam 233 Nov 12 10:19 charmcraft.yaml
-rw-rw-r-- 1 sam sam 419 Nov 12 10:19 config.yaml <-remove-this
-rw-rw-r-- 1 sam sam 653 Nov 12 10:20 metadata.yaml
-rw-rw-r-- 1 sam sam 36 Nov 12 10:19 requirements-dev.txt
-rw-rw-r-- 1 sam sam 13 Nov 12 10:19 requirements.txt
-rwxrwxr-x 1 sam sam 344 Nov 12 10:19 run_tests
drwxrwxr-x 2 sam sam 4096 Nov 12 10:19 src
drwxrwxr-x 2 sam sam 4096 Nov 12 10:19 tests <-remove-this
In addition, open the file metadata.yaml
and delete all fields below summary. This file shall look like the following text:
# Copyright 2021 yourusername
# See LICENSE file for licensing details.
# For a complete list of supported options, see:
# https://discourse.charmhub.io/t/charm-metadata-v2/3674/15
name: mini
display-name: |
mini
description: |
this is the tutorial!
summary: |
some summary here
Test run
Now you are ready to execute the first build of the Charm
$ charmcraft build
Packing charm 'mini_ubuntu-20.04-amd64.charm'...
Created 'mini_ubuntu-20.04-amd64.charm'.
With 1.5.0 of charmcraft
charmcraft build
is deprecated in favour of charmcraft pack
.
Accordingly, this build will do the minimum of what can be packaged. What should happen now is that juju requests lxc to deploy a container for building the Charm. In order to check what happens on the level of lxc, you can check (with a different shell, of course) listing the containers:
$ lxc list --project charmcraft
+-----------------------------------+---------+-----------------------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-----------------------------------+---------+-----------------------+------+-----------+-----------+
| charmcraft-mini-1322311-0-0-amd64 | RUNNING | 10.222.236.175 (eth0) | | CONTAINER | 0 |
+-----------------------------------+---------+-----------------------+------+-----------+-----------+
After the build has been done, no errors should return. The new Charm should be there, as you can check with a listing for the presence of the new file: mini_ubuntu-20.04-amd64.charm
. That is done as a first test, but it will not show anything particular.
Charm implementation and build
You do not need to edit the main charm implementation file at src/charm.py
. Rather, copy from the repository as downloaded in step3 the entire contents from deploy-minimal/src/charm.py
and replace the contents of your new Charm implementation in mini/src/charm.py
.
Let’s review the implementation for a moment: Note that all charms are inheriting from the charm base as declared at the line 14 class DeployMinimalCharm(CharmBase):
.
The next point in the source code we would like to look at is the install hook. Whenever install is called, the charm method at line 27 def _on_install(self, event):
is being executed. Take the time to check on a few more hooks, for example you can see the hook for changes to the configuration on line 34 def _on_config_changed(self, event):
.
If you consider all the hook methods you will also notice two main things: the logging and the setting of some status value. It is important that at every function the status value is being set.
The mini Charm at mini/src/charm.py
would be OK for now, we can go ahead with building the charm again:
$ charmcraft build
Packing charm 'mini_ubuntu-20.04-amd64.charm'...
Created 'mini_ubuntu-20.04-amd64.charm'.
With 1.5.0 of charmcraft
charmcraft build
is deprecated in favour of charmcraft pack
.
Deploy the mini charm
After the build has been executed successfully, the charm file mini_ubuntu-20.04-amd64.charm
should have been created. As the next step, the Charm can be deployed using juju, note that you point to the charm with the prefix ./
in order to let juju pick that file from your local file system:
$ juju deploy ./mini_ubuntu-20.04-amd64.charm
Located local charm "mini", revision 0
Deploying "mini" from local charm "mini", revision 0
If you do not use the prefix
./
but just write the plain charm name, juju will try to look for the Charm on Charmhub.io (and likely not find it there).
In order to check if everything went fine, the status of juju can be checked:
$ juju status
Model Controller Cloud/Region Version SLA Timestamp
default overlord localhost/localhost 2.9.17 unsupported 10:48:41+01:00
App Version Status Scale Charm Store Channel Rev OS Message
cassandra 3.11.11 active 1 cassandra charmhub stable 60 ubuntu Live seed
mini waiting 0/1 mini local 0 ubuntu waiting for machine
Unit Workload Agent Machine Public address Ports Message
cassandra/0* active idle 0 10.222.236.232 9042/tcp,9160/tcp Live seed
mini/0 waiting allocating 5 10.222.236.117 waiting for machine
Machine State DNS Inst id Series AZ Message
0 started 10.222.236.232 juju-92a670-0 focal Running
5 pending 10.222.236.117 juju-92a670-5 focal Running
Most likely its status is at waiting for a machine
, if you execute the commands quickly after each other it is very likely that you see in the status this message first until it is done.
Resolving hiccups
If the status does not change and this the deployment feels like it failed, there is a number of standard steps you can use:
- You can always check with the debug log of juju to see some suspicious output or error message. Use
juju debug-log
. - Depending on your machine building and deploying can take minutes, as with many things in server administration check with tools that something is actually working. If you deployed the lxd on your local machine, tools like
top
orhtop
for showing the cpu and memory utilisation can be useful to understand what is happening on your machine. If you use a remote cloud with juju, then you must have a look there accordingly. - Check source code again if you see deviation from the example or you missed saving the file after editing.
- Rebuild with a quick
charmcraft build
to make sure you got everything after you properly saved the file. - If you feel, something got really stuck, you can consider to remove the application with executing
juju remove-application mini
. - If there is no progress after trying to remove an application, you could try
juju remove-application mini --force
. - After an application has been removed, you could execute the deployment again
juju deploy ./mini_ubuntu-20.04-amd64.charm
. - check the progress and the status with
juju status
.
Wrapping up
If everything ran successfully, the status of the juju should look like the following:
$ juju status
Model Controller Cloud/Region Version SLA Timestamp
deploy-minimal overlord localhost/localhost 2.9.17 unsupported 11:18:18+01:00
App Version Status Scale Charm Store Channel Rev OS Message
mini active 1 mini local 2 ubuntu Step: 3/3
Unit Workload Agent Machine Public address Ports Message
mini/1* active idle 1 10.222.236.247 Step: 3/3
Machine State DNS Inst id Series AZ Message
1 started 10.222.236.247 juju-b06cf6-1 focal Running
Note that the message should be Step: 3/3
. If that is there the deployment ran successfully and everything is fine – you have deployed this very simple Charm!
Next steps
If you are wondering… | visit… |
---|---|
“How do I…?” | Juju SDK How-to docs |
“What is…?” | Juju SDK Reference docs |
“Why…?”, “So what?” | Juju SDK Explanation docs |
Finally, if your question is “How can I learn more about managing charms operations with Juju?”, consider also the Juju OLM docs!
Last updated 9 months ago.