{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Panel is built on top of Bokeh, which provides a powerful [Tornado](https://www.tornadoweb.org/en/stable/) based web-server to communicate between Python and the browser. The bokeh server makes it possible to share the app or dashboard you have built locally, your own web server or using any of the numerous cloud providers. In this guide we will go through the details of deploying an app on a local system or cloud provider step by step." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The server\n", "\n", "The Bokeh server is built on Tornado, which handles all of the communication between the browser and the backend. Whenever a user accesses the app or dashboard in a browser a new session is created which executes the app code and creates a new ``Document`` containing the models served to the browser where they are rendered by BokehJS. \n", "\n", "
\n", "\n", "
\n", "\n", "### Accessing request arguments\n", "\n", "When a user accesses the Panel application via the browser they can optionally provide additional arguments in the URL. For example the query string ``?N=10`` will result in the following argument will be available on ``pn.state.session_args``: ``{'N': [b'10']}``. Such arguments may be used to customize the application." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Deployment\n", "\n", "As was covered in [Deploy and Export](Deploy_and_Export.ipynb) guide a Panel app, either in a notebook or a Python script, can be annotated with `.servable()` and then launched from the commandline using ``panel serve``. This launches a Tornado server on a specific port (defaulting to 5006) which you can access locally at ``https://localhost:{PORT}``. This is a good option for simple deployments on a local network.\n", "\n", "However many deployment scenarios have additional requirements around authentication, scaling, and uptime." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### SSH\n", "\n", "In some scenarios a standalone bokeh server may be running on remote host. In such cases, SSH can be used to “tunnel” to the server. In the simplest scenario, the Bokeh server will run on one host and will be accessed from another location, e.g., a laptop, with no intermediary machines.\n", "\n", "Run the server as usual on the remote host:\n", "\n", "Next, issue the following command on the local machine to establish an SSH tunnel to the remote host:\n", "\n", "```\n", "ssh -NfL localhost:5006:localhost:5006 user@remote.host\n", "```\n", "\n", "Replace user with your username on the remote host and remote.host with the hostname/IP address of the system hosting the Bokeh server. You may be prompted for login credentials for the remote system. After the connection is set up you will be able to navigate to localhost:5006 as though the Bokeh server were running on the local machine.\n", "\n", "The second, slightly more complicated case occurs when there is a gateway between the server and the local machine. In that situation a reverse tunnel must be established from the server to the gateway. Additionally the tunnel from the local machine will also point to the gateway.\n", "\n", "Issue the following commands on the remote host where the Bokeh server will run:\n", "\n", "```\n", "nohup bokeh server &\n", "ssh -NfR 5006:localhost:5006 user@gateway.host\n", "```\n", "\n", "Replace user with your username on the gateway and gateway.host with the hostname/IP address of the gateway. You may be prompted for login credentials for the gateway.\n", "\n", "Now set up the other half of the tunnel, from the local machine to the gateway. On the local machine:\n", "\n", "```\n", "ssh -NfL localhost:5006:localhost:5006 user@gateway.host\n", "```\n", "\n", "Again, replace user with your username on the gateway and gateway.host with the hostname/IP address of the gateway. You should now be able to access the Bokeh server from the local machine by navigating to localhost:5006 on the local machine, as if the Bokeh server were running on the local machine. You can even set up client connections from a Jupyter notebook running on the local machine." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reverse proxy\n", "\n", "If the goal is to serve an web application to the general Internet, it is often desirable to host the application on an internal network, and proxy connections to it through some dedicated HTTP server. For some basic configurations to set up a Bokeh server behind some common reverse proxies, including Nginx and Apache, refer to the [Bokeh documentation](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html#basic-reverse-proxy-setup)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Cloud Deployments\n", "\n", "If you do not want to maintain your own web server and/or set up complex reverse proxies various cloud providers make it relatively simple to quickly deploy arbitrary apps on their system. In this section we will go through step-by-step to set up deployments on some of these providers.\n", "\n", "#### MyBinder\n", "\n", "Binder allows you to create custom computing environments that can be shared and used by many remote users. MyBinder is a public, free hosting option, with limited compute and memory resources, which will allow you to deploy your simple app quickly and easily.\n", "\n", "Here we will take you through the configuration to quickly set up a GitHub repository with notebooks containing Panel apps for deployment on MyBinder.org. As an example refer to the [Clifford demo repository](https://github.com/pyviz-demos/clifford).\n", "\n", "1. Create a GitHub repository and add the notebook or script you want to serve (in the example repository this is the clifford.ipynb file)\n", "\n", "2. Add an ``environment.yml`` which declares a conda environment with the dependencies required to run the app (refer to the [conda documentation](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-file-manually) to see how to declare your dependencies). Add `jupyter_panel_proxy` as a dependency by adding either `conda-forge` or `pyviz` to the channel list:\n", "\n", "\n", "```\n", "channels:\n", "- pyviz\n", "\n", "packages:\n", "- jupyter-panel-proxy\n", "```\n", "\n", "3. Go to mybinder.org, enter the URL of your GitHub repository and hit ``Launch``\n", "\n", "4. mybinder.org will give you a link to the deployment, e.g. for the example app it is https://mybinder.org/v2/gh/panel-demos/clifford-interact/master. To visit the app simply append ``?urlpath=/panel/clifford`` where you should replace clifford with the name of the notebook or script you are serving.\n", "\n", "\n", "#### Heroku\n", "\n", "Heroku makes deployment of arbitrary apps including Panel apps and dashboards very easy and provides a free tier to get you started. This makes it a great starting point for users not too familiar with web development and deployment.\n", "\n", "To get started working with Heroku [signup](https://signup.heroku.com) for a free account and [download and install the CLI](https://devcenter.heroku.com/articles/getting-started-with-python#set-up). Once you are set up follow the instructions to log into the CLI.\n", "\n", "1. Create a new Git repo (or to follow along clone the [minimal-heroku-demo](https://github.com/pyviz-demos/minimal-heroku-demo) GitHub repo)\n", "\n", "2. Add a Jupyter notebook or Python script which declares a Panel app or dashboard to the repository.\n", "\n", "3. Define a requirements.txt containing all the requirements for your app (including Panel itself). For the sample app the requirements are as minimal as:\n", "\n", "```\n", "panel\n", "hvplot\n", "scikit-learn\n", "```\n", "\n", "3. Define a `Procfile` which declares the command Heroku should run to serve the app. In the sample app the following command serves the `iris_kmeans.ipynb` example. The websocket origin should match the name of the app on Heroku ``app-name.herokuapp.com`` which you will declare in the next step:\n", "\n", "```\n", "web: panel serve --address=\"0.0.0.0\" --port=$PORT iris_kmeans.ipynb --allow-websocket-origin=app-name.herokuapp.com\n", "```\n", "\n", "4. Create a Heroku app using the CLI ensuring that the name matches the URL we declared in the previous step:\n", "\n", "```\n", "heroku create app-name\n", "```\n", "\n", "5. Push the app to heroku and wait until it is deployed.\n", "\n", "6. Visit the app at app-name.herokuapp.com\n", "\n", "Once you have deployed the app you might find that if your app is visited by more than one user at a time it will become unresponsive. In this case you can use the Heroku CLI [to scale your deployment](https://devcenter.heroku.com/articles/getting-started-with-python#scale-the-app).\n", "\n", "#### Anaconda Enterprise 5 (AE5)\n", "\n", "All live examples in the Panel documentation are served on AE5, to see further examples deployed there see [examples.pyviz.org](https://examples.pyviz.org) and for detailed instructions follow the [developer guide](https://examples.pyviz.org/developer_guide.html).\n", "\n", "#### Microsoft Azure\n", "\n", "Azure is popular choice for enterprises often in combination with an automated CI/CD pipeline via Azure DevOps. To get started you can use the [Azure Portal](portal.azure.com) to deploy your app as a Linux Web App via the web based user interface.\n", "\n", "There are a few things you need to be aware of in order to be able to start your app. \n", "\n", "Python Web Apps assumes your web app\n", "\n", "- is using `gunicorn` (like Flask or Django) or alternative is started by a `python` command. Thus\n", " - You **cannot use** `panel serve app.py ...` as a *Startup Command*.\n", " - You **can use** `python -m panel serve app.py ...` or `python app.py ...` as a *Startup command*.\n", "- is served on address 0.0.0.0 and port 8000\n", "\n", "Thus you can use \n", "\n", "```bash\n", "python -m panel serve app.py --address 0.0.0.0 --port 8000 --allow-websocket-origin=app-name.azurewebsites.net\n", "```\n", "\n", "as a *Startup command*.\n", "\n", "You might be able to use `python app.py` as a *Startup command* with `.show()` or `panel.serve` inside your `app.py` file, if you can configure the `address`, `port` and `allow-websocket-origin` in the app.py file or via environment variables.\n", "\n", "You also need to configure your app service **general settings** to \n", "\n", "- allow `Web sockets` and\n", "- be `Always on`\n", "\n", "\n", "\n", "If you would like to setup **automated CI/ CD** via Azure DevOps, Azure Pipelines and Docker to a Web App for Containers, you can find a good starting point in the [devops Folder](https://github.com/MarcSkovMadsen/awesome-panel/tree/master/devops) of [awesome-panel.org](https://awesome-panel.org).\n", "\n", "\n", "#### Google Cloud Platform (GCP) \n", "\n", "First, you need to set up your Google cloud account following the [Cloud Run documentation](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python) or the [App Engine documentation](https://cloud.google.com/appengine/docs/standard/python3/quickstart) depending on whether you would like to deploy your Panel app to Cloud Run or App Engine. \n", "\n", "Next, you will need three files:\n", "1. app.py: This is the Python file that creates the Panel App.\n", "\n", "2. requirements.txt: This file lists all the package dependencies of our Panel app. Here is an example for requirements.txt:\n", "\n", "```\n", "panel\n", "bokeh\n", "hvplot\n", "```\n", "3. app.yml (for App Engine) or Dockerfile (for Cloud Run)\n", "\n", "Here is an example for app.yml (if you would like to deploy to App Engine):\n", "```\n", "runtime: python\n", "env: flex\n", "entrypoint: panel serve app.py --address 0.0.0.0 --port 8080 --allow-websocket-origin=\"*\"\n", "\n", "runtime_config:\n", " python_version: 3\n", "```\n", "\n", "Here is an example for Dockerfile (if you would like to deploy to Cloud Run): \n", "```\n", "# Use the official lightweight Python image.\n", "# https://hub.docker.com/_/python\n", "FROM python:3.10-slim\n", "\n", "# Allow statements and log messages to immediately appear in the Knative logs\n", "ENV PYTHONUNBUFFERED True\n", "\n", "# Copy local code to the container image.\n", "ENV APP_HOME /app\n", "WORKDIR $APP_HOME\n", "COPY . ./\n", "\n", "# Install production dependencies.\n", "RUN pip install --no-cache-dir -r requirements.txt\n", "\n", "# Run the web service on container startup. \n", "CMD panel serve app.py --address 0.0.0.0 --port 8080 --allow-websocket-origin=\"*\" \n", "```\n", "\n", "Finally, to deploy a Panel app to App Engine run `gcloud app create` and `gcloud app deploy`. To deploy a Panel app to Cloud Run, run `gcloud run deploy`.\n", "\n", "For detailed information and steps, check out this [example](https://towardsdatascience.com/deploy-a-python-visualization-panel-app-to-google-cloud-cafe558fe787?sk=98a75bd79e98cba241cc6711e6fc5be5) on how to deploy a Panel app to App Engine and this [example](https://towardsdatascience.com/deploy-a-python-visualization-panel-app-to-google-cloud-ii-416e487b44eb?sk=aac35055957ba95641a6947bbb436410) on how to deploy a Panel app to Cloud Run.\n", "\n", "#### Other Cloud Providers\n", "\n", "Panel can be used with just about any cloud provider that can launch a Python process, including Amazon Web Services (AWS) and DigitalOcean. The Panel developers will add documentation for these services as they encounter them in their own work, but we would greatly appreciate step-by-step instructions from users working on each of these systems." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }