通过Nginx,Gunicorn和flask的具有HTTPS的Bokeh服务器

时间:2018-06-29 18:37:18

标签: nginx flask https bokeh gunicorn

使用https时,我无法让bokeh服务器呈现可视化效果。我有一个类似的堆栈,其中bokeh服务器按预期与http一起工作。另外,我可以为静态bokeh.html文件获得一个https连接。但是,将脚本标记传递到我的template.html不适用于https。

我在运行flask应用程序的gunicorn前面使用nginx作为反向代理。 flask应用程序从特定端口上正在运行的bokeh服务器中提取会话。主管人员监视bokeh服务器进程和Gunicorn / flask进程。

我在以下位置浏览了现有的bokeh文档:

https://bokeh.pydata.org/en/latest/docs/user_guide/server.html#reverse-proxying-with-nginx-and-ssl

我还在SO上浏览了一些类似的问题:

How to configure Nginx with gunicorn and bokeh serve

Bokeh Serve HTTPS instead of HTTP

How to enable SSL/HTTPS on bokeh 0.12.5?

Making script content 'safe' for HTTPS display (Bokeh)

在烧瓶应用程序中,我尝试了组合选项,分别用于flask_app中的relative_urls = True / False和--allow-websocket-origin =“ myIP”/["*"]。

在这一点上,我认为我只是缺少明显的东西,我希望其他人也会看到它。我认为关键是,在提供静态bokeh.html文件或其他内容时,https可以在堆栈中上下移动,但是在尝试嵌入bokeh服务器时会失败。我怀疑我缺少有关如何通过烧瓶进行会话的东西。

谢谢!

以下是我认为的配置和flask.app文件的所有相关部分:

/ etc / nginx / sites-available / flask_settings的内容

upstream gunicorn_flask {
    server 127.0.0.1:6000 fail_timeout=0;
}

server {
    listen 443 ssl http2 default_server;
    server_name ###.###.###.###;
    include snippets/self-signed.conf;
    include snippets/ssl-params.conf;
    access_log /var/log/nginx/https_access.log;
    error_log /var/log/nginx/https_error.log;
    ssl on;
    location / {
                proxy_pass http://gunicorn_flask;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_http_version 1.1;
                proxy_set_header X-Forwarded_Proto $scheme;
                proxy_set_header X-Forwarded_For $proxy_add_x_forwarded_for;
                proxy_buffering off;
#                proxy_set_header X-Real-IP $remote_addr;
#                proxy_set_header Host $host:$server_port;

               }
}

server {
    listen 80;
    server_name ###.###.###.###;
    return 301 https://$server_name$request_uri;
}

/etc/nginx/snippets/self-signed.conf的内容

ssl_certificate /path/to/my/file.crt;
ssl_certificate_key /path/to/my/file.key;

/etc/nginx/snippets/ssl-params.conf的内容

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000";
ssl_dhparam /etc/ssl/certs/dhparam.pem;

/etc/supervisor/conf.d/bokeh.conf的内容

[program:my_bokeh_viz]
directory=/my/path/to/bokeh/viz
command=/my/path/to/bokeh/bin/bokeh serve --allow-websocket-origin=###.###.###.### --address=127.0.0.1 --port 6001 --use-xheaders /my/path/to/bokeh/viz.py
autostart=true
autorestart=true
stderr_logfile=/var/log/my_bokeh_viz/Bokeh_Flask.err.log
stdout_logfile=/var/log/my_bokeh_viz/Bokeh_Flask.out.log

/etc/supervisor/conf.d/gunicorn.conf的内容

[program:gunicorn_bokeh]
directory=/my/path/to/flask/app
command=/my/path/to/anaconda3/bin/gunicorn my_flask_app:app --bind 127.0.0.1:6000 --pythonpath /my/path/to/flask/app
autostart=true
autorestart=true
stderr_logfile=/var/log/gunicorn_bokeh/Bokeh_Flask.err.log
stdout_logfile=/var/log/gunicorn_bokeh/Bokeh_Flask.out.log

/path/to/my/flask/app.py的内容

--- Relevant section of flask app.py
@app.route('/web/path/to/bokeh/viz', methods = ['GET'])
@login_required
def show_bokeh_viz():

  host_url = 'http://127.0.0.1:6001/my_bokeh_viz'
  my_session = pull_session(url = host_url)
  client_url = 'http://###.###.###.###/web/path/to/bokeh/viz'
  my_script = server_session(model = None,
                             session_id = my_session.id,
                             url = client_url,
                             relative_urls = False)
  return render_template("embed.html", script = my_script, template = "Flask")

@app.route('/testing', methods = ['GET'])
@login_required
def show_test():
  return render_template("stocks.html")

内容:/path/to/my/flask/app/templates/embed.html

<!doctype html>
<html>
  <body>
    {{ script|safe }}
  </body>
</html>

呈现结果的页面来源:

<!doctype html>
<html>
  <body>

<script
    src="https://###.###.###.###/web/path/to/my/bokeh/viz/autoload.js?bokeh-autoload-element=a4c3a28c-8bab-4e54-8ba8-8423914078aa&bokeh-app-path=/web/path/to/my/bokeh/viz&bokeh-absolute-url=https://###.###.###.###/web/path/to/my/bokeh/viz&bokeh-session-id=2GYRA1MPiBBX0qzlIB5ZEMyWQkV9AD1Br1KdgtZoew5P"
id="a4c3a28c-8bab-4e54-8ba8-8423914078aa"
    data-bokeh-model-id=""
    data-bokeh-doc-id=""
></script>
  </body>
</html> 

1 个答案:

答案 0 :(得分:0)

我认为有几点。通过创建bokeh会话,您实际上只创建了浏览器调用的脚本来执行bokeh背景。在浏览器中,它将尝试调用您提供的任何内容,而我们需要它调用https://example.com/ .....,以便nginx将bokeh流量发送到bokeh服务器而不是gunicorn服务器。

我发现的最佳解决方案是在所有bokeh server_session / server_document调用前加上一些前缀,以便nginx可以将它们发送到bokeh服务器而不是flask,然后用nginx去除前缀。我尝试使用bokeh serve --prefix标记,但是在加载静态资产时遇到了一些问题。

@app.route('/web/path/to/bokeh/viz', methods = ['GET'])
@login_required
def show_bokeh_viz():
  my_session = pull_session(url = http://localhost:6001/my_bokeh_viz)

  # use actual url here
  host_url = 'https://example.com/bokeh_stuff/my_bokeh_viz'
  my_script = server_session(model = None,
                             session_id = my_session.id,
                             url = url,
                             relative_urls = False)
  return render_template("embed.html", script = my_script, template = "Flask")

有关nginx的位:

# All bokeh plots are prefixed with some unique route, in this case 'bokeh_stuff'
# Here we will collect these, strip off the prefix, and send the result to the bokeh server
# Sends https://example.com/bokeh_stuff/sliders ⇒ http://localhost:6001/sliders
location /bokeh_stuff  {
    rewrite  ^/bokeh_stuff/(.*)  /$1 break;
    proxy_pass http://127.0.0.1:6001;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host:$server_port;
    proxy_buffering off;
}

# All traffic other than static and bokeh_stuff goes to the gunicorn server
location / {
    # forward application requests to the gunicorn server
    proxy_pass http://localhost:6000;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

然后在bokeh服务中,您需要添加--allow-websocket-origin=example.com

要测试正在发生的情况,请查看控制台和网络请求以及bokeh服务器的输出(主管日志),并查看它能为您提供什么。您也可以转到example.com/bokeh_stuff,即使您的路由功能仍然存在问题,也应该获取索引页。