Wu-Hsien Yu/ 文章
文章

被 Composer 的 Timeout 卡了好幾個小時

有件事很讓人洩氣:花了好幾個小時,以為在除錯一個單純的 Docker build 問題,最後卻發現原因完全出乎意料。最近在部署 Laravel 時,遇到的就是這種情況。


其實是很常規的工作 - 當時要為一個 Laravel 應用程式建置 Docker image,準備跑在 Linode 上。沒什麼特別的,就是很典型的設定:

問題

一切看起來都很順利,直到 build 每次都剛好卡在 300 秒 失敗。準時得像時鐘一樣:

dockerfile
=> CANCELED exporting to image                                                                                       37.6s
...
The following exception is caused by a process timeout
Check https://getcomposer.org/doc/06-config.md#process-timeout for details

ERROR: failed to solve: Canceled: context canceled
In Process.php line 1205:

  The process "DOCKER_BUILDKIT=1 docker 'build' '--no-cache' '-t' 'my/api' '.'" exceeded the timeout of 300 seconds.

build [--dev] [--no-dev] [--] [<args>...]

我的第一個念頭是:「太好了,Docker 又來了」。就是那種感覺 - 第一時間就把矛頭指向最明顯的戰犯。我花了不少時間鑽進 Docker 的官方文件,想找出某個我一定是漏掉的神祕 build timeout 設定。但有些地方兜不攏。許多官方 image 的建置時間遠超過 300 秒,卻都能正常運作。而且,如果 Docker 真的內建 timeout,不是應該會寫在文件的某個地方嗎?

情況變得更複雜

事情開始變得複雜:心想不如用 multi-stage build 來優化建置流程:

dockerfile
FROM php:8.3-fpm AS mongodb-builder

RUN apt-get update && apt-get install -y \
    libssl-dev \
    && pecl install mongodb \
    && docker-php-ext-enable mongodb

# ...

FROM php:8.3-fpm

COPY --from=mongodb-builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions
COPY --from=mongodb-builder /usr/local/etc/php/conf.d/docker-php-ext-mongodb.ini /usr/local/etc/php/conf.d/

RUN apt-get update && apt-get install -y \
        curl gnupg gosu zip unzip git supervisor sqlite3 libcap2-bin \
        libpng-dev dnsutils librsvg2-bin fswatch vim libzip-dev \
        libxml2-dev libcurl4-openssl-dev libssl-dev \
    && docker-php-ext-install zip pdo_mysql bcmath soap intl opcache \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# ...

有解決問題嗎?並沒有。不過至少讓 Dockerfile 看起來漂亮多了,也算是有點收穫。

靈光乍現的瞬間

接著,我注意到錯誤訊息裡有一行,先前一直被我略過:

shell
Check https://getcomposer.org/doc/06-config.md#process-timeout for details

Composer?可是我在 Dockerfile 裡只不過是安裝 Composer 而已:

shell
&& curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \

就在這一刻,我恍然大悟。我在 composer.json 裡放了幾個順手的捷徑:

json
"lint": "pint"
"clear": [
    "@php artisan lighthouse:clear-cache"
    "@php artisan config:clear"
    "@php artisan route:clear"
    "@php artisan view:clear"
    "@php artisan cache:clear"
],
"test": [
    "@lint"
    "@clear"
    "pest --profile --colors=always"
],
"docker": "DOCKER_BUILDKIT=1 docker"
"build": [
    "@clear"
    "@docker build --no-cache -t tgk/be ."
    "@docker tag tgk/be heytakeshi/tgk-be:latest"
    "@docker push heytakeshi/tgk-be"
]

而我一直是透過 Composer、用 composer build 來執行 Docker 建置。猜猜看什麼東西的預設 timeout 剛好是 300 秒?沒錯,就是 Composer。

解決方法

解法簡單到有點難為情。兩個做法擇一:

  1. 請 Composer 把時間放寬一點(管理 process timeout):
json
"config": {
    // ...
    "process-timeout": 600
},
  1. 或者把 Docker 指令移到 shell script 裡,讓 Composer 管不著:
shell
#!/bin/bash

DOCKER_BUILDKIT=1 docker build --no-cache -t my/api .
docker tag my/api my/api
docker push my/api

我選了第一個做法,因為懶得更新所有的部署腳本。

心得

有時候,問題並不在你以為的地方。

答案明明就寫在錯誤訊息裡,我卻花了太多時間找自以為是的戰犯。另外,把所有東西都塞進 Composer script 來跑,或許也不總是個好主意 - 不過那就留給未來的我去煩惱了。

相關文章

2025.11.20laravel-bcmath-cast:可重複使用的 Eloquent 浮點數 Cast 套件2025.01.22PHP 8.4 浮點數運算處理:將 BCMath Object API 整合進 Laravel