<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Loïc Gouarin</title><link href="https://jupyter.org/blog/" rel="alternate"/><link href="https://jupyter.org/blog/feeds/author-loic-gouarin.atom.xml" rel="self"/><id>https://jupyter.org/blog/</id><updated>2019-12-25T09:42:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>Deploying JupyterHub with Kubernetes on OpenStack</title><link href="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/" rel="alternate"/><published>2018-10-15T13:11:00+00:00</published><updated>2018-10-15T15:04:00+00:00</updated><author><name>Loïc Gouarin</name></author><id>tag:jupyter.org,2018-10-15:/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/</id><summary type="html">&lt;p&gt;Jupyter is now widely used for teaching and research. The use of Kubernetes for deploying a JupyterHub has enabled reliable setups scaling to thousands of users.&lt;/p&gt;</summary><content type="html">&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/001-1_cbBQmPCCtGh-j4a45vfcmA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jupyter is now widely used for teaching and research. The use of Kubernetes for deploying a JupyterHub has enabled reliable setups scaling to thousands of users.&lt;/p&gt;
&lt;p&gt;There are many cloud computing vendors (Google, Amazon, …) and the first attempts to use JupyterHub with Kubernetes is based on them. But relying on vendor clouds increases the risk of vendor lock-in.&lt;/p&gt;
&lt;p&gt;In addition, there are many pre-existing academic clouds managed by people with a high level of expertise and a thorough knowledge of their infrastructure and associated tools. These are often more cost-effective for research and education. Could we build upon these academic cloud computing to provide scalable and high-quality infrastructure for education and research?&lt;/p&gt;
&lt;p&gt;In this post, we will focus on how to deploy JupyterHub with Kubernetes on OpenStack. A first attempt to create academic cloud computing in France.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post is split into two parts (see links below).&lt;/p&gt;
&lt;p&gt;&lt;a href="#why-deploy-jupyterhub-on-openstack"&gt;&lt;strong&gt;Why to deploy JupyterHub on OpenStack&lt;/strong&gt;&lt;/a&gt; is a high-level description of our problem, and our steps to solve it. It explains why we want to deploy a JupyterHub on OpenStack, what difficulties we have encountered, and what we want to do in a near future.&lt;/p&gt;
&lt;p&gt;&lt;a href="#the-technical-details"&gt;&lt;strong&gt;A technical guide to deploying JupyterHub on OpenStack&lt;/strong&gt;&lt;/a&gt; is an in-depth guide that you may follow in order to deploy your own JupyterHub on Kubernetes on OpenStack. It’s designed for any person interested in how to replicate our deployment on their own infrastructure.&lt;/p&gt;
&lt;h2 id="our-story-our-difficulties-and-our-plans"&gt;Our story, our difficulties and our plans&lt;/h2&gt;
&lt;h3 id="why-deploy-jupyterhub-on-openstack"&gt;Why deploy JupyterHub on OpenStack?&lt;/h3&gt;
&lt;p&gt;JupyterHub, the multi-user Jupyter server, has been actively developed since 2014 and has seen a rapidly growing adoption in the past year.&lt;/p&gt;
&lt;p&gt;You may know about &lt;a href="https://zero-to-jupyterhub.readthedocs.io"&gt;Zero to JupyterHub&lt;/a&gt;, which provides step-by-step instructions for installing JupyterHub using a vendor-managed Kubernetes cluster. In the guide, you can also find how to set up a Kubernetes cluster on many vendor clouds such as AWS, Azure, and more recently on OpenShift. But what about other cloud infrastructures based on open-source infrastructure, such as OpenStack? While cloud vendors often provide you with many tools that make your life easier, OpenStack requires more explicit configuration and setup.&lt;/p&gt;
&lt;p&gt;Earlier this year, we set up a working group across several France universities to explore how to easily set up JupyterHub for teaching and research in our academic cloud infrastructures. It turns out that the technology used across these academic clouds is OpenStack. One of the objectives of this working group is to make just as easy to deploy JupyterHub on OpenStack as compared to following &lt;em&gt;Zero to JupyterHub&lt;/em&gt; and using vendor infrastructure.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We are not the first to work on this problem, and we should also mention the work done in Canada through &lt;a href="http://intro.syzygy.ca/"&gt;Syzygy.ca&lt;/a&gt; which is a project of PIMS, Compute Canada, and Cybera. They have developed their own deployment tools using terraform and ansible scripts. Our approach differs in that while we use the same technological stack, we prefer not to build a custom deployment tool that we would need to maintain over time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="issues-we-encountered"&gt;Issues we encountered&lt;/h3&gt;
&lt;p&gt;We started a deployment using Kubespray in January of this year and have had a bumpy path since then. To begin, we looked at what existed already in the OpenStack world. We came across &lt;a href="https://github.com/kubernetes-incubator/kubespray"&gt;Kubespray&lt;/a&gt;, which offers a great facility and a lot of flexibility when you want to deploy a Kubernetes cluster. An interesting fact about Kubespray is that it’s not dedicated to OpenStack infrastructures, so you should be able to follow the same procedure for other deployments such as a baremetal cluster.&lt;/p&gt;
&lt;p&gt;Using Kubespray, we very quickly had a Kubernetes cluster on OpenStack. However, we ran into network problems, and would lose network packets that made the JupyterHub completely unusable. It took us a long time to realize that we had &lt;a href="https://en.wikipedia.org/wiki/Maximum_transmission_unit"&gt;MTU issues&lt;/a&gt; and even longer to solve it. To make things harder, we used a production platform which made it very difficult to update. We finally solved the problem by using a test platform where we could have more freedom.&lt;/p&gt;
&lt;p&gt;In Kubespray, there are various CNIs (&lt;em&gt;Container Network Interface&lt;/em&gt;) and one of them (&lt;em&gt;weave&lt;/em&gt;) allows to modify the MTU. We tried to configure it carefully on the production platform, but we continued facing the same problem. Trying a new version of OpenStack on the test platform, we were able to solve the problem. That means that something bad had also happened with the LoadBalancer. For more explanation, see the &lt;a href="#configure-your-kubernetes-cluster"&gt;MTU section&lt;/a&gt; in the technical description below.&lt;/p&gt;
&lt;p&gt;We thought we could deploy JupyterHub with Kubernetes on OpenStack in a few weeks but as you can see, that’s not what happened at all. That’s why it was important for us to share our experience in the hopes that it makes the process easier for others. In the last section, we’ll cover more of the technical details for our deployment&lt;/p&gt;
&lt;h3 id="whats-next"&gt;What’s next ?&lt;/h3&gt;
&lt;p&gt;For us, the installation of JupyterHub on OpenStack was just the first step of a long journey. We are able now to offer to our researchers and our students a JupyterHub but we want more. Here’s a short wish-list our deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On-demand environments.&lt;/strong&gt; Imagine offering researchers and teachers an even more flexible platform where they can create their work environment and distribute them without needing to use central IT for the installation of their packages. As you may have guessed, we are more interested in what BinderHub has to offer.&lt;/p&gt;
&lt;p&gt;The steps described above also work for the installation of BinderHub. We deployed a BinderHub on OpenStack alongside a DockerHub registry. Kubespray also offers the possibility to deploy a private registry and we would like to test it with BinderHub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cluster monitoring.&lt;/strong&gt; It would also be great to have monitoring of the Kubernetes cluster. This would allow us to inspect the usage rates and resources available on the deployment. In the &lt;a href="https://github.com/kubernetes-incubator/kubespray/blob/master/docs/roadmap.md"&gt;roadmap&lt;/a&gt; of Kubespray, it is planned to add Grafana and Prometheus installations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication for BinderHub.&lt;/strong&gt; Currently BinderHub does not support authentication for users. However, note that a recent pull request on this subject was merged in BinderHub (see &lt;a href="https://github.com/jupyterhub/binderhub/pull/666"&gt;https://github.com/jupyterhub/binderhub/pull/666&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Persistent storage in BinderHub.&lt;/strong&gt; It is also currently not possible to persist storage across BinderHub sessions. Once authentication is possible in BinderHub, we’d also like to connect user accounts to their storage so that they can keep their work over time. This will also require being able to mount the home directory of each user.&lt;/p&gt;
&lt;p&gt;We will work on all these items in the next months.&lt;/p&gt;
&lt;h2 id="the-technical-details"&gt;The Technical Details&lt;/h2&gt;
&lt;p&gt;This part details the set up of a Kubernetes cluster and JupyterHub using a bare OpenStack infrastructure. To make it as reproducible as possible, we will start by listing the versions we have used.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OpenStack&lt;/strong&gt;: Pike&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubespray&lt;/strong&gt;: commit &lt;a href="https://github.com/kubernetes-incubator/kubespray/commit/36322901a6c057a6a1f6a157abab63b165b2a0a8"&gt;3632290&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt;: 1.11.3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helm&lt;/strong&gt;: 2.9.1&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JupyterHub&lt;/strong&gt;: 0.7.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we’ve described the components and the versions used, let’s start to deploy our JupyterHub on OpenStack !!&lt;/p&gt;
&lt;p&gt;The deployment steps are the following&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Connect to our OpenStack infrastructure&lt;/li&gt;
&lt;li&gt;Download Kubespray&lt;/li&gt;
&lt;li&gt;Create your infrastructure using terraform&lt;/li&gt;
&lt;li&gt;Deploy your Kubernetes cluster using ansible&lt;/li&gt;
&lt;li&gt;Deploy your JupyterHub using Helm chart&lt;/li&gt;
&lt;li&gt;Enjoy!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="connect-to-openstack"&gt;Connect to OpenStack&lt;/h3&gt;
&lt;p&gt;Kubespray needs a access to your OpenStack infrastructure in order to create all the instances needed for your Kubernetes cluster using the OpenStack CLI (&lt;em&gt;Command-Line Interface&lt;/em&gt;). When you log in to your OpenStack dashboard, you can download all the environment variables to use the CLI.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/002-1_amhpJittHOQA_6dYHOWlCg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;We chose to download the &lt;strong&gt;OpenStack RC File V3&lt;/strong&gt;. You should obtain something like this:&lt;/p&gt;
&lt;figure&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_AUTH_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://keystone.xxxxxx:5000/v3
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_PROJECT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xxxxxxxx&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_USER_DOMAIN_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xxxxxxxx&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-z&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$OS_USER_DOMAIN_NAME&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OS_USER_DOMAIN_NAME&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c1"&gt;# unset v2.0 items in case set&lt;/span&gt;
&lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OS_TENANT_ID
&lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OS_TENANT_NAME

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xxxxxxxx&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_REGION_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xxxxxxxx&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;# Don&amp;#39;t leave a blank variable, unset it if it was empty&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-z&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$OS_REGION_NAME&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OS_REGION_NAME&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_INTERFACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_IDENTITY_API_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;

&lt;span class="c1"&gt;# to be added&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xxxxxxxx&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_CLOUD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xxxxxxxx
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OS_CACERT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/loic/.certs/openstack_cacert.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figcaption&gt;
&lt;p&gt;Example of rc_file&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that we’ve removed the lines which ask for your password when you use the CLI and add it to “never ask again”. You also have to provide OS_CLOUD and OS_CACERT (even if you don’t have a certificate to access to your OpenStack infrastructure, you must provide one but you can keep it blank).&lt;/p&gt;
&lt;p&gt;Now, you can install the OpenStack CLI with the command line. We’ll show two ways to do this below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;with &lt;strong&gt;virtualenv&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;virtualenv ~/openstack
source ~/openstack/bin/activate
pip install python-openstackclient
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;with &lt;strong&gt;conda&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda create -n openstack python=3.6
source activate openstack
pip install python-openstackclient
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, source your rc file to activate it&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;source rc_file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and test your connection&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openstack project list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should be able to see your projects listed.&lt;/p&gt;
&lt;p&gt;Once you have access, you will need some information in order to use terraform with Kubespray. You should find the following things (we have highlighted them in the images below):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The name of the image you want to deploy. To list it, run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openstack image list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="List of OpenStack images" src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/003-1_eHy-VQtYqNo-myHQKqkKFQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;List of OpenStack images&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The id of the flavor describing the type of machine you want to deploy (the flavor must be aUUID and not an integer ID). To find it, run this command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openstack flavor list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="List of OpenStack flavors" src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/004-1_iP9duMZM1rjBamzc8hl1Gg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;List of OpenStack flavors&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Once you’ve got this information, it’s time to install Kubespray.&lt;/p&gt;
&lt;h3 id="install-kubespray"&gt;Install Kubespray&lt;/h3&gt;
&lt;p&gt;Because Kubespray is simply a GitHub repository, we don’t “install” it in a traditional sense, we only clone the repository to our machine. Since Kubespray is a project that evolves quickly, we’ll list the commit that we used for this post. Run the following command to get Kubespray:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git clone https://github.com/kubernetes-incubator/kubespray.git
cd kubespray
git checkout 3632290
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, prepare all the files describing your Kubernetes cluster. We’ll follow &lt;a href="https://github.com/kubernetes-incubator/kubespray/tree/master/contrib/terraform/openstack"&gt;the documentation given by Kubespray&lt;/a&gt; and will just change some flags. We encourage you to follow the procedure described below as the documentation seems to have some errors.&lt;/p&gt;
&lt;p&gt;Kubespray uses terraform and ansible to deploy your Kubernetes cluster. ansible needs an inventory file which describes your cluster in order to execute the playbook roles on it. Kubespray provides a skeleton dedicated to OpenStack platform to provision your cluster using terraform and create the inventory file for ansible accordingly. To use the skeleton provided by Kubespray, the steps are the following&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp -LRp contrib/terraform/openstack/sample-inventory inventory/jhub
cd inventory/jhub
ln -s ../../contrib/terraform/openstack/hosts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;jhub&lt;/strong&gt; is the name directory we choose to store our inventory but you can choose what you want.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;strong&gt;inventory/jhub&lt;/strong&gt; directory you will see&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cluster.tf&lt;/strong&gt;: the terraform file describing your inventory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;group_vars&lt;/strong&gt;: the directory where we set all the variables used by ansible scripts provided by Kubespray.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s describe our inventory.&lt;/p&gt;
&lt;h3 id="initialize-terraform"&gt;Initialize Terraform&lt;/h3&gt;
&lt;p&gt;In the &lt;strong&gt;cluster.tf&lt;/strong&gt; file, you can specify different kinds of Kubernetes clusters with floating IP for each VM. &lt;em&gt;floating ip&lt;/em&gt; means that you ask to OpenStack to give you a public IP address in order to connect to the VM from an external network. You can also have a &lt;em&gt;bastion&lt;/em&gt; where you have to log before reaching your Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;In the following, we only choose to have a master VM and two nodes for our Kubernetes cluster. Another important part is to specify a GlusterFS to have some storage resources for JupyterHub (database and home directories). Our inventory file &lt;strong&gt;cluster.tf&lt;/strong&gt; looks like this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# your Kubernetes cluster name here&lt;/span&gt;
&lt;span class="na"&gt;cluster_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;jhub&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# SSH key to use for access to nodes&lt;/span&gt;
&lt;span class="na"&gt;public_key_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;~/.ssh/id_rsa.pub&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# image to use for bastion, masters, standalone etcd instances, and nodes&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CentOS-7-x86_64-GenericCloud-20180108.qcow2&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;# user on the node (ex. core on Container Linux, ubuntu on Ubuntu, etc.)&lt;/span&gt;
&lt;span class="na"&gt;ssh_user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;centos&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# 0|1 bastion nodes&lt;/span&gt;
&lt;span class="na"&gt;number_of_bastions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="c1"&gt;# standalone etcds&lt;/span&gt;
&lt;span class="na"&gt;number_of_etcd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;

&lt;span class="c1"&gt;# masters&lt;/span&gt;
&lt;span class="na"&gt;number_of_k8s_masters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;number_of_k8s_masters_no_etcd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;number_of_k8s_masters_no_floating_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;number_of_k8s_masters_no_floating_ip_no_etcd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;flavor_k8s_master&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;5e879c45-e709-4671-a38b-45ed0573dc38&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# nodes&lt;/span&gt;
&lt;span class="na"&gt;number_of_k8s_nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;number_of_k8s_nodes_no_floating_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;flavor_k8s_node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;5e879c45-e709-4671-a38b-45ed0573dc38&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# GlusterFS&lt;/span&gt;
&lt;span class="c1"&gt;# either 0 or more than one&lt;/span&gt;
&lt;span class="na"&gt;number_of_gfs_nodes_no_floating_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;gfs_volume_size_in_gb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="c1"&gt;# Container Linux does not support GlusterFS&lt;/span&gt;
&lt;span class="na"&gt;image_gfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CentOS-7-x86_64-GenericCloud-20180108.qcow2&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;# May be different from other nodes&lt;/span&gt;
&lt;span class="na"&gt;ssh_user_gfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;centos&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;flavor_gfs_node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;5e879c45-e709-4671-a38b-45ed0573dc38&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# networking&lt;/span&gt;
&lt;span class="c1"&gt;#network_name = &amp;quot;&amp;lt;network&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;external_net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;6cd08271-...&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#subnet_cidr = &amp;quot;&amp;lt;cidr&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;floatingip_pool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public&amp;quot;&lt;/span&gt;

&lt;span class="na"&gt;dns_nameservers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;8.8.8.8&amp;quot;, &amp;quot;8.8.4.4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The flavors are the same for each master, node, and GlusterFS but you can do what you want. We also added &lt;strong&gt;dns_nameservers&lt;/strong&gt; to be sure that we have a correct DNS on each nodes. We will check in future experiments if it’s really necessary.&lt;/p&gt;
&lt;p&gt;The ID of the external network and the name of the &lt;strong&gt;floatingip_pool&lt;/strong&gt; can be obtained with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openstack network list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="List of OpenStack networks" src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/005-1_xPXJTn5YDLAHJ6yWlInnuQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;List of OpenStack networks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You need ssh keys to be able to connect to the nodes. From the documentation of Kubespray:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ensure your local ssh-agent is running and your ssh key has been added. This step is required by the terraform provisioner:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ssh-agent&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="k"&gt;)&lt;/span&gt;
ssh-add&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, it’s time to initialize terraform. It’s important for the next steps to be run from the root directory of Kubespray.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;terraform init contrib/terraform/openstack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, create your VMs!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jhub&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tfstate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jhub&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contrib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openstack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At the end of this process, you can see your instances in the dashboard of OpenStack. It’s important to keep the information given at the end of the output.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/006-1_1kFAkM2Y6HAaA8fOs7b3aw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;At this stage, you’ve just created several VMs with the images given in the &lt;strong&gt;cluster.tf&lt;/strong&gt; file. You don’t have a Kubernetes cluster up and running yet. It’s the next step!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you want to destroy all that you’ve done, run this command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;destroy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jhub&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tfstate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jhub&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contrib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;terraform&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openstack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="configure-your-kubernetes-cluster"&gt;Configure your Kubernetes cluster&lt;/h3&gt;
&lt;p&gt;Again, Kubespray lets you configure your Kubernetes cluster with a lot of possibilities. We will show you one setup, but once you understand the procedure, you should be able to make your own choices.&lt;/p&gt;
&lt;p&gt;Let’s start to see if we can ping our VMs. You have to add the following script that we called &lt;strong&gt;ssh-nodes.conf&lt;/strong&gt; in your &lt;strong&gt;inventory/jhub&lt;/strong&gt; directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10.0.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;centos&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ProxyCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;:%&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;centos&lt;/span&gt;&lt;span class="mf"&gt;@134.&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You also have to modify the &lt;strong&gt;ssh_args&lt;/strong&gt; variable in &lt;strong&gt;ansible.cfg&lt;/strong&gt; script in the root directory of Kubespray accordingly&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh_args = -F inventory/jhub/ssh-nodes.conf -o ControlMaster=auto -o ControlPersist=30m -o ConnectionAttempts=100 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Just pay attention that you give the right external address and that your internal network is &lt;strong&gt;10.0.0.*&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To check that everything is configured correctly, this command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ansible -i inventory/jhub/hosts -m ping all
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;should have an output like this&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/007-1_NFZ3Rd-JNp0W8rxZK6EJ-Q.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;You can now install your Kubernetes cluster with the ansible scripts provided by Kubespray. To do that, you will edit the files found in the &lt;strong&gt;group_vars&lt;/strong&gt; directory in &lt;strong&gt;inventory/jhub&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You need several things to have a JupyterHub up and running&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A CNI (Container Network Interface).&lt;/strong&gt; Kubespray offers different CNI for your Kubernetes cluster: cilium, calico, contiv, weave or flannel. We will choose &lt;strong&gt;calico&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Storage for the data.&lt;/strong&gt; We’ll deploy a GlusterFS and add storage on the Kubernetes cluster to have access to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A LoadBalancer&lt;/strong&gt; to have access to the service from the external network. You have two kinds of LoadBalancer on Openstack: Neutron or Octavia. You can use both with Kubespray. We will choose &lt;strong&gt;Neutron&lt;/strong&gt; but it will be preferable to use Octavia in the future.&lt;/p&gt;
&lt;p&gt;So how do we configure all these items?&lt;/p&gt;
&lt;p&gt;First, open the file &lt;strong&gt;inventory/jhub/group_vars/all/all.yml&lt;/strong&gt; and modify the following entries&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;bootstrap_os&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;centos&lt;/span&gt;

&lt;span class="n"&gt;upstream_dns_servers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;8.8&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;4.4&lt;/span&gt;

&lt;span class="n"&gt;cloud_provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openstack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that the dns address is specific to our infrastructure.&lt;/p&gt;
&lt;p&gt;Now, open the file &lt;strong&gt;inventory/jhub/group_vars/all/openstack.yml&lt;/strong&gt; and configure the LoadBalancer&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;openstack_lbaas_enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;openstack_lbaas_subnet_id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;48ec8433-...&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;openstack_lbaas_floating_network_id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;6cd08271-...&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The two IDs are those given at the end of the &lt;strong&gt;terraform apply&lt;/strong&gt; step.&lt;/p&gt;
&lt;p&gt;Open the file &lt;strong&gt;inventory/jhub/group_vars/k8s-cluster/k8s-cluster.yml&lt;/strong&gt; and set &lt;strong&gt;persistent_volumes_enabled&lt;/strong&gt; to &lt;strong&gt;true&lt;/strong&gt; and &lt;strong&gt;resolvconf_mode&lt;/strong&gt; to &lt;strong&gt;host_resolvconf&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Our OpenStack cloud infrastructure is configured with a VXLAN tunnel where the header size is 50 bytes. We use Calico with Kubernetes which also uses a VXLAN tunnel with a header of 50 bytes. Then, for a default MTU of 1500 bytes, we already have 100 bytes taken by the headers. So, we need to configure carefully the MTU of calico in order to be sure that the packet size (headers included) doesn’t exceed the 1500 bytes.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/008-1_kz9Duiwop3_9J-eEmBhViA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;To configure the MTU of calico, we have to edit the file &lt;strong&gt;inventory/jhub/group_vars/k8s-cluster/k8s-net-calico.yml&lt;/strong&gt; and set the &lt;strong&gt;calico_mtu&lt;/strong&gt; flag to &lt;strong&gt;1400&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It’s important to notice that setting MTU had no effect for OpenStack versions earlier than Pike. The LoadBalancer didn’t work correctly.&lt;/p&gt;
&lt;p&gt;The last file to modify is &lt;strong&gt;inventory/jhub/group_vars/k8s-cluster/addons.yml&lt;/strong&gt;. JupyterHub uses Helm charts to deploy all that you need on the Kubernetes cluster and Kubespray can install Helm for you. So just set the &lt;strong&gt;helm_enabled&lt;/strong&gt; flag to &lt;strong&gt;true&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now we can run ansible playbook&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ansible-playbook --become -i inventory/jhub/hosts cluster.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can take a coffee break because it takes time to install all the stuff. At the end of this process, you have a Kubernetes cluster up and running.&lt;/p&gt;
&lt;p&gt;To be sure, log in on the master nodes (the external address given by terraform) and enter the command&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;kubectl -n kube-system get pods
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should be able to see all pods of the kube-system namespace running.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/009-1_iozJBhJS2Dh6CTEopZKzdA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The last step is to install the persistent volume from our GlusterFS.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ansible-playbook --become -i inventory/jhub/hosts ./contrib/network-storage/glusterfs/glusterfs.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you log in again to the master of your Kubernetes cluster and enter the following command&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;kubectl get pv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;you will see your GlusterFS storage connected to your Kubernetes cluster.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/010-1_qTxenvjzUpkvnt4SLCYyYQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="install-jupyterhub"&gt;Install JupyterHub&lt;/h3&gt;
&lt;p&gt;Now that you have a Kubernetes cluster running, the procedure to install JupyterHub is exactly the same as the one described in &lt;a href="https://zero-to-jupyterhub.readthedocs.io"&gt;Zero to JupyterHub&lt;/a&gt;. The only difference is that you don’t have to install Helm, since Kubespray did it for you. We’ll post the commands below, and you can go to the Zero to JupyterHub website for more information.&lt;/p&gt;
&lt;p&gt;The first step is to log in to the master node of your Kubernetes cluster. Then, initialize Helm.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;helm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tiller&lt;/span&gt;

&lt;span class="nx"&gt;kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;patch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tiller&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;kube&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;op&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;add&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/spec/template/spec/containers/0/command&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/tiller&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--listen=localhost:44134&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, follow the procedure described here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://zero-to-jupyterhub.readthedocs.io/en/stable/setup-jupyterhub.html"&gt;Setting up JupyterHub - Zero to JupyterHub with Kubernetes 0.7.0 documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If the LoadBalancer did its job, you should be able to see the external IP to connect to your JupyterHub&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;kubectl -n jhub  get svc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you enter this address in your web browser. If everything worked, you will see the JupyterHub login page:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterHub login page" src="https://jupyter.org/blog/posts/2018/how-to-deploy-jupyterhub-with-kubernetes-on-openstack/images/011-1_X6gU8TYrMV9v-GarkSWvaQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterHub login page&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="wrapping-up-and-feedback"&gt;Wrapping up and feedback&lt;/h3&gt;
&lt;p&gt;The steps above described our attempts at running a JupyterHub on Kubernetes using OpenStack. There are likely many other ways to accomplish the same thing, and we’d love to hear feedback on the best procedure to install JupyterHub or BinderHub on OpenStack infrastructure. If you have encountered any issues, please ping us on the gitter channel of &lt;a href="https://gitter.im/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; or &lt;a href="https://gitter.im/binder-project/binder"&gt;Binder&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thanks to the Project Jupyter team for their review and helpful comments and especially to Sylvain Corlay and Chris Holdgraf.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="about-the-authors-alphabetical-order"&gt;About the Authors (alphabetical order)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;David Delavennat&lt;/strong&gt;, Research Engineer in Scientific Infrastructures at CMLS (Polytechnique/CNRS) and INSMI (CNRS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loïc Gouarin&lt;/strong&gt;, Research Engineer in Scientific Computing at CMAP (Polytechnique/CNRS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guillaume Philippon&lt;/strong&gt;, Research Engineer in Scientific Infrastructures at LAL (IN2P3/CNRS)&lt;/li&gt;
&lt;/ul&gt;
</content><category term="JupyterHub"/><category term="Kubernetes"/></entry><entry><title>Interactive Workflows for C++ with Jupyter</title><link href="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/" rel="alternate"/><published>2017-11-29T16:33:00+00:00</published><updated>2019-12-25T09:42:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jupyter.org,2017-11-29:/blog/posts/2017/interactive-workflows-for-c-with-jupyter/</id><summary type="html">&lt;p&gt;Scientists, educators and engineers not only use programming languages to build software systems, but also in interactive workflows, using the tools available to explore a problem and reason about it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Scientists, educators and engineers not only use programming languages to build software systems, but also in interactive workflows, using the tools available to &lt;em&gt;explore&lt;/em&gt; a problem and &lt;em&gt;reason&lt;/em&gt; about it.&lt;/p&gt;
&lt;p&gt;Running some code, looking at a visualization, loading data, and running more code. Quick iteration is especially important during the exploratory phase of a project.&lt;/p&gt;
&lt;p&gt;For this kind of workflow, users of the C++ programming language currently have no choice but to use a heterogeneous set of tools that don’t play well with each other, making the whole process cumbersome, and difficult to reproduce.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;We currently lack a good story for interactive computing in C++&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In our opinion, this hurts the productivity of C++ developers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Most of the progress made in software projects comes from incrementalism. Obstacles to fast iteration hinder progress.&lt;/li&gt;
&lt;li&gt;This also makes C++ more difficult to teach. The first hours of a C++ class are rarely rewarding as the students must learn how to set up a small project before writing any code. And then, a lot more time is required before their work can result in any visual outcome.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="project-jupyter-and-interactive-computing"&gt;Project Jupyter and Interactive Computing&lt;/h2&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/001-1_wOHyKy6fl3ltcBMNpCvC6Q.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The goal of Project Jupyter is to provide a consistent set of tools for scientific computing and data science workflows, from the exploratory phase of the analysis to the presentation and the sharing of the results. The Jupyter stack was designed to be agnostic of the programming language, and also to allow alternative implementations of any component of the layered architecture (back-ends for programming languages, custom renderers for file types associated with Jupyter). The stack consists of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a low-level specification for messaging protocols, standardized file formats,&lt;/li&gt;
&lt;li&gt;a reference implementation of these standards,&lt;/li&gt;
&lt;li&gt;applications built on top of these libraries: the Notebook, JupyterLab, Binder, JupyterHub&lt;/li&gt;
&lt;li&gt;and visualization libraries integrated into the Notebook and JupyterLab.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adoption of the Jupyter ecosystem has skyrocketed in the past years, with millions of users worldwide, over a million Jupyter notebooks shared on GitHub and large-scale deployments of Jupyter in universities, companies and high-performance computing centers.&lt;/p&gt;
&lt;h2 id="jupyter-and-c"&gt;Jupyter and C++&lt;/h2&gt;
&lt;p&gt;One of the main extension points of the Jupyter stack is the &lt;em&gt;kernel&lt;/em&gt;, the part of the infrastructure responsible for executing the user’s code. Jupyter kernels exist for &lt;a href="https://github.com/jupyter/jupyter/wiki/Jupyter-kernels"&gt;numerous programming languages&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most Jupyter kernels are implemented in the target programming language: the reference implementation &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt; in Python, &lt;a href="https://github.com/JuliaLang/IJulia.jl"&gt;IJulia&lt;/a&gt; in Julia, leading to a duplication of effort for the implementation of the protocol. A common denominator to a lot of these interpreted languages is that the interpreter generally exposes a C API, allowing the embedding into a native application. In an effort to consolidate these commonalities and save work for future kernel builders, we developed &lt;em&gt;xeus&lt;/em&gt;.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/002-1_TKrPv5AvFM3NJ6a7VMu8Tw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xeus"&gt;Xeus&lt;/a&gt; is a C++ implementation of the Jupyter kernel protocol. It is not a kernel itself but a library that facilitates the authoring of kernels, and other applications making use of the Jupyter kernel protocol.&lt;/p&gt;
&lt;p&gt;A typical kernel implementation using xeus would in fact make use of the target interpreter &lt;em&gt;as a library.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are a number of benefits of using xeus over implementing your kernel in the target language:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Xeus provides a complete implementation of the protocol, enabling a lot of features from the start for kernel authors, who only need to deal with the language bindings.&lt;/li&gt;
&lt;li&gt;Xeus-based kernels can very easily provide a back-end for Jupyter interactive widgets.&lt;/li&gt;
&lt;li&gt;Finally, xeus can be used to implement kernels for domain-specific languages such as SQL flavors. Existing approaches use a Python wrapper. With xeus, the resulting kernel won’t require Python at run-time, leading to large performance benefits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/003-1_Cr_cfHdrgFXHlO15qdNK7w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interpreted C++&lt;/strong&gt; is already a reality at CERN with the &lt;a href="https://root.cern.ch/cling"&gt;Cling&lt;/a&gt; C++ interpreter in the context of the &lt;a href="https://root.cern.ch/"&gt;ROOT&lt;/a&gt; data analysis environment.&lt;/p&gt;
&lt;p&gt;As a first example for a kernel based on xeus, we have implemented &lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt;, a pure C++ kernel.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Redirection of outputs to the Jupyter front-end, with different styling in the front-end." src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/004-1_NnjISpzZtpy5TOurg0S89A.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Redirection of outputs to the Jupyter front-end, with different styling in the front-end.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Complex features of the C++ programming language such as, polymorphism, templates, lambdas, are supported by the cling interpreter, making the C++ Jupyter notebook a great prototyping and learning platform for the C++ users. See the image below for a demonstration:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Features of the C++ programming language supported by the cling interpreter" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/005-1_lGVLY4fL1ytMfT-eWtoXkw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Features of the C++ programming language supported by the cling interpreter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finally, xeus-cling supports live quick-help, fetching the content on &lt;a href="http://en.cppreference.com/w/"&gt;cppreference&lt;/a&gt; in the case of the standard library.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Live help for the C++standard library in the Jupyter notebook" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/006-1_Igegq0xBebuJV8hy0TGpfg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Live help for the C++standard library in the Jupyter notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;We realized that we started using the C++ kernel ourselves very early in the development of the project. For quick experimentation, or reproducing bugs. No need to set up a project with a cpp file and complicated project settings for finding the dependencies… Just write some code and hit &lt;strong&gt;Shift+Enter&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Visual output can also be displayed using the rich display mechanism of the Jupyter protocol.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Using Jupyter's rich display mechanism to display an image inline in the notebook" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/007-1_t_9qAXtdkSXr-0tO9VvOzQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Using Jupyter’s rich display mechanism to display an image inline in the notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/008-1_OVfmXFAbfjUtGFXYS9fKRA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Another important feature of the Jupyter ecosystem are the &lt;a href="http://jupyter.org/widgets"&gt;Jupyter Interactive Widgets&lt;/a&gt;. They allow the user to build graphical interfaces and interactive data visualization inline in the Jupyter notebook. Moreover it is not just a collection of widgets, but a framework that can be built upon, to create arbitrary visual components. Popular interactive widget libraries include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/bloomberg/bqplot"&gt;bqplot&lt;/a&gt; (2-D plotting with d3.js)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jovyan/pythreejs"&gt;pythreejs&lt;/a&gt; (3-D scene visualization with three.js)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ellisonbg/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; (maps visualization with leaflet.js)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/maartenbreddels/ipyvolume"&gt;ipyvolume&lt;/a&gt; (3-D plotting and volume rendering with three.js)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/arose/nglview"&gt;nglview&lt;/a&gt; (molecular visualization)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just like the rest of the Jupyter ecosystem, Jupyter interactive widgets were designed as a language-agnostic framework. Other language back-ends can be created reusing the front-end component, which can be installed separately.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QUantStack/xwidgets"&gt;xwidgets&lt;/a&gt;, which is still at an early stage of development, is a native C++ implementation of the Jupyter widgets protocol. It already provides an implementation for most of the widget types available in the core Jupyter widgets package.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="C++ back-end to the Jupyter interactive widgets" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/009-1_ro5Ggdstnf0DoqhTUWGq3A.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;C++ back-end to the Jupyter interactive widgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Just like with ipywidgets, one can build upon xwidgets and implement C++ back-ends for the Jupyter widget libraries listed earlier, effectively enabling them for the C++ programming language and other xeus-based kernels: xplot, xvolume, xthreejs…&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/010-1_yCRYoJFnbtxYkYMRc9AioA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xplot"&gt;xplot&lt;/a&gt; is an experimental C++ back-end for the &lt;a href="https://github.com/bloomberg/bqplot"&gt;bqplot&lt;/a&gt; 2-D plotting library. It enables an API following the constructs of the &lt;a href="https://dl.acm.org/citation.cfm?id=1088896"&gt;&lt;em&gt;Grammar of Graphics&lt;/em&gt;&lt;/a&gt; in C++.&lt;/p&gt;
&lt;p&gt;In xplot, every item in a chart is a separate object that can be modified from the back-end, &lt;em&gt;dynamically&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Changing a property of a plot item, a scale, an axis or the figure canvas itself results in the communication of an update message to the front-end, which reflects the new state of the widget visually.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Changing the data of a scatter plot dynamically to update the chart" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/011-1_Mx2g3JuTG1Cfvkkv0kqtLA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Changing the data of a scatter plot dynamically to update the chart&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; the xplot and xwidgets projects are still at an early stage of development and are changing drastically at each release.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Interactive computing environments like Jupyter are not the only missing tool in the C++ world. Two key ingredients to the success of Python as the &lt;em&gt;lingua franca&lt;/em&gt; of data science is the existence of libraries like &lt;a href="http://www.numpy.org/"&gt;NumPy&lt;/a&gt; and &lt;a href="https://pandas.pydata.org/"&gt;Pandas&lt;/a&gt; at the foundation of the ecosystem.&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/012-1_HsU43Jzp1vJZpX2g8XPJsg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xtensor/"&gt;xtensor&lt;/a&gt; is a C++ library meant for numerical analysis with multi-dimensional array expressions.&lt;/p&gt;
&lt;p&gt;xtensor provides&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an extensible expression system enabling lazy NumPy-style broadcasting.&lt;/li&gt;
&lt;li&gt;an API following the &lt;em&gt;idioms&lt;/em&gt; of the C++ standard library.&lt;/li&gt;
&lt;li&gt;tools to manipulate array expressions and build upon xtensor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;xtensor exposes an API similar to that of NumPy covering a growing portion of the functionalities. A cheat sheet can be &lt;a href="http://xtensor.readthedocs.io/en/latest/numpy.html"&gt;found in the documentation&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Scrolling the NumPy to xtensor cheat sheet" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/013-1_PBrf5vWYC8VTq_7VUOZCpA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Scrolling the NumPy to xtensor cheat sheet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, xtensor internals are very different from NumPy. Using modern C++ techniques (template expressions, closure semantics) xtensor is a lazily evaluated library, avoiding the creation of temporary variables and unnecessary memory allocations, even in the case complex expressions involving broadcasting and language bindings.&lt;/p&gt;
&lt;p&gt;Still, from a user perspective, the combination of xtensor with the C++ notebook provides an experience very similar to that of NumPy in a Python notebook.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Using the xtensor array expression library in a C++ notebook" src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/014-1_ULFpg-ePkdUbqqDLJ9VrDw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Using the xtensor array expression library in a C++ notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In addition to the core library, the xtensor ecosystem has a number of other components&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xtensor-blas"&gt;&lt;strong&gt;xtensor-blas&lt;/strong&gt;&lt;/a&gt;: the counterpart to the numpy.linalg module.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/egpbos/xtensor-fftw"&gt;&lt;strong&gt;xtensor-fftw&lt;/strong&gt;&lt;/a&gt;: bindings to the &lt;a href="http://www.fftw.org/"&gt;fftw&lt;/a&gt; library.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xtensor-io"&gt;&lt;strong&gt;xtensor-io&lt;/strong&gt;&lt;/a&gt;: APIs to read and write various file formats (images, audio, NumPy’s NPZ format).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wolfv/xtensor_ros"&gt;&lt;strong&gt;xtensor-ros&lt;/strong&gt;&lt;/a&gt;: bindings for ROS, the robot operating system.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xtensor-python"&gt;&lt;strong&gt;xtensor-python&lt;/strong&gt;&lt;/a&gt;: bindings for the Python programming language, allowing the use of NumPy arrays in-place, using the NumPy C API and the pybind11 library.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/Xtensor.jl"&gt;&lt;strong&gt;xtensor-julia&lt;/strong&gt;&lt;/a&gt;: bindings for the Julia programming language, allowing the use of Julia arrays in-place, using the C API of the Julia interpreter, and the CxxWrap library.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xtensor-r"&gt;&lt;strong&gt;xtensor-r&lt;/strong&gt;&lt;/a&gt;: bindings for the R programming language, allowing the use of R arrays in-place.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Detailing further the features of the xtensor framework would be beyond the scope of this post.&lt;/p&gt;
&lt;p&gt;If you are interested in trying the various notebooks presented in this post, there is no need to install anything. You can just use &lt;em&gt;binder&lt;/em&gt;:&lt;/p&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/015-1_9cy5Mns_I0eScsmDBjvxDQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/"&gt;The Binder project&lt;/a&gt;, which is part of Project Jupyter, enables the deployment of containerized Jupyter notebooks, from a GitHub repository together with a manifest listing the dependencies (as conda packages).&lt;/p&gt;
&lt;p&gt;All the notebooks in the screenshots above can be run online, by just clicking on one of the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mybinder.org/v2/gh/QuantStack/xeus-cling/stable?filepath=notebooks/xcpp.ipynb"&gt;&lt;strong&gt;xeus-cling&lt;/strong&gt;&lt;/a&gt;: the main xeus-cling example notebook,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mybinder.org/v2/gh/QuantStack/xtensor/stable?filepath=notebooks/xtensor.ipynb"&gt;&lt;strong&gt;xtensor&lt;/strong&gt;&lt;/a&gt;: the C++ N-D array expression library in a C++ notebook,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mybinder.org/v2/gh/QuantStack/xwidgets/0.11.1?filepath=notebooks/xwidgets.ipynb"&gt;&lt;strong&gt;xwidgets&lt;/strong&gt;&lt;/a&gt;: the C++ back-end for Jupyter interactive widgets,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mybinder.org/v2/gh/QuantStack/xplot/0.5.0?filepath=notebooks"&gt;&lt;strong&gt;xplot&lt;/strong&gt;&lt;/a&gt;: the C++ back-end to the bqplot 2-D plotting library for Jupyter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="standalone-image"&gt;&lt;img src="https://jupyter.org/blog/posts/2017/interactive-workflows-for-c-with-jupyter/images/016-1_JwqhpMxMJppEepj7U4fV-g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; is the multi-user infrastructure underlying open wide deployments of Jupyter like Binder but also smaller deployments for authenticated users.&lt;/p&gt;
&lt;p&gt;The modular architecture of JupyterHub enables a great variety of scenarios on how users are authenticated, and what service is made available to them. JupyterHub deployment for several hundreds of users have been done in various universities and institutions, including the Paris-Sud University, where the C++ kernel was also installed for the students to use.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In September 2017, the 350 first-year students at Paris-Sud University who took the “&lt;a href="http://nicolas.thiery.name/Enseignement/Info111/"&gt;Info 111: Introduction to Computer
Science&lt;/a&gt;” class wrote their first lines of C++ in a Jupyter notebook.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The use of Jupyter notebooks in the context of teaching C++ proved especially useful for the first classes, where students can focus on the syntax of the language without distractions such as compiling and linking.&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;The software presented in this post was built upon the work of a large number of people including the &lt;strong&gt;Jupyter&lt;/strong&gt; team and the &lt;strong&gt;Cling&lt;/strong&gt; developers.&lt;/p&gt;
&lt;p&gt;We are especially grateful to &lt;a href="https://twitter.com/egpbos"&gt;Patrick Bos&lt;/a&gt; (who authored xtensor-fftw), Nicolas Thiéry, Min Ragan Kelley, Thomas Kluyver, Yuvi Panda, Kyle Cranmer, Axel Naumann and Vassil Vassilev.&lt;/p&gt;
&lt;p&gt;We thank the &lt;a href="http://diana-hep.org"&gt;DIANA/HEP&lt;/a&gt; organization for supporting travel to CERN and encouraging the collaboration between Project Jupyter and the ROOT team.&lt;/p&gt;
&lt;p&gt;We are also grateful to the team at &lt;strong&gt;Paris-Sud University&lt;/strong&gt; who worked on the JupyterHub deployment and the class materials, notably &lt;a href="https://twitter.com/pyviv"&gt;Viviane Pons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The development of xeus, xtensor, xwidgets and related packages at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; is sponsored by &lt;a href="http://www.techatbloomberg.com"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="about-the-authors-alphabetical-order"&gt;About the Authors (alphabetical order)&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/SylvainCorlay"&gt;&lt;em&gt;Sylvain Corlay&lt;/em&gt;&lt;/a&gt;, Scientific Software Developer at &lt;a href="https://github.com/QuantStack/"&gt;QuantStack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/lgouarin"&gt;&lt;em&gt;Loic Gouarin&lt;/em&gt;&lt;/a&gt;, Research Engineer at &lt;a href="https://www.math.u-psud.fr"&gt;Laboratoire de Mathématiques at Orsay&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/johanmabille?lang=en"&gt;&lt;em&gt;Johan Mabille&lt;/em&gt;&lt;/a&gt;, Scientific Software Developer at &lt;a href="https://github.com/QuantStack/"&gt;QuantStack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/wuoulf"&gt;&lt;em&gt;Wolf Vollprecht&lt;/em&gt;&lt;/a&gt;, Scientific Software Developer at &lt;a href="https://github.com/QuantStack/"&gt;QuantStack&lt;/a&gt;&lt;/p&gt;
</content><category term="C++"/><category term="science"/></entry></feed>