iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
1
Modern Web

成為 Modern PHPer系列 第 13

Day 13:PHP 佈署概述

  • 分享至 

  • xImage
  •  

「PHP 應該要被淘汰,因為它難以被容器化。」--PHP 經常面對的批評。

前言

本篇文章不會詳述 PHP 應用程式的佈署方法,僅僅說明目前主流的幾種做法,以及歸納我認為良好的 Better Practice。

傳統做法

雖然說是「傳統」,但並不表示「過時」。事實上這些做法至今仍是 PHP 應用程式佈署的主流,對於小型規模的團隊而言,比起導入什麼新潮的 Container 甚至是 Kubernetes,這樣的方式反而比較適合它們。

LAP

LAP 為 Linux, Apache, PHP 之縮寫,其職責分工相當明確:在 Linux 上使用 Apache 作為網頁伺服器並提供 PHP 服務。

一般而言,若要在 Apache 上使用 PHP,需要安裝 libapache2_mod_php(這個名字來自於 Debian 的 Repository),這是一個在編譯 PHP source code 時可以選擇編譯的 Apache 模組。

這樣的架構有個優點

  • 歷史悠久,基本上能碰上的問題都能夠在 Google 上找到解答

同時也存在一些缺點

  • Apache 的細節設定非常複雜
  • PHP 官方文件不建議正式環境中在 Apached2 下使用 threded MPM 來執行 PHP

註:WAMP 只是將 Linux 換成 Windows,事實上優缺點與設定方式大同小異。

LNP

LNP 為 Linux, Nginx, PHP 之縮寫,相較於 LAMP 僅是將 Web Server 改為 Nginx。

Nginx 與 PHP 溝通依賴 FastCGI 協定,不像 apache 那樣需要另外編譯一個模組,其設定上較 Apache 簡化許多。

這樣的架構具備以下優點

  • 經歷多年考驗,各種資料相當齊全

同時也可能有些缺點

  • 與 PHP 的溝通依靠 FastCGI,其理論效能會較 Apache 來得差
    • PHP + Nginx 的 FastCGI 有兩種溝通渠道:TCP 及 Unix Socket
    • TCP 不用多說,效能上必然較差一些,不過其優點是可以在叢集中自由分配資源
    • Unix Socket 相較 Apache 直接以動態函式庫的方式仍然存在些微的效能差距,但可忽略之

註:WNMP 只是將 Linux 換成 Windows,事實上優缺點與設定方式大同小異。
註:事實上也不一定要用 Nginx,大部份主流的 Web Server 都有提供 FastCGI Interface,這邊與以下內容可以自由代換。

WIP

WIP 是 Work In Progress Windows, IIS, PHP 的縮寫,雖然這年頭用 Windows + IIS 的人實在不多見了……

IIS 與 PHP 溝通的方式與 Nginx 類似:依賴 Windows CGI,不過可以使用 Web Platform Installer 簡化環境的安裝。

題外話,到維基百科上查到 Web PI 的最後穩定版是 6 年前發佈的 5.0,不確定微軟是否有繼續更新的打算?

現代方法

我絕對不會否認,PHP 是個非常難以容器化的語言。

將 PHP 容器化時,常常要面對幾個問題

  • PHP 跟 Nginx 應該放在同一個 Image 裡嗎?
  • PHP 跟 Apache 應該放在同一個 Image 裡嗎?
  • 應該在什麼時候打包 PHP code 與 Composer dependencies?
  • (Optional)應該在什麼時候打包 Assets?(Javascript, CSS 等)

就我個人而言,PHP 是否與 Nginx 放在同一個 Image 這沒有正確答案:這取決於你是否想要同時維護 PHP 與 Nginx 的狀態。如果在同個 Container,它們會被視為同一服務處理(尤其是出錯時),如果在不同 Container,它們之間的結合不一定需要是一對一,甚至可以一對多(一個 Nginx Container 對多個 PHP Container),這有利於橫向擴展。

至於 Apache 的話因為綁定 libapache2_mod_php,所以通常它們會在同一個 Image。

註:其實還有另外一派的想法是:讓 PHP 與 Nginx 位於同個 Pod 但不同 Container。我個人認為這是目前最佳解,然而因為 Kubernetes Pod 目前仍未成為標準,且各家服務實現程度有所差距,故此處暫不討論(或許明年鐵人賽就能討論了?)

打包程式碼與 Composer Dependencies

一般來說,我會在專案的根目錄建立 Dockerfile

FROM composer

WORKDIR /code

COPY . .

RUN composer install --no-dev

並且將 vendor/ 加入 .dockerignore,避免在 COPY 時把它加進去。

會這麼做的目的是:

  • 本機使用的 composer dependencies 與正式環境使用的通常會不同
    • 例如在正式環境上不需要安裝 phpunit/phpunit
  • 程式碼本身會同時被 Copy 進去
    • 如果對 image 有潔癖的人甚至可以只將 composer.json, composer.locksrc/ 加入即可。

應該在什麼時候打包 Assets?

通常 assets 會使用像是 webpack 這樣的工具做最佳化,我個人是推薦使用 Use multi-stage builds 的功能

FROM node AS asset-builder

WORKDIR /code

# 這邊甚至可以只 COPY package.json 及原始檔案
COPY . .

# 這邊會視你的 webpack 指令是怎麼寫的
RUN npm run production

FROM composer

WORKDIR /code

COPY . .

# 這邊看編譯出來的位置下去複製
COPY --from=asset-builder /code/public/app.css /code/public/app.css
COPY --from=asset-builder /code/public/app.js /code/public/app.js

RUN composer install --no-dev

註:打包 assets 或打包 composer dependencies 的順序是沒什麼關係的(除非有明確的相依性)

偏門方法

roadrunner

Roadrunner 是一套用 Golang 寫的 PHP Web Server,在不需要啟用 FastCGI 的前提下就能夠產生用於 production 的 Web Server 服務,使其能夠直接包在一個容器中執行。

它相容於 PSR-7,所以諸如 Slim Framework 就能良好地被支援,對於 Laravel 的支援度也算相當高,不過無法用於像 Wordpress 那樣不支援 PSR-7 的環境。

需要注意的是,對於一些會讓 Process 停擺的函式(如 die, exit)會無法使用。

swoole

swoole 是一個 PHP 的 extension,它大幅度改寫 PHP 的底層運作,並且提升 PHP 運行效率(在部份 case 中甚至能與 Golang 一較高下)

swoole 允許啟動一個 web server (事實上 swoole 內建了 tcp 及 udp server 的啟動能力,並且可以用於設計 websocket 或 gRPC service),並直接將其包在一個容器中執行。

swoole 目前還是存在許多問題,例如對於 Xdebug 以及 PostgresQL 的支援性都還不夠高,且因為底層改寫 MySQL 或 Redis 等連接方式,導致與一般使用邏輯迥異。

後記

就個人角度而言,如果是既有專案,不妨重構為 podman/k8s 這樣以 Pod 為單位的現代化容器服務;如果是新建立的專案,可以考慮使用 roadrunner 甚至是 swoole 去做現代化容器設計。

就目前階段而言,我認為 roadrunner 比 swoole 來得方便,尤其是對於既有程式思維與設計方式而言。


上一篇
Day 12:使用 PHPUnit
下一篇
Day 14:php.ini 的設定
系列文
成為 Modern PHPer30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言