A Journey With Rock Crafting

Anas Husseini
7 min readFeb 16, 2024
A “Cool Rock” Photo by Emile Guillemot on Unsplash

In a journey to a achieving better container images, let’s stop in a cozy corner where we will dive into the fascinating world of Rockcraft. If you’re a bit of a container enthusiast, which I’m guessing you are, you are in for a treat. Today, we are going to unravel some of the secrets of building secure and mini-sized images, affectionately known as “rocks”, using Rockcraft.

So, what’s Rockcraft, I hear you ask? It is a very crafty tool in your developer’s toolkit, something like a Swiss Army knife for creating super-efficient and secure container images. It is like a professional cook that you gives the recipe (aka rockcraft.yaml), then proceeds to fetch the ingredients, then cook, and finally produce the cake. The only drawback is you may lose a tooth or two if you try a bite, as that cake will be as hard as a “rock”.

Get Ready for the Hike!

And if you are wondering what makes “rocks” special, there are 3 main reasons:
- It is based on Ubuntu LTS images for a higher security.
- It uses chiselled packages to include only necessary files and binaries.
- It has a cute embedded service manager called Pebble!

Before we start, we’ll will make a checklist of the items we need in our little journey:
- Ubuntu system or one of its flavors/derivatives
- Docker needed to test and run our rock
- LXD required internally by Rockcraft

Next, let’s install Rockcraft on our system. The most recommended way is by using snap, ensure that we will get the latest stable Rockcraft version:

snap install rockcraft --classic

Next let’s create an empty folder and move inside it. We will proceed to initiate our first rock:

mkdir my-first-rock
cd my-first-rock
rockcraft init

One YAML to Conquer Them All!

Rockcraft’s init command will create a rockcraft.yaml file in our folder, containing a template ready to be filled in. This yaml file is the recipe to cook our rock. You could think of it as a Dockerfile, but unlike a Dockerfile, it uses a declarative language, allowing for a more efficient image building.

Looking at our auto-created rockcraft.yaml, it should look like this:

name: my-rock-name # the name of your rock
base: ubuntu@22.04 # the base environment for this rock
version: '0.1' # just for humans. Semantic versioning is recommended
summary: Single-line elevator pitch for your amazing rock # 79 char long summary
description: |
This is my my-rock-name's description. You have a paragraph or two to tell the
most important story about it. Keep it under 100 words though,
we live in tweetspace and your description wants to look good in the
container registries out there.
license: GPL-3.0 # your application's SPDX license
platforms: # The platforms this rock should be built on and run on
amd64:
parts:
my-part:
plugin: nil

Fields like name, version, summary, description and license are informative fields and can be changed according to the rock we are trying to make.

Secure The Base!

Moving on, the base field allows us to choose which LTS version we want to base our rock on. At the time of writing this article, the supported versions are ubuntu@20.04, ubuntu@22.04 and ubuntu@24.04.

But wait! base also allows for a very cool value called bare. Imagine you want to put only your application files inside your rock, and include only the required libraries for your application to run, and not the whole system. With Docker, you would go about that with something like the following in your Dockerfile:

FROM ubuntu:22.04 AS builder
RUN mkdir /rootfs
# do things in your /rootfs folder

FROM scratch
COPY --from=builder=/rootfs /
ENTRYPOINT […]

However with Rockcraft, just use base: bare. Along with another field called build-base, base: bare will do the same magic as above. build-base will tell Rockcraft which Ubuntu image will be used to create your rock, but other than that, your image will be stripped out from all other unnecessary files:

base: bare
build-base: ubuntu@22.04

Pretty neat, huh?

build-base supports an extra value called devel, which points to the next Ubuntu version that is yet to be released.

The next field that may have caught your attention is platforms. It allows you to build rocks for multiple architectures at the same time (if your system allows it). You will get a separate rock file for each architecture you build for. There are several architectures supported: amd64, arm64, armhf, i386, riscv64, ppc64el and s390x.

Plug-in-Part!

Now let’s come to juicy part, the part where you make the sauce! Conveniently, that part is called parts. This is where you put the files and packages you want to have in your rock. You can have multiple parts, each with a different name, and each part needs to have a plugin, which can be nil.

Secret Agent nil

If you want just to install packages inside your part, either apt packages from ubtunu repositories or slices (but not both at the same time), then the nil plugin is your friend. For instance:

parts:
nodejs:
plugin: nil
stage-packages:
- nodejs_bins

This will install the nodejs_bins slice inside your part, which contains all the necessary files to run Nodejs. By the way, you can discern a slice from an apt package by the underscore, since slice names must have an underscore. stage-packages field means that we are asking for that package to be installed in the stage step. There are 5 steps involved in rock packing process, but let’s not delve too deep into that right now.

Other Plugins to Explore

Unsurprisingly, the nil plugin is not the only plugin you can use in your part. But what’s a plugin, I hear you say? It is a build environment already prepared for your configuration and use. If you want to build a python application in your rock, the python plugin will do that for you, courtesy of Rockcraft’s parts model.

There are a variety of plugins currently supported in Rockcraft parts. The list includes, but is not limited to: nil, python, npm, make, qmake, cmake, go, rust, java and dotnet.

Another example is the python plugin, you can also have it come pre-installed with whatever python packages you need with a plugin property called python-packages. Alternatively, you can use python-requirements to point to the location(s) of your requirements file(s).

Let’s try that right away, shall we? Let’s create a simple Flask project in a new folder called src inside my-first-rock folder. We tell it to install flask package via python-packages. Furthermore, we will need the python3-venv installed through stage-packages.

parts:
python-runtime:
plugin: python
source: src/
python-packages:
- flask
stage-packages:
- python3-venv

However, for our first journey we want to try something simple, like running a single python file. The python plugin is made to build Python projects, so it will look for something like setup.py and pyproject.toml and build accordingly. Since we aren’t going that far, we are just using the python plugin as a quick way to install Python and Flask.

Inside the src folder, create a file called hello.py with the following contents:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"

Finally, we want to copy the contents of our local src folder to the root of our image. That can be done by adding a second part that uses the dump plugin:

parts:
python-runtime:
plugin: python
source: src/
python-packages:
- flask
stage-packages:
- python3-venv

hello-app-files:
plugin: dump
source: src/

Et voilà! It is simplicity itself, my dear Watson!

Thank You for Your Service!

Now that we have our files and packages already well-tidied in our rock image, we need to run our Flask application. Our tiny friend Pebble comes to shine at this moment, because it will take care of running everything under the hood through the field services. In our case, we will use something as simple as:

services:
flask-service:
startup: enabled
override: replace
command: flask --app /hello run --host=0.0.0.0

This will make Flask runs the single application file we have at /hello.py inside the rock.

Now our complete rockcraft.yaml should look like this:

name: my-first-rock
base: ubuntu@22.04
version: '1.0'
summary: My first amazing rock
description: |
This is my first rock. Deal with it.
license: MIT
platforms:
amd64:

services:
flask-service:
startup: enabled
override: replace
command: flask --app /hello run --host=0.0.0.0

parts:
python-runtime:
plugin: python
source: src/
python-packages:
- flask
stage-packages:
- python3-venv

hello-app-files:
plugin: dump
source: src/

Cook It Nice and Slow!

We are ready to cook as all the ingredients are ready in the pot. Start the fire by running:

rockcraft pack

This may take a couple of minutes, so lay back and contemplate the beauty of the nature around or your incomparable genius, whichever way you swing.

When Rockcraft successfully finishes packing, you will get your prize: a nice rock image named my-first-rock_1.0_amd64.rock.

Run It for Your Life!

Unfortunately, we cannot run the rock image directly in a container manager such as Docker. However, a nifty tool called skopeo can convert our rock, an oci archive, to a Docker image. Luckily for us, skopeo comes installed within the Rockcraft snap. Run the following command:

skopeo --insecure-policy copy oci-archive:my-first-rock_1.0_amd64.rock docker-daemon:my-first-rock:1.0

Let’s run this Docker image right away:

docker run -d -p 5000:5000 --name first-rock my-first-rock:1.0

This will launch our flask app in our rock, which we can access on a browser at http://localhost:5000.

Rock Hard!

After this rather, hopefully, enjoyable journey, I hope we learned together a thing or two about rocks and how to craft them. There is much more to discover about them rocks, so here are a few tips on what to do next:

- Subscribe to the Rocks Discourse to stay updated with rock news!
- Hop in the Rocks Matrix server to talk to Rockstars, seek help or just to say hello!
- Learn more about Rockraft through its official documentation!
- Join the Rocks Community and help creating and mainting rocks!

--

--

Anas Husseini

A programmer who loves comedy for dinner, and a writer who likes to address issues with satire.