Hyac 开发一:应用容器私有依赖安装的实现
写在前面
对于一个Faas框架来说,每一个应用容器都是独立的开发环境,在独立的开发环境中,实现特定的依赖安装是非常必要的。
实现思路
Hyac的运行逻辑是 server 管理多个app runtime,每一个 runtime 都在单独的 docker 容器中,在每个容器启动之后,在依赖安装部分,需要执行两个操作,首先安装公共依赖(后续为了启动速度的优化,可能会将系统依赖打包至image内),其次需要安装用户所需的依赖。
一般的python项目安装依赖方式
普通的情况下,在开发过程中,动态的添加、删除依赖,发布之前可以通过pip生成一份requirements.txt (pip freeze > requirements.txt
)。当其它环境下运行此项目时可以通过 pip install -r requirements.txt
。但Hyac的运行环境中除系统依赖外,随着用户的开发过程,所使用的依赖也一定是变化的。因此这个问题就变成了如何 实现可变依赖的安装逻辑。
问题拆解
当我们分析这个问题时,可以简单的从中提取一些关键信息,如下
- 依赖数据存储在哪、以什么格式存储
- 程序启动时如何初始化安装项目依赖
- 项目依赖如何动态的添加与删除
围绕以上三个问题,逐一解决,那么Hyac项目中的依赖管理部分就可以完成了,接下来就是具体的实现方案。
实现步骤
依赖数据的存储与格式
首先需要考虑继续使用 requirements.txt 文件是否合适,如果继续使用会有以下几个问题:
- requirements.txt 文件的读写效率不高
- 当一个项目多个人操作的时候,对单个文件的读写操作会发生冲突
- 实现方式不优雅
综合以上问题,最终决定使用mongodb提供依赖数据的存储,接下来需要设计依赖数据的安装结构,因此将依赖信息放置在应用(Application)文档上,结构如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"app_id": "hyac_demo",
...
"dependencies":[
{
"name": "requests",
"version": "0.0.1"
},
{
"name": "pillow",
"version": "0.0.1"
}
],
...
}
应用启动时依赖的初始化
因为需要将依赖安装到当前的运行环境中,首先查询数据库获取当前运行环境的依赖信息,之后生成安装命令 install_command = uv pip install [package name] == [ package version ] -- system
,接下来是实现动态安装功能的核心:
process = await asyncio.create_subprocess_exec(
*install_command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
之后我们可以通过获取这个管道的返回码与输出,来判断某个依赖是否安装成功。
如何动态的添加与删除
实际开发中,这部分的实现时最简单的,首先 https://pypi.org/simple/
会返回当前所有 python 官方的依赖,我们可以获取到所有的依赖名称,之后通过对比来判断用户输入的依赖是否合法。如果用户输入的依赖是合法的,那么继续下一步选择某一个依赖的版本,选择版本时可以访问 https://pypi.org/pypi/[package name]/json
接口,这个接口可以返回我们需要的信息,包括名称、描述、版本等。
当用户选择完毕之后,我们将用户选择的依赖添加入数据库即可。