Recently, when I wanted to install IPython notebook server I discovered that for quite some time the project had changed its name to Jupyter. You may recall one of my posts about the IPython where I presented how to install it on Raspberry Pi. Now, I would like to present the Jupyter project to you which comes with some neat new features.
Jupyter is a project that was formerly named IPython, that’s a fact and ther is no doubt about it.
Install required packages
To install Jupyter you can use python’s pip. But before doing this you have to update it. Run following command
pip install --upgrade pip
Above will take a moment but will update the pip version and now installing jupyter will be possible. After that you can use pip tool to install Jupyter. During the process some extra packages will also be installed. Now, run this command
pip install jupyter
If you would like to test some examples which are at the bottom of this page you have to install matplotlib. It is very useful package which allows you to plot 2D and 3D graphs. To install it use apt and following command
apt-get install python-matplotlib
Now, when you have installed jupyter it is time to give it a try. Log into your RPi and run following command:
This will start jupyter server and open a default browser. If everything goes according to plan you should see jupyter running in your browser in a few seconds. You should be able to see something similar to
Remember to actually run the jupyter on Raspberry Pi not via ssh! Now, you can test it a little bit by creating some additional notebooks
or doing some serious math 😉
The only disadvantage to this way of running Jupyter server is that it is local. It means that you can not access it from outside your local network, and even from there. You can only access it locally from your Raspberry Pi. It is a bit inconvenient but we will deal with this 😉 Let’s start with configuring the Jupyter …
Remote access to Jupyter
To make Jupyter server visible from local network we have to configure it. First of all a local configuration file has to be created. It can be done with this command:
jupyter notebook --generate-config
Above won’t start server but instead will create a configuration file in your home directory i.e. /home/pi/.jupyter/jupyter_notebook_config.py. You can start to customize it from there on. Open the file and insert following two lines at the very end of this file. You will find that the file is a python script which contains a lot of parameters but all are commented out. At least it was in my case.
c.NotebookApp.ip = '*' c.NotebookApp.port = 5555
The first line allows incoming connections from any IP address. This is a beginning if you would like to host the Jupyter server while the Raspberry Pi is directly connected to your ISP. The second line says that the server should listen on port number 5555. By default it listens on 8888. Feel free to change it! Now, when you run your server it will be available under URL http://localhost:5555.
You can add one other line to the configuration file, namely:
c.Notebook.open_browser = False
As the name suggest the server will not start a browser. It is pretty useful if you run your Jupyter server via script and do not want it to open a browser every time you start it.
One of important aspect of hosting a server is its security. Firstly, you can set up a password for the server so every time a new session is started a screen with password prompt will show up to restrict access to your python notebooks. Secondly, you can encrypt the whole traffic between a client and the server.
To add password protection to the Jupyter you can modify the configuration file which was altered in previous step. Add at the bottom following line:
c.NotebookApp.password = u'HASH'
What is important is the HASH. It is an encoded password so it would not be available in plain form. To generate that kind of hash sum you need to start python — what else 😉 after that write inside python console following two lines:
from notebook.auth import passwd passwd()
First line will import passwd() function and the second line will invoke it. After that you will be prompted for password. For example write down secretpassword and press enter. You should receive following for this exact password:
>>> passwd() Enter password: Verify password: 'sha1:847cb8a5687d:bd62e1c30614656c1cb8dc80b6bc0ad4eb971b77' >>>
This string sha1:847cb8a5687d:bd62e1c30614656c1cb8dc80b6bc0ad4eb971b77 is your HASH. So past it inside the jupyter_notebook_config.py file. Now, it should look more or less as this one:
c.NotebookApp.ip = '*' c.NotebookApp.port = 5555 c.NotebookApp.password = u'sha1:847cb8a5687d:bd62e1c30614656c1cb8dc80b6bc0ad4eb971b77' c.Notebook.open_browser = False
Great! Save the file and run jupyter. Try to access your server with this URL http://localhost:5555 or http://RASPBERRY_PI_IP_ADDRESS:5555 from your local network. You should be able to see something like below
Unless you enter correct password you won’t be able to access your notebooks.
As I mentioned earlier it is nice to have second level of protection — a kind which will encrypt the whole bidirectional traffic. In other words, if someone in your local network would like to sniff what you are typing into Jupyter notebook he will not be able to. This is a nice-to-have feature if you want your i.e. scientific calculation to be private.
Let’s start with adding some additional lines to our local configuration file. Those two should be enough:
c.NotebookApp.certfile = u'/home/pi/jupyter_keys/mycert.pem' c.NotebookApp.keyfile = u'/home/pi/jupyter_keys/mykey.key'
As you probably suspect, the first one tells jupyter server where to find SSL certificate and the second one is your secret key. It is important to keep those files private like for example SSH keys. Create a jupyter_keys in you home directory and go inside it with cd command. Then run openssl to generate a certificate and a key. You can use this command:
openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.key -out mycert.pem
There are a few important things about this command. Firstly, you would like to specify how long this certificate is valid. In my case it was 365 days. You can go ahead and increase this number to two years or even more but by cautious! It is not advised to do so unless you know what you are doing. After one year you will be required to regenerate it! And your web browser will alert you that the certificate was changed and it will tell you it is not trusted anymore. The other thing is the key length, nowadays 4096 bits is enough. The last two, the most important parameters, are were to save your key and your certificate. Below I attach a log file with the session:
[email protected]:~/jupyter_keys $ openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.key -out mycert.pem Generating a 4096 bit RSA private key .............................................................................................++ ....++ writing new private key to 'mykey.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]: Locality Name (eg, city) : Organization Name (eg, company) [Internet Widgits Pty Ltd]: Organizational Unit Name (eg, section) : Common Name (e.g. server FQDN or YOUR name) : Email Address : [email protected]:~/jupyter_keys $
The process takes a while. Also keep in mind that you will be asked about some additional informations for the certificate. You can go with default but also you can put there whatever you want. Here you have also a screen shoot with above.
Now, when you have the certificate and the key you should restrict access to those files. You can do this with chmod command. If you are inside the jupyter_keys directory go up one level with cd .. and enter below two commands:
chmod 700 jupyter_keys chmod 600 jupyter_keys/*
Above restrict the access to the director and the files themselves. Basically only user to whom the files belong to can write and read them.
To test new configuration run jupyter:
and enter HTTPS://localhost:5555 or HTTPS://RASPBERRY_PI_IP_ADDRESS:5555. Notice that we are now using HTTPS instead of HTTP. Voila! You have yourself a secure Jupyter Notebook!
You can rename the jupyter_keys directory to .jupyter_keys (with a dot before the name). This way the directory won’t be visible unless you type ls -a to list hidden files an directories. But please remember to change the configuration script accordingly.
It would be nice to have Jupyter to start during system boot, this way you would not have to run it manually. To do that you have to modify the /etc/rc.local file. Put following line just before exit 0 statement:
su pi -c "jupyter notebook --log-level='CRITICAL' --no-browser --notebook-dir=/home/pi/jupyter &> /dev/null" &
Above will do the following thing. It will run a command in quotes as a pi user. The command will run a jupyter notebook while setting log level to critical. It means that only some critical messages will be displayed. No browser will be launched during the booting process and the notebook directory, the one that is shown when you log into jupyter server via web, is set to a local jupyter directory in home of pi user. Lastly, the whole output, from standard output and error, is redirected to /dev/null. The whole command is run in background so this will not disrupt the booting process.
Also, setting the log level to critical is not necessary because whole output is redirected either way, but it is more efficient. Here you can see how much of information is displayed when the log level is set to INFO — default settings:
As a treat I attach some basic scripts to jump start your scientific computations 😉 It is good to install matplotlib, since it is a great graphic library and must-have. If you haven’t already installed it do it now with following command:
apt-get install python-matplotlib
Plot some 2D graph
%matplotlib inline import matplotlib import numpy as np import matplotlib.pyplot as plt x=[i/10.0 for i in range(0,100)] sx=np.sin(x) fig = plt.figure() ax = fig.add_subplot(111) ax.plot(sx)
Plot some 3D graph
%matplotlib inline import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.axes3d import Axes3D, get_test_data from matplotlib import cm import numpy as np from numpy import exp fig = plt.figure(figsize=plt.figaspect(1.0)) X = np.arange(-4, 4, 0.025) Y = np.arange(-4, 4, 0.025) X, Y = np.meshgrid(X, Y) ax = fig.add_subplot(1, 1, 1, projection='3d') Z = 3**exp(-0.5*((X-1)**2 + (Y-1)**2)) - 3**exp(-0.5*((X+1)**2 + (Y+1)**2)) Zmin = Z.min() Zmax = Z.max() ax.plot_surface(X, Y, Z, rstride=10, cstride=10, cmap='gnuplot', vmax=Zmax, vmin=Zmin) plt.show()
Show captured image
from IPython.display import Image Image("some_image.png" )
or you can use matplotlib
%matplotlib inline import matplotlib.pyplot as plt import matplotlib.image as mpimg img=mpimg.imread("some_image.png") plt.imshow(img)
List current directory
import os content = os.listdir('.') for l in content : print l
„`c.NotebookApp.password = u’sha1:847cb8a5687d:bd62e1c30614656c1cb8dc80b6bc0ad4eb971b77‚„`
„`c.NotebookApp.password = ‚sha1:847cb8a5687d:bd62e1c30614656c1cb8dc80b6bc0ad4eb971b77’„`
The ‘u’ before the string with a hash was added by the haslib generator. It says that the following string should be treated as a unicode string. Since there is no unicode characters it is safe to omit the ‘u’. However, it should work in both cases. Thank you!
Great tutorial, thank you! I could get it to work in no time thanks to the clear instructions.
Thanks especially for having some regards for the server’s security.
Side note for those installing jupyter through pip3 and having later the kernel fail with reference to prompt_toolkit import problems: a pip3 install –upgrade ipython managed to iron out that wrinkle in my setup.
I’m glad you like it.
Yes, sometimes it turns out that you have to manually upgrade a package because it is causing some issues.