前情提要

最近偶爾會使用 PaaS 的佈署平台例如 GCP App Engine 或是 Digital Ocean 的 App Platform,這兩個 PaaS 平台都有個共通的特點就是,有的時候想要做一些特殊的任務或是想要安裝自己的 PHP Extension 的時候,就必須要使用自己的 Docker Image 來作為載體,而這些平台提供的功能又不能用 Docker compose 把 Nginx 跟 PHP-FPM 分開裝,所以只能把這兩個東西裝在一起。

話說用 DigitalOcean App Platform 的時候,就覺得自己用自己的 Image 勢在必行,如果你跟我一樣使用 Doctrine 的話,那麼你”有機率”在部暑到 App Platform 之後得到這麼一個錯誤:

Operation ‘Doctrine\DBAL\Platforms\AbstractPlatform::getSequenceNextValSQL’ is not supported by platform.

然後你就只能在 Digital Ocean 中重開一個 App,然後用同一個步驟,多試幾次就解決了,完全不知道為什麼,所以這就使得佈署在 Digital Ocean 增加了一些不穩定性,所以想佈署在那裡,請一定要用自己的 Docker Image。

小牢騷

開工

其實這個工作並不難,我們只要在 Symfony 的 Project root directory 底下新增三個檔案,分別是 docker.sh、nginx以及Dockerfile。

建立 Docker 啟動腳本 (docker.sh)

這個腳本是在 Docker 啟動之後運行的 shell script:

#!/usr/bin/env sh

service php7.4-fpm restart
nginx -g 'daemon off;'

簡單來說就是在 Docker Instance 啟動之後,先啟動 PHP7.4 FPM 之後再啟動 nginx。之所以要將 nginx 的 daemon mode 關掉是因為,沒有一個應用程式在前台運行,docker 就會自動結束。

建立 Nginx Configuration File (nginx)

此外這個檔案可以依照需求自己改變。

server {
    root /app/public;

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    # optionally disable falling back to PHP script for the asset directories;
    # nginx will return a 404 error when files are not found instead of passing the
    # request to Symfony (improves performance but Symfony's 404 page is not displayed)
    # location /bundles {
    #     try_files $uri =404;
    # }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        # fastcgi_param APP_ENV prod;
        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        # Caveat: When PHP-FPM is hosted on a different machine from nginx
        #         $realpath_root may not resolve as you expect! In this case try using
        #         $document_root instead.
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # return 404 for all other php files not matching the front controller
    # this prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }

    # error_log /var/log/nginx/project_error.log;
    # access_log /var/log/nginx/project_access.log;
}

建立 Dockerfile

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt install -y nginx php php-fpm composer
RUN apt install -y php-xml php-mysql php-zip

# 網站根目錄將會被放在 /app 中
COPY . /app
WORKDIR /app
RUN rm -R vendor
RUN APP_ENV=prod composer install
RUN chmod +x docker.sh

RUN rm /etc/nginx/sites-available/default
RUN cp nginx /etc/nginx/sites-available/default

# 這可以改成你想改的 Port
EXPOSE 80

# instance 啟動之後執行 /app/docker.sh
CMD ["/app/docker.sh"]

建立 DockerImage 及嘗試運行

sudo docker build -t floatflower/symfony .
# 如果你在 Dockerfile 中有更改 Port,那麼記得把 80 改成你指定的 Port。
sudo docker run -d -p 8003:80 floatflower/symfony

接下來我們用瀏覽器打開 http://localhost:8003,就能看到以下畫面,說明我們的 Docker 建立成功: