<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Kevin Bates</title><link href="https://jupyter.org/blog/" rel="alternate"/><link href="https://jupyter.org/blog/feeds/author-kevin-bates.atom.xml" rel="self"/><id>https://jupyter.org/blog/</id><updated>2018-11-01T20:29:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>On-demand Notebooks with JupyterHub, Jupyter Enterprise Gateway and Kubernetes</title><link href="https://jupyter.org/blog/posts/2018/on-demand-notebooks-with-jupyterhub-jupyter-enterprise/" rel="alternate"/><published>2018-10-16T15:03:00+00:00</published><updated>2018-11-01T20:29:00+00:00</updated><author><name>Luciano Resende</name></author><id>tag:jupyter.org,2018-10-16:/blog/posts/2018/on-demand-notebooks-with-jupyterhub-jupyter-enterprise/</id><summary type="html">&lt;p&gt;This article describes how to deploy multiple components from the Jupyter Notebook stack to provide an on-demand analytics platform powered by JupyterHub and Jupyter Enterprise Gateway on a Kubernetes cluster.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://jupyter-notebook.readthedocs.io/en/stable/"&gt;&lt;strong&gt;Jupyter Notebook&lt;/strong&gt;&lt;/a&gt; has become the “de facto” platform used by data scientists to build interactive applications and to tackle big data and AI problems.&lt;/p&gt;
&lt;p&gt;With the increased adoption of Machine Learning and AI by enterprises, we have seen more and more requirement to build analytics platforms that provide on-demand notebooks for data scientists and data engineers in general.&lt;/p&gt;
&lt;p&gt;This article describes how to deploy multiple components from the Jupyter Notebook stack to provide an on-demand analytics platform powered by JupyterHub and Jupyter Enterprise Gateway on a Kubernetes cluster.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Image 1 — Deployment Architecture" src="https://jupyter.org/blog/posts/2018/on-demand-notebooks-with-jupyterhub-jupyter-enterprise/images/001-1_F_jJ1nDSQgBhrkbsEXB93Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Image 1 — Deployment Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="on-demand-notebooks-infrastructure"&gt;On-Demand Notebooks Infrastructure&lt;/h2&gt;
&lt;p&gt;Below are the main components we are going to use to build our solution, and its high-level description:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterhub.readthedocs.io/"&gt;JupyterHub&lt;/a&gt; enables the creation of a multi-user Hub which spawns, manages, and proxies multiple instances of the single-user &lt;a href="https://jupyter-notebook.readthedocs.io/"&gt;Jupyter Notebook&lt;/a&gt; server providing the ‘as a service’ feeling we are looking for.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyter-enterprise-gateway.readthedocs.io/en/latest/"&gt;Jupyter Enterprise Gateway&lt;/a&gt; provides optimal resource allocations by enabling kernels to be launched in its own pod enabling notebook pods to have minimal resources while kernel specific resources are allocated/deallocated accordingly to its lifecycle. It also enables the base image of the kernel to become a choice.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; enables easy management of containerized applications and resources with the benefit of Elasticity and multiple other quality of services.&lt;/p&gt;
&lt;h2 id="jupyterhub-deployment"&gt;JupyterHub Deployment&lt;/h2&gt;
&lt;p&gt;JupyterHub is the entry point for our solution, it will manage user authorization and provisioning of individual Notebook servers for each user.&lt;/p&gt;
&lt;p&gt;JupyterHub configuration is done via a config.yaml, and the following settings are required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Enable custom notebook configuration (coming from the customized user image).&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;hub&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;extraConfig&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="n"&gt;config&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="s1"&gt;&amp;#39;/etc/jupyter/jupyter_notebook_config.py&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define the docker image to be used when instantiating the notebook server for each user&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define custom environment variables used to connect the Notebook server with Jupyter Enterprise Gateway to enable support for remote kernels&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;singleuser&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elyra&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nb2kg&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;storageClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;extraEnv&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KG_URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FQDN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Gateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KG_HTTP_USER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jovyan&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KERNEL_USERNAME&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jovyan&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KG_REQUEST_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The complete config.yaml would look like the one below:&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;hub&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;extraConfig&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="n"&gt;config&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="s1"&gt;&amp;#39;/etc/jupyter/jupyter_notebook_config.py&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Spawner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cmd&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="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;jupyter-labhub&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;secretToken&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;xxx&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;ingress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;hosts&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FQDN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Kubernetes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Master&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;singleuser&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;defaultUrl&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;/lab&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elyra&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nb2kg&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dev0&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;storageClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;extraEnv&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KG_URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FQDN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Gateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KG_HTTP_USER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jovyan&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KERNEL_USERNAME&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jovyan&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;KG_REQUEST_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;

&lt;span class="n"&gt;rbac&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Detailed deployment instructions for JupyterHub can be found at &lt;a href="https://zero-to-jupyterhub.readthedocs.io/en/stable/"&gt;Zero to JupyterHub for Kubernetes&lt;/a&gt;, but the command below would deploy it into a Kubernetes environment.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;helm upgrade --install --force hub jupyterhub/jupyterhub --namespace hub --version 0.7.0 --values jupyterhub-config.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="custom-jupyterhub-user-image"&gt;Custom JupyterHub user image&lt;/h2&gt;
&lt;p&gt;By default, JupyterHub would deploy a vanilla Notebook Server image which will require that all resources ever used by the image to be allocated when the Kubernetes image is instantiated.&lt;/p&gt;
&lt;p&gt;Our custom image will enable kernels to be started in its own pod, promoting a better resource allocation as resources can be allocated and freed up as needed. This also gives us the flexibility of supporting different frameworks for different notebooks (e.g. a notebook using Python and TensorFlow, while another is using Python and Caffe2).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Dockerfile for &lt;a href="https://github.com/jupyter/enterprise_gateway/tree/master/etc/docker/nb2kg"&gt;elyra-nb2kg custom image&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;FROM jupyterhub/k8s-singleuser-sample:0.7.0

&lt;span class="gh"&gt;#&lt;/span&gt; Do the pip installs as the unprivileged notebook user
USER $NB_USER

ADD jupyter_notebook_config.py /etc/jupyter/jupyter_notebook_config.py

&lt;span class="gh"&gt;#&lt;/span&gt; Install NB2KG
RUN pip install --upgrade nb2kg &amp;amp;&amp;amp; \
    jupyter serverextension enable --py nb2kg --sys-prefix
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Jupyter Notebook custom configuration to override Notebook handlers with the ones from NB2KG that will enable the notebook to connect with the Enterprise Gateway that enables remote kernels.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jupyter_core.paths&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jupyter_data_dir&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;errno&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;stat&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8888&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_manager_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nb2kg.managers.SessionManager&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kernel_manager_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nb2kg.managers.RemoteKernelManager&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kernel_spec_manager_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nb2kg.managers.RemoteKernelSpecManager&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# https://github.com/jupyter/notebook/issues/3130&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileContentsManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_to_trash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

&lt;span class="c1"&gt;# Generate a self-signed certificate&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;GEN_CERT&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dir_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jupyter_data_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pem_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;notebook.pem&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;OSError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python &amp;gt;2.5&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errno&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EEXIST&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt;
    &lt;span class="c1"&gt;# Generate a certificate if one doesn&amp;#39;t exist on disk&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;openssl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;req&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s1"&gt;&amp;#39;-newkey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rsa:2048&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s1"&gt;&amp;#39;-days&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;365&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s1"&gt;&amp;#39;-nodes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-x509&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s1"&gt;&amp;#39;-subj&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/C=XX/ST=XX/L=XX/O=generated/CN=generated&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s1"&gt;&amp;#39;-keyout&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pem_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s1"&gt;&amp;#39;-out&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pem_file&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c1"&gt;# Restrict access to the file&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pem_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IRUSR&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S_IWUSR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotebookApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;certfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pem_file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that the document above was generated by &lt;code&gt;jupyter notebook --generate-config&lt;/code&gt; and then updated with the required handlers override:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c.NotebookApp.session_manager_class = &amp;#39;nb2kg.managers.SessionManager&amp;#39;
c.NotebookApp.kernel_manager_class = &amp;#39;nb2kg.managers.RemoteKernelManager&amp;#39;
c.NotebookApp.kernel_spec_manager_class = &amp;#39;nb2kg.managers.RemoteKernelSpecManager&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="jupyter-enterprise-gateway-deployment"&gt;Jupyter Enterprise Gateway deployment&lt;/h2&gt;
&lt;p&gt;Jupyter Enterprise Gateway enables Jupyter Notebook to launch and manage remote kernels in a distributed cluster, including Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;Enterprise Gateway provides a Kubernetes deployment descriptor that makes it simple to deploy it on a Kubernetes environment with the command below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/jupyter-incubator/enterprise_gateway/master/etc/kubernetes/enterprise-gateway.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also recommend that the kernel images be downloaded on all nodes of the Kubernetes cluster to avoid delays/timeouts when launching kernels for the first time on these nodes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker pull elyra/enterprise-gateway:dev
docker pull elyra/kernel-py:dev
docker pull elyra/kernel-tf-py:dev
docker pull elyra/kernel-r:dev
docker pull elyra/kernel-scala:dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="automated-one-click-deployment-using-ansible"&gt;Automated One-Click Deployment using Ansible&lt;/h2&gt;
&lt;p&gt;If you are eager to get started and try this in a few machines, we have published an &lt;a href="https://github.com/lresende/ansible-kubernetes-cluster"&gt;&lt;code&gt;ansible script&lt;/code&gt;&lt;/a&gt; that deploys the full set of components described above on vanilla RHEL machines/VMs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ansible-playbook --verbose setup-kubernetes.yml -c paramiko -i hosts-fyre-kubernetes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Jupyter Enterprise Gateway provides remote kernel management to Jupyter Notebooks. In a JupyterHub/Kubernetes environment, it enables hub to launch tiny Jupyter Notebook pods and only allocate large kernel resources when these are created as independent pods. This approach also allows for easy sharing of expensive resources as GPUs, etc&lt;/p&gt;
&lt;h2 id="special-thanks"&gt;Special Thanks&lt;/h2&gt;
&lt;p&gt;Special thanks to &lt;a href="https://twitter.com/e_sundell"&gt;Erik Sundell&lt;/a&gt; and &lt;a href="https://twitter.com/minrk"&gt;Min RK&lt;/a&gt; from the JupyterHub team for the support and initial discussions around JupyterHub.&lt;/p&gt;
</content><category term="Jupyter Enterprise Gateway"/><category term="JupyterHub"/><category term="Kubernetes"/></entry><entry><title>Introducing Jupyter Enterprise Gateway</title><link href="https://jupyter.org/blog/posts/2018/introducing-jupyter-enterprise-gateway/" rel="alternate"/><published>2018-09-17T20:19:00+00:00</published><updated>2018-09-17T20:19:00+00:00</updated><author><name>Luciano Resende</name></author><id>tag:jupyter.org,2018-09-17:/blog/posts/2018/introducing-jupyter-enterprise-gateway/</id><summary type="html">&lt;p&gt;Yesterday, the Jupyter Steering Council voted to make Jupyter Enterprise Gateway a top-level Jupyter Project.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Yesterday, the Jupyter Steering Council voted to make Jupyter Enterprise Gateway a &lt;a href="https://github.com/jupyter/enhancement-proposals/blob/master/jupyter-enterprise-gateway-incorporation/jupyter-enterprise-gateway-incorporation.md"&gt;top-level Jupyter Project&lt;/a&gt;. I want to thank everyone for their contributions so far — code from my teammates at IBM and the community in general; advice from the Jupyter development team and mentors; and questions, issues, and requirements from end users.&lt;/p&gt;
&lt;p&gt;As we become an official Jupyter project, I would like to take the opportunity to give an update on the project’s progress during our incubation period.&lt;/p&gt;
&lt;h2 id="what-is-jupyter-enterprise-gateway"&gt;What is Jupyter Enterprise Gateway?&lt;/h2&gt;
&lt;p&gt;Jupyter Enterprise Gateway enables Jupyter Notebook to launch remote kernels in a distributed cluster, including Apache Spark managed by YARN, IBM Spectrum Conductor or Kubernetes.&lt;/p&gt;
&lt;p&gt;Although Enterprise Gateway is mostly kernel agnostic, it provides out of the box configuration examples for the following kernels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python using &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; kernel&lt;/li&gt;
&lt;li&gt;R using &lt;a href="https://github.com/IRkernel/IRkernel"&gt;IRkernel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Scala using &lt;a href="https://toree.incubator.apache.org/"&gt;Apache Toree&lt;/a&gt; kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jupyter Enterprise Gateway does not manage multiple Jupyter Notebook deployments, for that you should look for &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt;. Having said that, Enterprise Gateway can enable &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; to launch remote kernels as individual Kubernetes pods, providing better resource allocation and enabling better environment management as each pod can be based on different images (e.g. TensorFlow, Anaconda, etc)&lt;/p&gt;
&lt;h2 id="supported-platforms"&gt;Supported Platforms&lt;/h2&gt;
&lt;p&gt;Jupyter Enterprise Gateway currently enables remote kernels in the following platforms:&lt;/p&gt;
&lt;h3 id="distributed-kernels-in-apache-spark"&gt;Distributed Kernels in Apache Spark&lt;/h3&gt;
&lt;p&gt;Jupyter Enterprise Gateway leverages different resource managers to enable distributed kernels in Apache Spark clusters. One example shown below describes kernels being launched in YARN cluster mode across all nodes of a cluster.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Enterprise Gateway leverages Apache Spark resource managers to distribute kernels" src="https://jupyter.org/blog/posts/2018/introducing-jupyter-enterprise-gateway/images/001-1_oKl3bDSanz-SFqsgWsAPBw.gif" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Enterprise Gateway leverages Apache Spark resource managers to distribute kernels&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that, Jupyter Enterprise Gateway also provides some other value-added capabilities such as enhanced security and multiuser support with user impersonation.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Enterprise Gateway provides Enhanced Security and Multiuser support with user Impersonation" src="https://jupyter.org/blog/posts/2018/introducing-jupyter-enterprise-gateway/images/002-1_ihpHPqvgzXKepRAVZc7EIA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Enterprise Gateway provides Enhanced Security and Multiuser support with user Impersonation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="distributed-kernels-in-kubernetes"&gt;Distributed Kernels in Kubernetes&lt;/h3&gt;
&lt;p&gt;Jupyter Enterprise Gateway support for Kubernetes enables decoupling the Jupyter Notebook Server and its kernels into multiple pods. This enables running Notebook server pods with minimally necessary resources based on the workload being processed.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Enterprise Gateway enable remote kernels on Kubernetes cluster" src="https://jupyter.org/blog/posts/2018/introducing-jupyter-enterprise-gateway/images/003-1__R0tS0CZLy__LmL7o5b6vg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Enterprise Gateway enable remote kernels on Kubernetes cluster&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="jupyter-enterprise-gateway-and-jupyterhub"&gt;Jupyter Enterprise Gateway and JupyterHub&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; is a multi-user server that manages and proxies multiple instances of the single-user Jupyter notebook server. Particularly in a Kubernetes environment, Jupyter Enterprise Gateway can enable &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; to launch remote kernels as individual Kubernetes pods, providing better resource allocation and enabling better environment management as each pod can be based on different images (e.g. TensorFlow, Anaconda, etc). This has proven to be very desired, particularly when working on Deep Learning related Notebooks.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterHub and Jupyter Enterprise Gateway together in a Kubernetes cluster" src="https://jupyter.org/blog/posts/2018/introducing-jupyter-enterprise-gateway/images/004-1_9QJMPJLTZ04CFLc30aapTg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterHub and Jupyter Enterprise Gateway together in a Kubernetes cluster&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="some-project-metrics"&gt;Some project metrics&lt;/h2&gt;
&lt;p&gt;The following stats have been collected from the Jupyter Enterprise Gateway GitHub repository &lt;strong&gt;during the incubation period&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10 releases&lt;/li&gt;
&lt;li&gt;12 individual contributors&lt;/li&gt;
&lt;li&gt;90 Stars&lt;/li&gt;
&lt;li&gt;34 Forks&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="source-code-documentation-and-other-community-resources"&gt;Source code, documentation, and other community resources&lt;/h2&gt;
&lt;p&gt;The Jupyter Enterprise Gateway community provides multiple resources that both users and contributors can use:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source Code available at GitHub&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/jupyter/enterprise_gateway"&gt;https://github.com/jupyter/enterprise_gateway&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentation available at ReadTheDocs&lt;/strong&gt;&lt;br&gt;
&lt;a href="http://jupyter-enterprise-gateway.readthedocs.io/en/latest/"&gt;http://jupyter-enterprise-gateway.readthedocs.io/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automated builds available at Travis.CI&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://travis-ci.org/jupyter-incubator/enterprise_gateway"&gt;https://travis-ci.org/jupyter/enterprise_gateway&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Releases available at PyPi.org and Conda Forge&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://pypi.org/project/jupyter_enterprise_gateway/"&gt;https://pypi.org/project/jupyter_enterprise_gateway/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/conda-forge/jupyter_enterprise_gateway-feedstock"&gt;https://github.com/conda-forge/jupyter_enterprise_gateway-feedstock&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Related Docker Images available at Elyra organization at DockerHub&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://hub.docker.com/u/elyra/dashboard/"&gt;https://hub.docker.com/u/elyra/dashboard/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;We are eager to build an even greater community around the project, and tailor the project roadmap based on community advise.&lt;/p&gt;
&lt;p&gt;Currently, we are busy working on advancing our Kubernetes support and integration with JupyterHub.&lt;/p&gt;
&lt;p&gt;As always, we welcome questions, comments, and suggestions from users and the community in general.&lt;/p&gt;
</content><category term="kernels"/><category term="Kubernetes"/></entry></feed>