0. 需求由来
为了在 DSM 7.2 版本下的群晖 NAS 里运行我基于 flask 3.0.2 编写的网页应用程序,我上网查了非常多资料,也踩了很多坑。最主要的就是 7.2 版本的界面与旧版略有不同,而网络上的资料大多基于旧版界面,且大部分仅仅说明了 How to do,而没有解释 Why to do。因此本人随手记录一下。
1. 新建虚拟环境
最开始的一步,自然是建设虚拟环境,用以支撑 Flask 的项目运行:
- 从自己的 Flask 项目中导出所需的软件包:
pip freeze > requirements.txt
- 在 DSM 的管理界面下:web station ➡️ 脚本语言设置 ➡️ python ➡️ 新增 ➡️ 名称设为 flask_venv ➡️ uWSGI 可保持默认 ➡️ 模块页面,点击上传 requirements.txt
本步骤要点:
- DSM 7.2 里的 web station,其脚本语言设置,其实就是搭建一个可以复用的虚拟环境。
- 无需自行远程登录 DSM 去手动安装 pip。自 python 3.4 版本以来,pip 已经被内置到官方版本内部了,仅需一个
python -m ensurepip --upgrade
命令,完整安装的 python 即可离线安装好 pip。因此,我们在 DSM 上面的配置过程,全程无需后台登录操作,仅需在 web 页面下足以。
2. 上传代码
上传代码到 DSM 有很多种方式,最简单的是通过 File Station 的 web 界面,将项目代码拖进去,比如 web 下的一个子目录。也可以通过开启 DSM 的 ftp、sftp 等协议进行上传
关于 flask 的代码有一个注意点,即若 flask 的实例是采用工厂模式创建的,则必须添加一个独立的文件,名称随意(一般叫 wsgi.py),内容如下:
from hello import create_app
app = create_app()
本步骤要点:
- 需要对 wsgi 协议有一定了解,知道 wsgi 的程序跑起来需要哪些设置,特别是工厂模式下的 flask 如何支持 wsgi 需要了解。
- DSM 下的 sftp 服务,其根目录就是 File Station 见到的根目录,可以依据此设置自动脚本,实现文件的自动上传,以便后期部署过程中的不断修正。为确保安全,建议使用公钥体系,避免使用密码,不赘述。
3. 设置网页服务
设置网页服务: web station ➡️ 网页服务 ➡️ 新增 ➡️ 本机脚本语言网站,服务项选择 python3.9 以及 flask_venv ➡️ 文档根目录指向第2步上传的代码所在目录,指定 wsgi 文件及 Callable
本步骤要点:
- wsgi 文件:即 flask 的主程序所在的 python 文件,一般就是初始化了 Flask 对象的那个。若是工厂模式,则指定前述新增的那个 wsgi.py 文件
- Callable:即 wsgi 文件中,代表被初始化的 Flask 对象的那个变量,一般都是 app
- 需要理解,DSM 语境下的网页服务,实际意思就是基于虚拟环境与代码实现的一套网页应用。到了这一步,代码其实已经运行起来了,只是尚未对外提供服务而已(详见
/usr/local/etc/nginx/sites-enabled
)。DSM 使用 uwsgi 支持 python,并通过 nginx 反向代理,对外提供服务。
4. 对外提供服务
现在到了最后一步,我们需要设置网络门户,以便我们可以面向互联网提供我们前面配置好的网页服务:web station ➡️ 网络门户 ➡️ 网页服务门户
- 服务:选择我们刚新增的那个 flask 网页服务
- 门户类型:有三个选择,分别是基于端口、基于名称,和别名门户。
- 若是只有一个域名,一般选择基于端口,然后选择一个不冲突的端口即可。
- 若拥有额外的域名,则此时可以选择基于名称并复用 80/443 端口,且要确保该域名设置了正确的解析,指向 NAS 的公网地址。
- 至于别名门户,就是在 URL 末端用一个子目录,指向你的网站。比如我们如果设置了名称为 myapp 的别名,则访问地址可能为
yourname.synology.me/myapp
(前面的 yourname.synology.me 依据你注册使用的地址不同而略有不同)。
如果要使用这一项,则必须确保你的 flask 项目能够部署在子目录,比如我通过利用 functools.wraps 强制重写 flask.url_for 函数,实现硬编码的 url 子目录前缀,最终实现 flask 项目部署的子目录化。(关于如何强制重写 flask.url_for 函数,另文描述)
我个人倾向于使用别名门户,看起来正经,而且无需额外的域名,使用群晖自带的服务即可。
若是出现 Internal Server Error
的错误提示,则一般都是你的 flask 代码有问题,在本地测试修改,并确认第1步建立的虚拟环境内的各模块正确,一般都可以解决了。
至此,flask 应用已经正常跑起来了。
参考
- flask 官方文档: https://flask.palletsprojects.com/en/3.0.x/
- ensurepip 的官方文档: https://docs.python.org/zh-cn/dev/library/ensurepip.html
- 群晖nas中使用python的Flask框架搭建网站(用Web Station): https://www.cnblogs.com/yaoyue68/p/17587070.html
- 群晖NAS中Web Station使用Flask作为后端服务教程: https://www.bilibili.com/read/cv17483317/