摘要: 众所周知,php 是 web 编程最流行的编程语言,如果有人告诉你,有 serverless 的 php web 开发新模式,你是不是会感到好奇和兴奋?本文以部署 wordpress 工程在函数计算环境中为例,向您讲解如何使用阿里云函数计算快速构建或移植基于 php 框架开发的 web, 体验 serverless 开发web 的新姿势。
前言
这篇文章适合所有的php开发新手、老鸟以及想准备学习开发 php 的程序猿。众所周知,php 是 web 编程最流行的编程语言,如果有人告诉你,有 serverless 的 php web 开发新模式,你是不是会感到好奇和兴奋?在介绍 serverless web 开发新模式之前,我们先了解下将 php web serverless 化的好处:
- 无需采购和管理服务器等基础设施
- 弹性伸缩,动态扩容
- 免运维, 极大降低人力成本
- 按需付费,财务成本低
本文以部署 wordpress 工程在函数计算环境中为例,向您讲解如何使用阿里云函数计算快速构建或移植基于 php 框架开发的 web ,通过本文,您将会了解以下内容:
- 案例概览
- 传统服务器架构 vs serverless架构
- serverless架构详解
- 函数计算运行php框架原理
- 案例开发配置步骤
- fc web 设置自定义域名
案例概览
在本教程中,我们讲解如何利用函数计算一步一步来构建 web 的 server 端,该案例是把一个 wordpress 部署到函数计算,本文旨在展示函数计算做 web backend 能力,具体表现为以下几点:
- 完善的 php 系统迁移到 fc 的成本不高
- fc 打通了专有网络 vpc 功能,用户的函数可以配置访问专有网络的云资源,比如本案例中 mysql, nas
案例体验入口:
- 体验地址: http://1986114430573743.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/share/wp-func/
- 账号:wp-test
- 密码:wp-pwd
传统服务器架构 vs serverless架构
正常来说,用户开发 server 端服务,常常面临开发效率,运维成本高,机器资源弹性伸缩等痛点,而使用 serverless 架构可以很好的解决上述问题。下面是传统架构和 serverless 架构的对比:
阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。
serverless 架构详解
从上面的示例图中,整体架构十分简单明了, 用 fc 替代了 web 服务器,但是换来的是免运维,弹性扩容,按需付费等一系列优点
函数计算运行 php 框架原理
传统服务器 php 运行原理
- 原理示意图
- a simple nginx conf
从上面原理示意图我们可以看出,web 服务器根据conf 中 location将 php 脚本交给 php-fpm 去解析,然后将解析后的结果返回给 client 端
fc 驱动 php 工程原理
- 函数计算的执行环境相当于传统 web 服务的 apache/nginx
- 用户函数相当于实现 apache/nginx 的 conf 中 location
- 用户将 web 网站部署在 nas,然后挂载 nas 到函数的执行环境, 比如下面代码中 /mnt/www 目录
- 对于 wordpress 入口函数代码就是这么简单, 建议您先了解下 php runtime
- php 入口函数
- php 执行环境
<?phpuse ringcentralpsr7
esponse;function startswith($haystack, $needle) { $length = strlen($needle); return (substr($haystack, 0, $length) === $needle);}function handler($request, $context): response{ $uri = $request->getattribute("requesturi"); $uriarr = explode("?", $uri); // default php / or /wp-admin/ if (preg_match('#/$#', $uriarr[0]) && !(strpos($uri, '.php'))) { $uriarr[0] .= "index.php"; $uri = implode($uriarr); if (startswith($uri, "/2016-08-15/proxy/share/wp-func/wp-admin/")) { // wordpress admin entrypoint $request = $request->withattribute("requesturi", $uri); } } $proxy = $globals['fcphpcgiproxy']; $root_dir = '/mnt/www'; //php script if (preg_match('#.php.*#', $uri)) { $format = '%s.%s.fc.aliyuncs.com'; $host = sprintf($format, $context['accountid'], $context['region']); // maybe user define domain $resp = $proxy->requestphpcgi($request, $root_dir, "index.php", ['server_name' => $host, 'server_port' => '80', 'http_host' => $host], ['debug_show_cgi_params' => false, 'readwritetimeout' => 15000] ); return $resp; } else { // static files, js, css, jpg ... $filename = $root_dir . explode("?", $uri)[0]; $handle = fopen($filename, "r"); $contents = fread($handle, filesize($filename)); fclose($handle); $headers = [ 'content-type' => $proxy->getmimetype($filename), 'cache-control' => "max-age=8640000", 'accept-ranges' => 'bytes', ]; return new response(200, $headers, $contents); }}
其中函数计算为用户提供了一个 $globals['fcphpcgiproxy'] 对象用来和 php-fpm 进行交互,对
php 工程中的 php 文件进行解析,该对象提供了两个重要的接口:
- requestphpcgi
requestphpcgi($request, $docroot, $phpfile = "index.php", $fastcgiparams = [], $options = [])
- $request: 跟 php http invoke 入口的参数一致
- $docroot: web 工程的根目录
- $phpfile: 用于拼接 cgi 参数中的 script_filename 的默认参数
- $fastcgiparams: 函数计算内部尽量根据$request给您构造 default cgi params, 但是如果您不是想要的,可以使用$fastcgiparams覆盖一些参数 (reference: cgi)
- $options: array类型,可选参数, debug_show_cgi_params 设为 true ,会打印每次请求 php 解析时候的 cgi 参数, 默认为 false ;readwritetimeout 设置解析的时间, 默认为 5 秒
案例开发配置步骤
准备工作
由于函数运行时的 ip 是不固定的,您需要设置 rds 允许所有 ip 访问。但是这样会有风险,不建议这样做。在本教程中,我们将创建一个 rds mysql 数据库,并将它置于一个专有网络 vpc 环境内,函数计算支持 vpc 功能,用户可以通过授权的方式安全地访问 vpc 中的资源(同时包含本示例中的 nas )。
1. 创建 rds mysql 数据库, 配置 vpc , 具体参考通过 vpc 访问 rds 实例
2. 创建 nas 挂接点,配置 vpc (注意:这里跟 rds 采用相同的 vpc), 具体参考函数计算nas使用示例
3. 可选操作,在准备函数的 region 创建日志,用于函数的调试, 具体参考函数计算配置日志服务
创建函数
1. 创建 service (假设是 share ), 配置准备 vpc config , nas config 和日志服务,比如案例体验的service配置如下图:
2. 下载 wordpress, 然后将 wordpress 工程移到上述配置的 nas 中, www 表示 wordpress 的工程的根目录
|-- index.py|-- www
index.py代码:
# -*- coding: utf-8 -*-import logging import osfile = "/mnt/www/2016-08-15/proxy/share/wp-func"def mkdir(path): folder = os.path.exists(path) if not folder: os.makedirs(path) def lsdir(): os.system("ls -ll /mnt/www/2016-08-15/proxy/share/wp-func/")def handler(event, context): mkdir(file) os.system("cp -r /code/www/* /mnt/www/2016-08-15/proxy/share/wp-func/") print(lsdir()) return 'ok'
基于上述代码创一个函数 move-wp-nas , 执行函数,将 wordpress 工程包移动到 nas 的/mnt/www/2016-08-15/proxy/share/wp-func 目录。
- q1: 为什么创建 /2016-08-15/proxy/share/wp-func 这么奇怪的目录?
- a:因为http trigger, 函数访问的格式为下面的url: http://${account_id}.${region}.fc.aliyuncs.com/2016-08-15/proxy/$(seevice_name}/{function_name}/,为了保证从一个页面跳转到另外一个页面的时候,能自动带上/2016-08-15/proxy/$(seevice_name}/{function_name}/,我们需要建立这样目录和设置 cgi 相关参数达到 php 框架内部自动跳转正确的问题。
- q2: 可不可以不用/2016-08-15/proxy/share/wp-func这么奇怪的目录?
- a:可以,等函数计算自定义域名功能上线,可以解决这个问题,具体操作后续会在此文中更新。
3. 创建入口函数 wp-func (对应上面步骤中的 /mnt/www/2016-08-15/proxy/share/wp-func ), 给函数设置 http trigger ,类型为 anonymous , 类型都选上。
<?php use ringcentralpsr7
esponse; function startswith($haystack, $needle) { $length = strlen($needle); return (substr($haystack, 0, $length) === $needle); } function handler($request, $context): response{ $uri = $request->getattribute("requesturi"); $uriarr = explode("?", $uri); // default php / or /wp-admin/ if (preg_match('#/$#', $uriarr[0]) && !(strpos($uri, '.php'))) { $uriarr[0] .= "index.php"; $uri = implode($uriarr); if (startswith($uri, "/2016-08-15/proxy/share/wp-func/wp-admin/")) { // wordpress admin entrypoint $request = $request->withattribute("requesturi", $uri); } } $proxy = $globals['fcphpcgiproxy']; $root_dir = '/mnt/www'; //php script if (preg_match('#.php.*#', $uri)) { $format = '%s.%s.fc.aliyuncs.com'; $host = sprintf($format, $context['accountid'], $context['region']); // maybe user define domain $resp = $proxy->requestphpcgi($request, $root_dir, "index.php", ['server_name' => $host, 'server_port' => '80', 'http_host' => $host], ['debug_show_cgi_params' => false, 'readwritetimeout' => 15000] ); return $resp; } else { // static files, js, css, jpg ... $filename = $root_dir . explode("?", $uri)[0]; $handle = fopen($filename, "r"); $contents = fread($handle, filesize($filename)); fclose($handle); $headers = [ 'content-type' => $proxy->getmimetype($filename), 'cache-control' => "max-age=8640000", 'accept-ranges' => 'bytes', ]; return new response(200, $headers, $contents); } }
4. 直接通过 url 访问首页,第一次访问会提示您安装 wordpress, 安装过程中配置之前准备好的数据库、管理员等相关信息, 安装成功后,就可以成功访问首页,登录后台管理 wordpress 网站了。
http://${account_id}.${region}.fc.aliyuncs.com/2016-08-15/proxy/$(seevice_name}/{function_name}/for example:http://1986114430573743.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/share/wp-func/
fc web 设置自定义域名
未完待更新...
总结
函数计算有如下优势:
- 无需采购和管理服务器等基础设施
- 专注业务逻辑的开发
- 提供日志查询、性能监控、报警等功能快速排查故障
- 以事件驱动的方式触发应用响应用户请求
- 毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
- 按需付费。只需为实际使用的计算资源付费,适合有明显波峰波谷的用户访问场景
除了上面所列的优势,fc 可以做为 web backend,只需要编写一个函数实现传统 web 服务器中的 conf 中的逻辑,就可以将一个完整的 web 工程迁移到 fc ,从而从传统的 web 网站运维,监控等繁琐的事务中解放出来。
作者:rsong