Deploying Applications on OCI, the SageMath Example - Part 1 Docker Image

April 2, 2019 | 6 minute read
John Featherly
Cloud Native Architect
Text Size 100%:

Applications

When applications were called programs if you needed your computer to do something you wrote the code, compiled, linked, loaded and ran the program. If you had an elaborate problem you would write, trade or buy subroutine libraries and construct an application out of routines from libraries. To change the program, modify the code and subroutine libraries as needed. Building an application in the cloud era is conceptually similar but now we are assembling functionality from micro services and aim to automate the integration, deployment and updates of the components. Those automated operations are configured by the application developers with the assistance of tools such as kubernetes, istio and Jenkins In this article we'll take a look at this process in the context of a substantial open source application SageMath.

SageMath

As stated at the SageMath website: "SageMath is a free open-source mathematics software system licensed under the GPL. It builds on top of many existing open-source packages: NumPy, SciPy, matplotlib, Sympy, Maxima, GAP, FLINT, R and many more. Access their combined power through a common, Python-based language or directly via interfaces or wrappers."

There are a number of options to deploy SageMath, similar to other open source applications. You can start with the source distribution and compile in Linux, Mac or Windows OS, virtual machine images are available, compiled binaries for Debian, Ubuntu, Mac and Windows. The distributions we are interested in here are the docker images made available at dockerhub. Ultimately we are aiming to construct a continuous integration of ongoing releases of SageMath through a customization step and into OCI.

Test Run

As a simple first step and run test we'll run Sage on an OCI compute instance. Our eventual goal is to setup a customized docker image in an OCI repository and deploy to a kubernetes cluster but we'll start with just running a base image from dockerhub.

Create a Linux compute instance with associated VCN and install docker with

# yum install docker-engine

then start docker and enable the daemon

# systemctl start docker
# systemctl enable docker

and finally pull and run the latest SageMath image from dockerhub

# docker run -it sagemath/sagemath
Unable to find image 'sagemath/sagemath:latest' locally
Trying to pull repository docker.io/sagemath/sagemath ...
latest: Pulling from docker.io/sagemath/sagemath
34667c7e4631: Pull complete
d18d76a881a4: Pull complete
119c7358fbfc: Pull complete
2aaf13f3eff0: Pull complete
8281b8be8550: Pull complete
b3b5dd6f8b04: Pull complete
b094611edbf3: Pull complete
dedb20e0cb5f: Pull complete
caf09ad63d60: Pull complete
944854b466f6: Pull complete
85aeff07acd3: Pull complete
Digest: sha256:4fe6550b1ead6af312fcd7e27c9f5dc9bdac49fb63d4c9edf715e864fa05a8d1
Status: Downloaded newer image for sagemath/sagemath:latest
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 8.7, Release Date: 2019-03-23                     │
│ Using Python 2.7.15. Type "help()" for help.                       │
└────────────────────────────────────────────────────────────────────┘
Setting permissions of DOT_SAGE directory so only you can read and write it.
sage:

To run Sage in a jupyter notebook

# docker run -p8888:8888 sagemath/sagemath sage-jupyter
[I 08:42:59.156 NotebookApp] Using MathJax: nbextensions/mathjax/MathJax.js
[I 08:42:59.162 NotebookApp] Writing notebook server cookie secret to /home/sage/.local/share/jupyter/runtime/notebook_cookie_secret
[I 08:42:59.347 NotebookApp] Serving notebooks from local directory: /home/sage
[I 08:42:59.347 NotebookApp] The Jupyter Notebook is running at:
[I 08:42:59.347 NotebookApp] http://(bf1ac072b105 or 127.0.0.1):8888/?token=94ce455807417077ace0175f28077a9a8a0d98d06ee7a451
[I 08:42:59.347 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 08:42:59.351 NotebookApp]
    To access the notebook, open this file in a browser:
        file:///home/sage/.local/share/jupyter/runtime/nbserver-1-open.html
    Or copy and paste one of these URLs:
        http://(bf1ac072b105 or 127.0.0.1):8888/?token=1234567...

The access token is displayed as shown above. Make sure you open TCP port 8888 in the VCN the compute instance is connected to and use the URL http://oci-server:8888/?token=1234567... to open jupyter.

Create a new notebook and enter some math to solve for example

a, b, c = var('a, b, c')
quad = solve(a*x^2 + b*x + c, x, solution_dict=True)
quad

running the cell should give the familiar solution to quadratic equations

The output doesn't look great so turn on LaTex to get a better looking result using "%display latex"

Looks like the image is working fine. There is much more mathematics capability available to try in Sage.

Customization

The sky is the limit if you are interested in contributing to and customizing Sage. Take a look at the docker images on GitHub in particular sagemath-develop if you have some ideas you would like to contribute. For demonstration purposes we'll make a trivial customization of adding the JupyterLab notebook code.

In addition to jupyter, Sage supports other notebook types such as the legacy SageNB and JupyterLab. Let's customize the docker image to include JupyterLab as it is not in the base image. The two step process is to first login to the container and pip install JupyterLab then commit the change to a new docker image.

Start with the base image, start a container running bash

# docker run -it sagemath/sagemath /bin/bash

inside the container, pip install JupyterLab

sage@750dec70503d:~$ sage --pip install jupyterlab

While the container is still running commit it to a new docker image. Open a new ssh connection to the host compute instance and find the running container ID

# docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                    NAMES
750dec70503d        sagemath/sagemath            "/usr/local/bin/sage…"   35 seconds ago      Up 34 seconds       8888/tcp                 confident_feynman

Using the container ID commit it to a new image tagged "withjlab" (or any other tag you might like to use)

# docker commit 750dec70503d sagemath/sagemath:withjlab

The new committed image is now in the local repository

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
sagemath/sagemath   withjlab            79b9250bd755        9 seconds ago       2.53GB
sagemath/sagemath   latest              273bc8ab4f96        8 days ago          2.46GB

To run the JupyterLab notebook we will eventually modify /usr/local/bin/sage-entrypoint to handle the argument sage-jupyterlab but for now we can test it directly by launching a container with the direct notebook arguments and port mapping.

# docker run -p0.0.0.0:8888:8888 sagemath/sagemath:withjlab sage -notebook=jupyterlab --no-browser --ip="0.0.0.0" --port=8888

Open in a browser as before with the given access token and add our quadratic equation test code to a notebook. You should see something that looks like

 

Everything appears to be working fine. Now modify the entry point file mentioned above so the command argument sage-jupyterlab is recognized and handled. (login to the container as root and substitute the code below for the contents of /usr/local/bin/sage-entrypoint)

#!/bin/bash
if [ x"$1" = x"sage-jupyter" ]; then
    # If "sage-jupyter" is given as a first argument, we start a jupyter notebook
    # with reasonable default parameters for running it inside a container.
    shift
    exec sage -n jupyter --no-browser --ip='0.0.0.0' --port=8888 "$@"
elif [ x"$1" = x"sage-jupyterlab" ]; then
    # If "sage-jupyterlab" is given as a first argument, we start a JupyterLab notebook
    # with reasonable default parameters for running it inside a container.
    shift
    exec sage -n jupyterlab --no-browser --ip='0.0.0.0' --port=8888 "$@"
else
    exec sage -sh -c "$*"
fi

Save the modified container (while it is still running) to a new image and then you should be able to launch containers using the simpler docker run command as shown

# docker run -p8888:8888 sagemath/sagemath:jlabentry sage-jupyterlab

Push to OCI Repository

Now that the image is ready we need to push it to an OCIR repository to prepare for the kubernetes (OKE) deployment. Create a repository in an OCI Registry if you don't already have one.

connect to the OCI Registry using docker login (make sure you have an Auth Token on your user settings to use for the login password and use tenancy/username for the login username)

# docker login fra.ocir.io
Username: <tenancy>/John.Featherly@oracle.com
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded

Now add the tag for the image as it will be named in the OCI Registry

# docker tag sagemath/sagemath:jlabentry fra.ocir.io/<tenancy>/jhf/sagemath/sagemath:latest

and finally push the image to the remote repository

# docker push fra.ocir.io/<tenancy>/jhf/sagemath/sagemath:latest
The push refers to repository [fra.ocir.io/<tenancy>/jhf/sagemath/sagemath]
0d020826ed48: Pushed
75739f7fc64e: Pushed
5326d6c2f76d: Pushed
4ef33130c0d3: Pushed
84c9839ef9ad: Pushed
a207d49c2b7d: Pushed
3dffbb6dbc9d: Pushed
05ffb0daa86f: Pushed
fd9e0ee034b8: Pushed
297fd071ca2f: Pushed
2f0d1e8214b2: Pushed
7dd604ffa87f: Pushed
aa54c2bc1229: Pushed
latest: digest: sha256:e7b8f9ed4f78c7614f936801ac9ce9c57dcaf98d26577f2a42221c6cacba8acb size: 3033

check the OCI console to verify the image has been registered

Summary

We've seen how to take a significant open source application, customize it and register the resulting docker image in OCIR. In Part 2 we will look at deploying the image to an OCI kubernetes (OKE) cluster.

John Featherly

Cloud Native Architect


Previous Post

Using the OCI CLI with a federated user from a Docker container (or over ssh)

Christopher Johnson | 3 min read

Next Post


OAC Remote Data Connector with Jetty

Richard Williams | 12 min read