Prime Time: Docker, Juju, and Snappy Ubuntu Core
by Dustin Kirkland on 20 July 2015
As you probably remember from grade school math class, primes are numbers that are only divisible by 1 and themselves. 2, 3, 5, 7, and 11 are the first 5 prime numbers, for example.
Many computer operations, such as public-key cryptography, depends entirely on prime numbers. In fact, RSA encryption, invented in 1978, uses a modulo of a product of two very large primes for encryption and decryption. The security of asymmetric encryption is tightly coupled with the computational difficulty in factoring large numbers. I actually use prime numbers as the status update intervals in Byobu, in order to improve performance and distribute the update spikes.
Euclid proved that there are infinitely many prime numbers around 300 BC. But the Prime Number Theorem (proven in the 19th century) says that the probability of any number is prime is inversely proportional to its number of digits. That means that larger prime numbers are notoriously harder to find, and it gets harder as they get bigger!
What’s the largest known prime number in the world?
Well, it has 17,425,170 decimal digits! If you wanted to print it out, size 11 font, it would take 6,543 pages — or 14 reams of paper!
That number is actually one less than a very large power of 2. 257,885,161-1. It was discovered by Curtis Cooper on January 25, 2013, on an Intel Core2 Duo.
Actually, each of the last 14 record largest prime numbers discovered (between 1996 and today) have been of that form, 2P-1. Numbers of that form are called Mersenne Prime Numbers, named after Friar Marin Mersenne, a French priest who studied them in the 1600s.
Friar Mersenne’s work continues today in the form of the Great Internet Mersenne Prime Search, and the mprime program, which has been used to find those 14 huge prime numbers since 1996.
mprime is a massive parallel, cpu scavenging utility, much like SETI@home or the Protein Folding Project. It runs in the background, consuming resources, working on its little piece of the problem. mprime is open source code, and also distributed as a statically compiled binary. And it will make a fine example of how to package a service into a Docker container, a Juju charm, and a Snappy snap.
Docker Container
First, let’s build the Docker container, which will serve as our fundamental building block. You’ll first need to download the mprime tarball from here. Extract it, and the directory structure should look a little like this (or you can browse it here):
├── license.txt
├── local.txt
├── mprime
├── prime.log
├── prime.txt
├── readme.txt
├── results.txt
├── stress.txt
├── undoc.txt
├── whatsnew.txt
└── worktodo.txt
And then, create a Dockerfile, that copies the files we need into the image. Here’s our example.
FROM ubuntu
MAINTAINER Dustin Kirkland email@example.com
COPY ./mprime /opt/mprime/
COPY ./license.txt /opt/mprime/
COPY ./prime.txt /opt/mprime/
COPY ./readme.txt /opt/mprime/
COPY ./stress.txt /opt/mprime/
COPY ./undoc.txt /opt/mprime/
COPY ./whatsnew.txt /opt/mprime/
CMD ["/opt/mprime/mprime", "-w/opt/mprime/"]
Now, build your Docker image with:
$ sudo docker build .
Sending build context to Docker daemon 36.02 MB
Sending build context to Docker daemon
Step 0 : FROM ubuntu
...
Successfully built de2e817b195f
Then publish the image to Dockerhub.
$ sudo docker push kirkland/mprime
You can see that image, which I’ve publicly shared here: https://registry.hub.docker.com/u/kirkland/mprime/
[IMAGE 2]
Now you can run this image anywhere you can run Docker.
$ sudo docker run -d kirkland/mprime
And verify that it’s running:
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c9233f626c85 kirkland/mprime:latest "/opt/mprime/mprime 24 seconds ago Up 23 seconds furious_pike
Juju Charm
So now, let’s create a Juju Charm that uses this Docker container. Actually, we’re going to create a subordinate charm. Subordinate services in Juju are often monitoring and logging services, things that run along side primary services. Something like mprime is a good example of something that could be a subordinate service, attached to one or many other services in a Juju model.
Our directory structure for the charm looks like this (or you can browse it here):
└── trusty
└── mprime
├── config.yaml
├── copyright
├── hooks
│ ├── config-changed
│ ├── install
│ ├── juju-info-relation-changed
│ ├── juju-info-relation-departed
│ ├── juju-info-relation-joined
│ ├── start
│ ├── stop
│ └── upgrade-charm
├── icon.png
├── icon.svg
├── metadata.yaml
├── README.md
└── revision
3 directories, 15 files
The three key files we should look at here are metadata.yaml, hooks/install and hooks/start:
$ cat metadata.yaml
name: mprime
summary: Search for Mersenne Prime numbers
maintainer: Dustin Kirkland
description: |
A Mersenne prime is a prime of the form 2^P-1.
The first Mersenne primes are 3, 7, 31, 127
(corresponding to P = 2, 3, 5, 7).
There are only 48 known Mersenne primes, and
the 13 largest known prime numbers in the world
are all Mersenne primes.
This charm uses a Docker image that includes the
statically built, 64-bit Linux binary mprime
which will consume considerable CPU and Memory,
searching for the next Mersenne prime number.
See http://www.mersenne.org/ for more details!
tags:
- misc
subordinate: true
requires:
juju-info:
interface: juju-info
scope: container
And:
$ cat hooks/install
#!/bin/bash
apt-get install -y docker.io
docker pull kirkland/mprime
And:
$ cat hooks/start
#!/bin/bash
service docker restart
docker run -d kirkland/mprime
Now, we can add the mprime service to any other running Juju service. As an example here, I’ll –bootstrap, deploy the Apache2 charm, and attach mprime to it.
$ juju bootrap
$ juju deploy apache2
$ juju deploy cs:~kirkland/mprime
$ juju add-relation apache2 mprime
Looking at our services, we can see everything deployed and running here:
$ juju status
services:
apache2:
charm: cs:trusty/apache2-14
exposed: false
service-status:
current: unknown
since: 20 Jul 2015 11:55:59-05:00
relations:
juju-info:
- mprime
units:
apache2/0:
workload-status:
current: unknown
since: 20 Jul 2015 11:55:59-05:00
agent-status:
current: idle
since: 20 Jul 2015 11:56:03-05:00
version: 1.24.2
agent-state: started
agent-version: 1.24.2
machine: "1"
public-address: 23.20.147.158
subordinates:
mprime/0:
workload-status:
current: unknown
since: 20 Jul 2015 11:58:52-05:00
agent-status:
current: idle
since: 20 Jul 2015 11:58:56-05:00
version: 1.24.2
agent-state: started
agent-version: 1.24.2
upgrading-from: local:trusty/mprime-1
public-address: 23.20.147.158
mprime:
charm: local:trusty/mprime-1
exposed: false
service-status: {}
relations:
juju-info:
- apache2
subordinate-to:
- apache2
Snappy Ubuntu Core Snap
Finally, let’s build a Snap. Snaps are applications that run in Ubuntu’s transactional, atomic OS, Snappy Ubuntu Core.
We need the simple directory structure below (or you can browse it here):
├── meta
│ ├── icon.png
│ ├── icon.svg
│ ├── package.yaml
│ └── readme.md
└── start.sh
1 directory, 5 files
The package.yaml describes what we’re actually building, and what capabilities the service needs. It looks like this:
name: mprime
vendor: Dustin Kirkland
architecture: [amd64]
icon: meta/icon.png
version: 28.5-11
frameworks:
- docker
services:
- name: mprime
description: "Search for Mersenne Prime Numbers"
start: start.sh
caps:
- docker_client
- networking
And the start.sh launches the service via Docker.
#!/bin/sh
PATH=$PATH:/apps/docker/current/bin/
docker rm -v -f mprime
docker run --name mprime -d kirkland/mprime
docker wait mprime
Now, we can build the snap like so:
$ snappy build .
Generated 'mprime_28.5-11_amd64.snap' snap
$ ls -halF *snap
-rw-rw-r-- 1 kirkland kirkland 9.6K Jul 20 12:38 mprime_28.5-11_amd64.snap
First, let’s install the Docker framework, upon which we depend:
$ snappy-remote --url ssh://snappy-nuc install docker
=======================================================
Installing docker from the store
Installing docker
Name Date Version Developer
ubuntu-core 2015-04-23 2 ubuntu
docker 2015-07-20 1.6.1.002
webdm 2015-04-23 0.5 sideload
generic-amd64 2015-04-23 1.1
=======================================================
And now, we can install our locally built Snap.
$ snappy-remote --url ssh://snappy-nuc install mprime_28.5-11_amd64.snap
=======================================================
Installing mprime_28.5-11_amd64.snap from local environment
Installing /tmp/mprime_28.5-11_amd64.snap
2015/07/20 17:44:26 Signature check failed, but installing anyway as requested
Name Date Version Developer
ubuntu-core 2015-04-23 2 ubuntu
docker 2015-07-20 1.6.1.002
mprime 2015-07-20 28.5-11 sideload
webdm 2015-04-23 0.5 sideload
generic-amd64 2015-04-23 1.1
=======================================================
Alternatively, you can install the snap directly from the Ubuntu Snappy store, where I’ve already uploaded the mprime snap:
$ snappy-remote --url ssh://snappy-nuc install mprime.kirkland
=======================================================
Installing mprime.kirkland from the store
Installing mprime.kirkland
Name Date Version Developer
ubuntu-core 2015-04-23 2 ubuntu
docker 2015-07-20 1.6.1.002
mprime 2015-07-20 28.5-11 kirkland
webdm 2015-04-23 0.5 sideload
generic-amd64 2015-04-23 1.1
=======================================================
Conclusion
How long until this Docker image, Juju charm, or Ubuntu Snap finds a Mersenne Prime? Almost certainly never 🙂 I want to be clear: that was never the point of this exercise!
Rather I hope you learned how easy it is to run a Docker image inside either a Juju charm or an Ubuntu snap. And maybe learned something about prime numbers along the way 😉
Join us in #docker, #juju, and #snappy on irc.freenode.net.