| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- # ========================================
- # 第一阶段:构建前端资源
- # ========================================
- FROM node:20-alpine AS frontend-builder
- # 设置工作目录
- WORKDIR /app
- # 安装 bun
- RUN npm install -g bun@latest
- # 复制 package.json 并配置 npm 镜像
- COPY package.json ./
- RUN npm config set registry https://registry.npmmirror.com
- # 安装依赖并生成 lockfile
- RUN bun install
- # 复制其他配置文件
- COPY tailwind.config.js postcss.config.js vite.config.js ./
- # 复制源码
- COPY resources/ ./resources/
- # 构建前端资源
- RUN bun run build
- # ========================================
- # 第二阶段:PHP 运行时(使用 php-fpm)
- # ========================================
- FROM php:8.3-fpm-alpine AS base-runtime
- # 安装系统依赖 - 使用阿里云镜像
- RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
- apk add --no-cache \
- git \
- curl \
- ca-certificates \
- libzip-dev \
- libpng-dev \
- oniguruma-dev \
- libjpeg-turbo-dev \
- freetype-dev \
- icu-dev \
- libzip-dev \
- sqlite-dev \
- # Chrome/Chromium 依赖(PDF 生成必须)
- chromium \
- nss \
- freetype \
- harfbuzz \
- # dbus(解决 Chrome 在容器中的连接问题)
- dbus \
- # 字体(PDF 中文显示必须)
- ttf-freefont \
- font-noto-cjk \
- font-noto \
- ttf-dejavu \
- fontconfig \
- # PDF 合并工具
- poppler-utils \
- # Node.js(KaTeX 服务端渲染需要)
- nodejs \
- npm \
- && rm -rf /var/cache/apk/* \
- && fc-cache -fv \
- && mkdir -p /run/dbus
- # 设置 Chrome 环境变量
- ENV CHROME_BIN=/usr/bin/chromium-browser \
- CHROME_PATH=/usr/lib/chromium/ \
- PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
- PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
- # 安装 PHP 扩展
- RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
- docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_sqlite gd zip intl pcntl
- # 安装 Redis 扩展
- RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
- && pecl install redis \
- && docker-php-ext-enable redis \
- && apk del .build-deps
- # 安装 Composer - 使用国内镜像
- COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
- RUN composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
- # 创建应用目录
- WORKDIR /app
- # 复制 Composer 文件并安装依赖
- COPY composer.json composer.lock ./
- RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --no-scripts
- # 安装 KaTeX(服务端公式渲染)
- RUN npm config set registry https://registry.npmmirror.com && \
- npm install -g katex@0.16.9
- # 复制应用代码(排除 node_modules, vendor, storage 等)
- COPY --chown=www-data:www-data . .
- # 创建必要目录
- RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache
- # 设置运行时可写目录权限,避免 chown -R /app 复制出巨大镜像层
- RUN chmod -R 775 /app/storage /app/bootstrap/cache
- # 通用入口脚本(queue worker 模式仅执行命令,不启动 nginx)
- COPY docker-entrypoint.sh /usr/local/bin/
- RUN chmod +x /usr/local/bin/docker-entrypoint.sh
- ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
- # ========================================
- # 第三阶段:App Runtime(Nginx + PHP-FPM)
- # ========================================
- FROM base-runtime AS app-runtime
- # 安装 Nginx(仅 Web API 容器需要)
- RUN apk add --no-cache nginx && \
- mkdir -p /run/nginx /var/log/nginx
- # 从第一阶段复制构建好的前端资源
- COPY --from=frontend-builder /app/public/build ./public/build
- # 缓存路由和视图(不缓存配置,配置在运行时从 .env 读取)
- RUN php artisan route:cache && \
- php artisan view:cache && \
- php artisan filament:upgrade || true
- # 复制 Nginx 和 PHP-FPM 配置
- COPY docker/nginx.conf /etc/nginx/nginx.conf
- COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
- EXPOSE 8000
- # 健康检查(检查 Nginx 是否响应)
- HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
- CMD curl -f http://127.0.0.1:8000/health || exit 1
- CMD ["nginx", "-g", "daemon off;"]
- # ========================================
- # 第四阶段:PDF Worker Runtime(无 Nginx/无前端构建)
- # ========================================
- FROM base-runtime AS pdfworker
- CMD ["php", "artisan", "queue:work", "--queue=pdf", "--sleep=3", "--tries=2", "--max-time=300", "--max-jobs=10"]
- # ========================================
- # 第五阶段:Slim Runtime(不安装 Chromium,配合 Gotenberg/远端 PDF worker)
- # ========================================
- FROM php:8.3-fpm-alpine AS base-runtime-local
- RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
- apk add --no-cache \
- git \
- curl \
- ca-certificates \
- libzip-dev \
- libpng-dev \
- oniguruma-dev \
- libjpeg-turbo-dev \
- freetype-dev \
- icu-dev \
- sqlite-dev \
- # 字体(HTML渲染/回退字体)
- ttf-freefont \
- font-noto-cjk \
- font-noto \
- ttf-dejavu \
- fontconfig \
- # Node.js(KaTeX 服务端渲染需要)
- nodejs \
- npm \
- && rm -rf /var/cache/apk/* \
- && fc-cache -fv
- RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
- docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_sqlite gd zip intl pcntl
- RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
- && pecl install redis \
- && docker-php-ext-enable redis \
- && apk del .build-deps
- COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
- RUN composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
- WORKDIR /app
- COPY composer.json composer.lock ./
- RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --no-scripts
- RUN npm config set registry https://registry.npmmirror.com && \
- npm install -g katex@0.16.9 && \
- npm cache clean --force
- RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && \
- chmod -R 775 storage bootstrap/cache
- COPY docker-entrypoint.sh /usr/local/bin/
- RUN chmod +x /usr/local/bin/docker-entrypoint.sh
- ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
- # 热加载 app 目标:源码由 mount compose 挂载,不复制项目代码
- FROM base-runtime-local AS app-runtime-local-hot
- RUN apk add --no-cache nginx && \
- mkdir -p /run/nginx /var/log/nginx
- COPY --from=frontend-builder /app/public/build ./public/build
- COPY docker/nginx.conf /etc/nginx/nginx.conf
- COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
- EXPOSE 8000
- HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
- CMD curl -f http://127.0.0.1:8000/health || exit 1
- CMD ["nginx", "-g", "daemon off;"]
- # 热加载 worker 目标:源码由 mount compose 挂载
- FROM base-runtime-local AS worker-local-hot
- CMD ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"]
- # Slim app 目标(无 Chromium)
- FROM base-runtime-local AS app-runtime-local
- RUN apk add --no-cache nginx && \
- mkdir -p /run/nginx /var/log/nginx
- COPY --chown=www-data:www-data . .
- COPY --from=frontend-builder /app/public/build ./public/build
- RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && \
- chmod -R 775 storage bootstrap/cache
- RUN php artisan route:cache && \
- php artisan view:cache && \
- php artisan filament:upgrade || true
- COPY docker/nginx.conf /etc/nginx/nginx.conf
- COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
- EXPOSE 8000
- HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
- CMD curl -f http://127.0.0.1:8000/health || exit 1
- CMD ["nginx", "-g", "daemon off;"]
- # Slim worker 目标(无 Chromium)
- FROM base-runtime-local AS worker-local
- COPY --chown=www-data:www-data . .
- RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && \
- chmod -R 775 storage bootstrap/cache
- CMD ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"]
- # API 服务器目标(无 Chromium,PDF 走 Gotenberg/远端 worker 策略)
- FROM app-runtime-local AS app-runtime-api
- # API 服务器代码挂载目标(用于测试/联调热更新)
- FROM app-runtime-local-hot AS app-runtime-api-hot
|