十分钟上线-函数计算玩转 WordPress

本文涉及的产品
简介: 众所周知,PHP 是 Web 编程最流行的编程语言,如果有人告诉你,有 Serverless 的 PHP WEB 开发新模式,你是不是会感到好奇和兴奋?本文以部署 WordPress 工程在函数计算环境中为例,向您讲解如何使用阿里云函数计算快速构建或移植基于 PHP 框架开发的 Web, 体验 serverless 开发web 的新姿势。

这个文章介绍方案过时, 直接参考自定义镜像方案

函数计算目前已经支持了自定义镜像, 感兴趣的同学直接使用镜像体验更流畅,使用 custom-container, 和传统的 php 使用方法一致, 通过 S 工具可以一键部署

前言

这篇文章适合所有的PHP开发新手、老鸟以及想准备学习开发 PHP 的程序猿。众所周知,PHP 是 Web 编程最流行的编程语言,如果有人告诉你,有 Serverless 的 PHP WEB 开发新模式,你是不是会感到好奇和兴奋?在介绍 Serverless Web 开发新模式之前,我们先了解下将 PHP Web Serverless 化的好处:

  1. 无需采购和管理服务器等基础设施
  2. 弹性伸缩,动态扩容
  3. 免运维, 极大降低人力成本
  4. 按需付费,财务成本低

本文以部署 WordPress 工程在函数计算环境中为例,向您讲解如何使用阿里云函数计算快速构建或移植基于 PHP 框架开发的 Web ,通过本文,您将会了解以下内容:

案例概览

在本教程中,我们讲解如何利用函数计算一步一步来构建 Web 的 Server 端,该案例是把一个 WordPress 部署到函数计算,本文旨在展示函数计算做 Web Backend 能力,具体表现为以下几点:

  • 完善的 PHP 系统迁移到 FC 的成本不高
  • FC 打通了专有网络 VPC 功能,用户的函数可以配置访问专有网络的云资源,比如本案例中 MYSQL, NAS

传统服务器架构 VS Serverless架构

正常来说,用户开发 Server 端服务,常常面临开发效率,运维成本高,机器资源弹性伸缩等痛点,而使用 Serverless 架构可以很好的解决上述问题。下面是传统架构和 Serverless 架构的对比:
image

阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。

Serverless 架构详解

image.png

从上面的示例图中,整体架构十分简单明了, 用 FC 替代了 Web 服务器,但是换来的是免运维,弹性扩容,按需付费等一系列优点

函数计算运行 PHP 框架原理

传统服务器 PHP 运行原理

  • 原理示意图

image.png

  • A simple nginx conf

image.png

从上面原理示意图我们可以看出,Web 服务器根据conf 中 location将 PHP 脚本交给 php-fpm 去解析,然后将解析后的结果返回给 client 端

FC 驱动 PHP 工程原理

image.png

  • 函数计算的执行环境相当于传统 web 服务的 Apache/Nginx
  • 用户函数相当于实现 Apache/Nginx 的 conf 中 location
  • 用户将 Web 网站部署在 NAS,然后挂载 NAS 到函数的执行环境, 比如下面代码中 /mnt/www 目录
  • 对于 WordPress 入口函数代码就是这么简单, 建议您先了解下 PHP Runtime

    • PHP 入口函数
    • PHP 执行环境

      <?php
      use RingCentral\Psr7\Response;
      
      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];
              $filename = rawurldecode($filename);
              $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 秒

案例开发配置步骤

本文编写的时候由于工具链不够完善, 后续章节的操作都是基于控制台,如果您喜欢命令行, 使用 Fun 工具可以大大提高配置部署效率。

准备工作

由于函数运行时的 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配置如下图:

image.png

2. 然后将 WordPress 工程移到上述配置的 NAS 中, www 表示 WordPress 的工程的根目录

|-- index.py
|-- www

index.py代码:

# -*- coding: utf-8 -*-
import logging  
import os

file = "/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:可以,FC Web 设置自定义域名

3. 创建入口函数 wp-func (对应上面步骤中的 /mnt/www/2016-08-15/proxy/share/wp-func ), 给函数设置 http trigger ,类型为 anonymous , 类型都选上。

  <?php
  use RingCentral\Psr7\Response;

  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];
          $filename = rawurldecode($filename);
          $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 设置自定义域名

  • 下载 WordPress, 然后将 WordPress 工程移到上述配置的 NAS 中, www 表示 WordPress 的工程的根目录
|-- index.py
|-- www

index.py代码:

# -*- coding: utf-8 -*-
import logging  
import os

file = "/mnt/www/"

def mkdir(path):
  folder = os.path.exists(path)
  if not folder:                  
    os.makedirs(path)           
    
def lsDir():
  os.system("ls -ll /mnt/www/")

def handler(event, context):
  mkdir(file)  
  os.system("cp -r /code/www/* /mnt/www/")
  print(lsDir())
  return 'ok'

基于上述代码创一个函数 move-wp-nas , 执行函数,将 WordPress 工程包移动到 NAS 的/mnt/www/ 目录。

  • 同时入口函数做下小改动,改成如下代码:

    <?php
    use RingCentral\Psr7\Response;
    
    function endsWith($haystack, $needle) {
        $length = strlen($needle);
    
        return $length === 0 ||
            (substr($haystack, -$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);
        }
    
        $proxy    = $GLOBALS['fcPhpCgiProxy'];
        $root_dir = '/mnt/www';
      
        //php script
        if (preg_match('#\.php.*#', $uri)) {
            $host   = "fcdemo.mofangdegisn.cn"; // your define domain
            $resp   = $proxy->requestPhpCgi($request, $root_dir, "index.php",
                ['HTTP_HOST' => $host, 'SERVER_NAME' => $host, 'SERVER_PORT' => '80'],
                ['debug_show_cgi_params' => false, 'readWriteTimeout' => 60000]
            );
            return $resp;
        } else {
            // static files, js, css, jpg ...
            $filename = $root_dir . explode("?", $uri)[0];
            $filename = rawurldecode($filename);
            $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);
        }
    }
  • 给函数入口配置自定义域名(操作过程请参考:绑定自定义域名示例), 具体配置假设如下:

image

注意: 绑定自定义域名之后,不用使用控制台来进行调试,就只能使用浏览器来触发函数,日志服务来进行调试。

总结

函数计算有如下优势:

  • 无需采购和管理服务器等基础设施
  • 专注业务逻辑的开发
  • 提供日志查询、性能监控、报警等功能快速排查故障
  • 以事件驱动的方式触发应用响应用户请求
  • 毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
  • 按需付费。只需为实际使用的计算资源付费,适合有明显波峰波谷的用户访问场景

除了上面所列的优势,FC 可以做为 Web Backend,只需要编写一个函数实现传统 Web 服务器中的 conf 中的逻辑,就可以将一个完整的 Web 工程迁移到 FC ,从而从传统的 Web 网站运维,监控等繁琐的事务中解放出来。

最后欢迎大家通过扫码加入我们用户群中,搭建过程中有问题或者有其他问题可以在群里提出来。

函数计算官网客户群(11721331)。
image

相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
1月前
|
关系型数据库 Serverless 分布式数据库
PolarDB PostgreSQL版Serverless功能上线公测啦,公测期间免费使用!
Serverless数据库能够使得数据库集群资源随客户业务负载动态弹性扩缩,将客户从复杂的业务资源评估和运维工作中解放出来。PolarDB PostgreSQL版 Serverless提供了CPU、内存、存储、网络资源的实时弹性能力,构建计算与存储分离架构下的 PolarDB PostgreSQL版产品新形态。
|
1月前
|
JavaScript 前端开发 Cloud Native
报名开启!2024 开源之夏丨Serverless Devs 课题已上线!
2024 年,Serverless Devs 再次加入中国科学院软件研究所开源软件供应链点亮计划支持下的系列高校开源活动——开源之夏 2024。
|
10月前
|
人工智能 弹性计算 运维
阿里云宣布 Serverless 应用引擎 SAE2.0 将公测上线,多款产品全新升级
阿里云宣布 Serverless 应用引擎 SAE2.0 将公测上线,多款产品全新升级
66112 53
|
9月前
|
弹性计算 安全 JavaScript
基于函数计算快速搭建Wordpress博客系统
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Wordpress博客。
161 0
|
10月前
|
弹性计算 人工智能 运维
阿里云宣布 Serverless 应用引擎SAE2.0 将公测上线
7月31日,阿里云智能云原生应用平台负责人丁宇宣布,Serverless 应用引擎 SAE2.0 将于8月7日公测上线,开源版将于9月30日发布。本次升级围绕极简体验、标准开放、极致弹性三大优势展开,应用冷启动全面提效,支持缩容到 0,应用成本下降 40% 以上。
阿里云宣布 Serverless 应用引擎SAE2.0 将公测上线
EMQ
|
NoSQL Serverless Linux
Serverless MQTT 服务即将正式上线、新增 2 个平台安装包
EMQX Cloud Serverless 正式版已于四月初正式上线。该版本通过多租户技术和按量计费的模式,为用户提供了极速的部署创建和有效的成本控制。
EMQ
220 0
Serverless MQTT 服务即将正式上线、新增 2 个平台安装包
|
存储 人工智能 安全
如何用serverless部署wordpress?【AI科技应用-每日问答】
如何用serverless部署wordpress?【AI科技应用-每日问答】
|
安全 关系型数据库 MySQL
WordPress上线笔记
WordPress上线笔记
WordPress上线笔记
|
Serverless
《Serverless 开发实战--十分钟上线一个 Web 应用》电子版地址
Serverless 开发实战--十分钟上线一个 Web 应用
421 0
《Serverless 开发实战--十分钟上线一个 Web 应用》电子版地址
|
域名解析 关系型数据库 MySQL
阿里云轻量服务器安装WordPress应用镜像网站博客上线图文教程
阿里云轻量应用服务器自带WordPress应用镜像,一键搭建WordPress博客网站,WordPress镜像环境基于Alibaba Cloud Linux3操作系统,PHP 8.1、MySQL 5.7和Nginx 1.22版本,阿里云百科来详细说下阿里云轻量应用服务器安装WordPress应用镜像全过程:
1171 0
阿里云轻量服务器安装WordPress应用镜像网站博客上线图文教程

热门文章

最新文章

相关产品

  • 函数计算