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.
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.
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.
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
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
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.