Nginx部署

介绍

尽管Sanic可以直接在互联网上运行,但在它前面使用一个代理服务器(如Nginx)可能会很有用。这对于在同一个IP上运行多个虚拟主机,在一个Sanic应用程序旁边提供NodeJS或其他服务特别有用,它还允许高效地提供静态文件。SSL和HTTP/2也很容易在这种代理上实现。

我们将Sanic应用程序设置为仅在本地提供服务 127.0.0.1:8000 ,Nginx安装负责向域内公共互联网提供服务 example.com . 静态文件将从 /var/www/ .

代理Sanic应用程序

应用程序需要设置一个用于识别可信代理的密钥,以便能够识别真实的客户端IP和其他信息。这可以防止任何人在互联网上发送假标题来欺骗他们的IP地址和其他细节。选择任意随机字符串并在app和Nginx config中配置它。

from sanic import Sanic
from sanic.response import text

app = Sanic("proxied_example")
app.config.FORWARDED_SECRET = "YOUR SECRET"

@app.get("/")
def index(request):
    # This should display external (public) addresses:
    return text(
        f"{request.remote_addr} connected to {request.url_for('index')}\n"
        f"Forwarded: {request.forwarded}\n"
    )

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, workers=8, access_log=False)

由于这将是一个系统服务,请将代码保存到 /srv/sanicexample/sanicexample.py .

要进行测试,请在终端中运行应用程序。

Nginx配置

允许快速透明代理需要相当多的配置,但在大多数情况下,这些都不需要修改,所以请容忍我。

上游服务器需要单独配置 upstream 块来启用HTTP keep alive,这可以极大地提高性能,因此我们使用它而不是直接在中提供上游地址 proxy_pass 指令。在本例中,上游段命名为 server_name ,即公共域名,然后在 Host 标题。您可以根据需要更改命名。还可以为负载平衡和故障转移提供多个服务器。

更改 example.com 你的真实域名,而不是 YOUR SECRET 使用你为你的应用选择的秘密。

upstream example.com {
  keepalive 100;
  server 127.0.0.1:8000;
  #server unix:/tmp/sanic.sock;
}

server {
  server_name example.com;
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;
  # Serve static files if found, otherwise proxy to Sanic
  location / {
    root /var/www;
    try_files $uri @sanic;
  }
  location @sanic {
    proxy_pass http://$server_name;
    # Allow fast streaming HTTP/1.1 pipes (keep-alive, unbuffered)
    proxy_http_version 1.1;
    proxy_request_buffering off;
    proxy_buffering off;
    # Proxy forwarding (password configured in app.config.FORWARDED_SECRET)
    proxy_set_header forwarded "$proxy_forwarded;secret=\"YOUR SECRET\"";
    # Allow websockets
    proxy_set_header connection "upgrade";
    proxy_set_header upgrade $http_upgrade;
  }
}

为了避免cookie可见性问题和搜索引擎上地址不一致,最好将所有访问者重定向到一个真正的域,始终使用HTTPS:

# Redirect all HTTP to HTTPS with no-WWW
server {
  listen 80 default_server;
  listen [::]:80 default_server;
  server_name ~^(?:www\.)?(.*)$;
  return 301 https://$1$request_uri;
}

# Redirect WWW to no-WWW
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name ~^www\.(.*)$;
  return 301 $scheme://$1$request_uri;
}

上面的节可以放在配置中 /etc/nginx/sites-available/default 或在其他站点配置中(确保将它们符号链接到 sites-enabled 如果您创建新的)。

确保在主配置中配置了SSL证书,或者添加 ssl_certificatessl_certificate_key 对每个人的指令 server 侦听SSL的部分。

另外,将这些复制粘贴到 nginx/conf.d/forwarded.conf

# RFC 7239 Forwarded header for Nginx proxy_pass

# Add within your server or location block:
#    proxy_set_header forwarded "$proxy_forwarded;secret=\"YOUR SECRET\"";

# Configure your upstream web server to identify this proxy by that password
# because otherwise anyone on the Internet could spoof these headers and fake
# their real IP address and other information to your service.


# Provide the full proxy chain in $proxy_forwarded
map $proxy_add_forwarded $proxy_forwarded {
  default "$proxy_add_forwarded;by=\"_$hostname\";proto=$scheme;host=\"$http_host\";path=\"$request_uri\"";
}

# The following mappings are based on
# https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/

map $remote_addr $proxy_forwarded_elem {
  # IPv4 addresses can be sent as-is
  ~^[0-9.]+$          "for=$remote_addr";

  # IPv6 addresses need to be bracketed and quoted
  ~^[0-9A-Fa-f:.]+$   "for=\"[$remote_addr]\"";

  # Unix domain socket names cannot be represented in RFC 7239 syntax
  default             "for=unknown";
}

map $http_forwarded $proxy_add_forwarded {
  # If the incoming Forwarded header is syntactically valid, append to it
  "~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";

  # Otherwise, replace it
  default "$proxy_forwarded_elem";
}

对于不使用 conf.dsites-available ,所有上述配置也可以放在 http 主截面 nginx.conf .

更改后重新加载Nginx配置:

sudo nginx -s reload

现在你应该可以在上连接你的应用了 https://example.com/ . 任何404错误都将由Sanic的错误页处理,并且每当静态文件出现在给定的路径上时,它将由Nginx提供服务。

SSL证书

如果您还没有在服务器上配置有效的证书,现在就可以这样做了。安装 certbotpython3-certbot-nginx ,然后运行

certbot --nginx -d example.com -d www.example.com

https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/

作为服务运行

这一部分针对的是基于 systemd . 创建单位文件 /etc/systemd/system/sanicexample.service ::

[Unit]
Description=Sanic Example

[Service]
User=nobody
WorkingDirectory=/srv/sanicexample
ExecStart=/usr/bin/env python3 sanicexample.py
Restart=always

[Install]
WantedBy=multi-user.target

然后重新加载服务文件,启动服务并在引导时启用它:

sudo systemctl daemon-reload
sudo systemctl start sanicexample
sudo systemctl enable sanicexample