Deploy

When it comes time to deploy your bot to a server, we recommend using gunicorn and nginx. The following information will help you run the bot under gunicorn with nginx as its reverse proxy.

This information is adapted from the Deploying Gunicorn document, you may wish to head to it for more advanced setups.

Install required system packages

Before starting, it is important to have nginx and the appropriate Python 3 packages installed.

Ubuntu 16.04 / 18.04

sudo apt install nginx python3 python3-pip python3-virtualenv

Install Python packages in a virtualenv

Create a virtualenv for SparkBot with the required packages. This will keep system-level Python packages separate from your SparkBot packages.

It’s a good idea to create a new service account with the bare minimum permissions for Sparkbot:

sudo useradd --system --create-home sparkbot

Now, log in to the sparkbot user so we can install the virtualenv:

sudo -Hu sparkbot /bin/bash

Finally, create the virtualenv and install SparkBot into it:

python3 -m virtualenv --python=python3 /home/sparkbot/sparkbotenv
source /home/sparkbot/sparkbotenv/bin/activate
pip install git+https://github.com/universalsuperbox/SparkBot.git gunicorn
deactivate
exit

Get your run.py script

This guide assumes that your SparkBot script is called run.py and is placed at /home/sparkbot/run.py. If your script is named differently, change run in run:bot.receiver in the ExecStart entry to the script’s name (without .py). If your script is located in a different directory, change the WorkingDirectory.

Add nginx configuration

We’ll use nginx to proxy requests to the bot. You may use this configuration as a template for your reverse proxy for the bot’s webhook receiver:

/etc/nginx/conf.d/sparkbot.conf
upstream app_server {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response

  # for UNIX domain socket setups
  server unix:/run/sparkbot/socket fail_timeout=0;
}

server {
  # if no Host match, close the connection to prevent host spoofing
  listen 80 default_server;
  return 444;
}

server {
  # use 'listen 80 deferred;' for Linux
  # use 'listen 80 accept_filter=httpready;' for FreeBSD
  listen 80;
  client_max_body_size 4G;

  # set the correct host(s) for your site
  server_name example.com www.example.com;

  keepalive_timeout 5;

  # path for static files
  root /path/to/app/current/public;

  location / {
    # checks for static file, if not found proxy to app
    try_files $uri @proxy_to_app;
  }

  location @proxy_to_app {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    # we don't want nginx trying to do something clever with
    # redirects, we set the Host: header above already.
    proxy_redirect off;
    proxy_pass http://app_server;
  }
}

Remember to set the server_name property to the FQDN of your server.

It is highly recommended to use HTTPS for this reverse proxy, but setting that up is outside of the scope of this guide.

Auto-start with systemd

First, we’ll add a unit file for the Gunicorn socket. This goes at /etc/systemd/system/sparkbot.socket:

/etc/systemd/system/sparkbot.socket
[Unit]
Description=SparkBot gunicorn socket

[Socket]
ListenStream=/run/sparkbot/socket

[Install]
WantedBy=sockets.target

Next, create the file /etc/systemd/system/sparkbot.service with the following content. Once finished, save and close the file then run systemctl daemon-reload:

/etc/systemd/system/sparkbot.service
[Unit]
Description=Cisco Spark chatbot
Requires=sparkbot.socket
After=network.target

[Service]
PIDFile=/run/sparkbot/pid
RuntimeDirectory=sparkbot
Environment="SPARK_ACCESS_TOKEN="
Environment="WEBHOOK_URL="
User=sparkbot
ExecStart=/home/sparkbot/sparkbotenv/bin/gunicorn \
            --bind unix:/run/gunicorn/socket run:bot.receiver
WorkingDirectory=/home/sparkbot/
Restart=on-abort
StandardOutput=journal
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Next, run systemctl edit sparkbot.service and enter the following, changing the options in curly brackets to match your desired settings:

systemctl edit sparkbot.service
[Service]
Environment="SPARK_ACCESS_TOKEN={api_token}"
Environment="WEBHOOK_URL={url}"

The values should be the same as the ones you used when you followed the Quickstart guide.

Once that’s finished, run the following to enable the bot on startup:

sudo systemctl daemon-reload
sudo systemctl enable sparkbot.socket
sudo systemctl enable sparkbot.service
sudo systemctl start sparkbot.socket
sudo systemctl start sparkbot.service