前言
首先介绍下在本文出现的几个比较重要的概念:
函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考。
Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考。
备注: 本文介绍的技巧需要 Fun 版本大于等于 3.0.0+。
Funfile 是什么
就如同 Dockerfile 用于构造 docker 镜像,Funfile 可以用于构造函数计算的交付产物。
Funfile 中包含了一系列的命令,这些命令相当于构造函数计算交付产物一个个“步骤”,Fun 会忠实地执行这些步骤,最终产出“交付产物”。
初始化 Funfile 文件
Funfile 文件可以通过执行 fun install init 完成初始化创建。执行 fun install init,会让选择函数运行时:
$fun install init
? Select a runtime
nodejs6
nodejs8
nodejs10
python2.7
python3
java8
php7.2
(Move up and down to reveal more choices)
比如,这里选择 nodejs10 后,会在当前目录生成一个名为 Funfile 的文件,内容为:
RUNTIME nodejs10
由于 Funfile 就是一个文本文件,不使用 fun 进行初始化,直接创建一个名为 Funfile 的文件,然后填写相应的内容也是可以的。
使用 Funfile 进行构建
无论是使用 fun install、还是 fun build,当操作的函数目录顶级存在 Funfile 时,都会自动执行 Funfile 的构建。
Funfile 的使用体验
举个简单场景的例子,比如要安装一个 pip 的 flask 库。除了我们在《开发函数计算的正确姿势 —— 依赖安装小结》中介绍的一些方法外,也可以通过 Funfile 进行安装。
首先第一行通过 RUNTIME 指定要安装的依赖所服务的函数 runtime,可以是 python3、python2.7、nodejs8 等等。
然后,后续的命令直接编写相关命令即可,这里只需要对 apt-get 与 pip 的依赖特殊注意下,需要在这两个命令前加上 fun-install 前缀。比如,原来安装 flask 的命令为 pip install flask,现在只需要通过 fun-install pip install flask 即可。
Funfile 内容如下:
RUNTIME python3
RUN fun-install pip install flask
执行 fun install,可以看到输出:
$ fun install
using template: template.yml
start installing function dependencies without docker
building pyzbar-srv/pyzbar-fun
Funfile exist, Fun will use container to build forcely
Step 1/2 : FROM aliyunfc/runtime-python3.6:build-1.6.1
---> 2110e9fa9393
Step 2/2 : RUN fun-install pip install flask
---> Using cache
---> 992b0d65136d
sha256:992b0d65136dfa63b9566b8280881bb4cf22c4f1b68155713545e4b61ada8dd9
Successfully built 992b0d65136d
Successfully tagged fun-cache-e95a9bfa-a6fa-4d70-b5b4-0084613e2b8d:latest
copying function artifact to /Users/tan/code/fun/examples/install/pyzbar_example
could not find any manifest file for pyzbar-srv/pyzbar-fun, [install] stage for manifest will be skipped
Tips for next step
======================
* Invoke Event Function: fun local invoke
* Invoke Http Function: fun local start
* Build Http Function: fun build
* Deploy Resources: fun deploy
fun install 安装的依赖,会被安装到代码目录,这时,可以通过 fun local 相关命令进行本地调试,也可以通过 fun deploy 一键部署到云端。
Funfile 支持的语法
Funfile 的能力与 Dockerfile 是等价的,Dockerfile 中支持的一些命令例如 COPY、RUN、ENV、USER、WORKDIR 等,Funfile 也都是支持的。比如,这里我们可以将函数目录的某个文件复制到容器执行一些操作:
RUNTIME nodejs8
COPY ./package.json .
RUN npm install
我们也可以拿更复杂的语法举例,比如在函数计算中安装 ralng 的例子,Funfile 内容如下:
RUNTIME python3
RUN fun-install apt-get install libblas3
RUN fun-install pip install pandas
RUN apt-get build-dep -y r-base; \
curl -L https://fc-demo-public.oss-cn-hangzhou.aliyuncs.com/fun/examples/R-3.5.2.tar.gz | tar -zxf -; \
cd R-3.5.2 ; \
./configure --prefix=/code/.fun/R/ --enable-R-shlib --with-blas --with-lapack ; \
make ; \
make install;
RUN PATH=/code/.fun/R/bin:$PATH LD_LIBRARY_PATH=/code/.fun/root/usr/lib/libblas:$LD_LIBRARY_PATH fun-install pip install rpy2
RUN rm -rf R-3.5.2; \
rm -rf .fun/root/usr/share; \
rm -rf .fun/R/share; \
rm -rf .fun/R/lib/R/doc; \
rm -rf .fun/python/lib/python3.6/site-packages/pandas/tests; \
rm -rf .fun/R/lib/R/library/*/{help,demo,html,doc}; \
find .fun -type f -name "*.so" -exec strip --strip-all {} \;
相比较于原生的 Dockerfile 命令,只需要将原来的 apt-get 以及 pip 安装的包前面加个 fun-install 的前缀。
Funfile 的兼容性
(未使用过 fun.yml 的用户可以忽略该内容。)
实际上,我们在 2.0+ 的版本,已经推出过类似的功能,即 fun.yml。Funfile 其实可以看作是 fun.yml 功能的升级,因为 fun.yml 支持的语法,Funfile 也都是支持的,而 Funfile 在原来的基础上,又新增了很多特性。3.0 版本,我们会统一从 fun.yml 迁移到 Funfile。
原先使用 fun.yml 的小伙伴,也不用担心,使用 fun install 功能时,如果我们检测到旧版本的 fun.yml 文件,我们会自动将 fun.yml 转换成等价的 Funfile。如果用户需要增加新的依赖,只需要修改 Funfile 的内容即可。
使用 Funfile 的优势
- 通过 Funfile 描述详细的依赖安装步骤,其实更有助于“标准化”开发流程。
- 通过将 Funfile 而不是将具体的某些依赖提交到版本控制系统中,也更有利于函数代码的维护。
- Funfile 的编写,更接近原生体验,结合 fun install sbox,可以先在交互式环境进行各种尝试,然后将最终安装步骤编写到 Funfile,这个类似与 Dockerfile 的编写。
- Funfile 也支持 Dockerfile 的 cache 功能,如果项目的改动对 Funfile 不影响,那么 Funfile 可以利用 cache 直接跳过相应的命令执行过程。这有助于加速 Funfile 的执行过程,提升开发效率。
下面是下载 rlang 依赖的例子,第一次执行编译,需要超过 30 分钟+ 才能编译好,之后再此执行,只要不修改 Funfile 的内容,都可以在 5s 内执行完成: