十分鐘上線-函式計算玩轉 WordPress
摘要: 眾所周知,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
案例體驗入口:
傳統伺服器架構 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 執行環境
<?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 )。
- 建立 RDS MYSQL 資料庫, 配置 VPC , 具體參考通過 VPC 訪問 RDS 例項
- 建立 NAS 掛接點,配置 VPC (注意:這裡跟 RDS 採用相同的 VPC), 具體參考函式計算nas使用示例
- 可選操作,在準備函式的 region 建立日誌,用於函式的除錯, 具體參考函式計算配置日誌服務
建立函式
- 建立 Service (假設是 share ), 配置準備 vpc config , nas config 和日誌服務,比如案例體驗的Service配置如下圖:
- 下載 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:可以,等函式計算自定義域名功能上線,可以解決這個問題,具體操作後續會在此文中更新。
- 建立入口函式 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);
}
}
- 直接通過 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