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

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

前言

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

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

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

  • 案例概览
  • 传统服务器架构 VS Serverless架构
  • Serverless架构详解
  • 函数计算运行PHP框架原理
  • 案例开发配置步骤
  • FC Web 设置自定义域名

案例概览

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

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

案例体验入口:web

传统服务器架构 VS Serverless架构

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

clipboard.png

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

Serverless 架构详解

clipboard.png

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

函数计算运行 PHP 框架原理

传统服务器 PHP 运行原理服务器

  • 原理示意图

clipboard.png

  • A simple nginx conf

clipboard.png

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

FC 驱动 PHP 工程原理架构

clipboard.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];
        $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配置以下图:

clipboard.png

  1. 下载 WordPress, 而后将 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:能够,等函数计算自定义域名功能上线,能够解决这个问题,具体操做后续会在此文中更新。

  1. 建立入口函数 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];
          $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);
      }
  }
  1. 直接经过 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

阅读原文

本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索