Dockerfile 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. # ========================================
  2. # 第一阶段:构建前端资源
  3. # ========================================
  4. FROM node:20-alpine AS frontend-builder
  5. # 设置工作目录
  6. WORKDIR /app
  7. # 安装 bun
  8. RUN npm install -g bun@latest
  9. # 复制 package.json 并配置 npm 镜像
  10. COPY package.json ./
  11. RUN npm config set registry https://registry.npmmirror.com
  12. # 安装依赖并生成 lockfile
  13. RUN bun install
  14. # 复制其他配置文件
  15. COPY tailwind.config.js postcss.config.js vite.config.js ./
  16. # 复制源码
  17. COPY resources/ ./resources/
  18. # 构建前端资源
  19. RUN bun run build
  20. # ========================================
  21. # 第二阶段:PHP 运行时(使用 php-fpm)
  22. # ========================================
  23. FROM php:8.3-fpm-alpine AS base-runtime
  24. # 安装系统依赖 - 使用阿里云镜像
  25. RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
  26. apk add --no-cache \
  27. git \
  28. curl \
  29. ca-certificates \
  30. libzip-dev \
  31. libpng-dev \
  32. oniguruma-dev \
  33. libjpeg-turbo-dev \
  34. freetype-dev \
  35. icu-dev \
  36. libzip-dev \
  37. sqlite-dev \
  38. # Chrome/Chromium 依赖(PDF 生成必须)
  39. chromium \
  40. nss \
  41. freetype \
  42. harfbuzz \
  43. # dbus(解决 Chrome 在容器中的连接问题)
  44. dbus \
  45. # 字体(PDF 中文显示必须)
  46. ttf-freefont \
  47. font-noto-cjk \
  48. font-noto \
  49. ttf-dejavu \
  50. fontconfig \
  51. # PDF 合并工具
  52. poppler-utils \
  53. # Node.js(KaTeX 服务端渲染需要)
  54. nodejs \
  55. npm \
  56. && rm -rf /var/cache/apk/* \
  57. && fc-cache -fv \
  58. && mkdir -p /run/dbus
  59. # 设置 Chrome 环境变量
  60. ENV CHROME_BIN=/usr/bin/chromium-browser \
  61. CHROME_PATH=/usr/lib/chromium/ \
  62. PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
  63. PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
  64. # 安装 PHP 扩展
  65. RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
  66. docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_sqlite gd zip intl pcntl
  67. # 安装 Redis 扩展
  68. RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
  69. && pecl install redis \
  70. && docker-php-ext-enable redis \
  71. && apk del .build-deps
  72. # 安装 Composer - 使用国内镜像
  73. COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
  74. RUN composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
  75. # 创建应用目录
  76. WORKDIR /app
  77. # 复制 Composer 文件并安装依赖
  78. COPY composer.json composer.lock ./
  79. RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --no-scripts
  80. # 安装 KaTeX(服务端公式渲染)
  81. RUN npm config set registry https://registry.npmmirror.com && \
  82. npm install -g katex@0.16.9
  83. # 复制应用代码(排除 node_modules, vendor, storage 等)
  84. COPY --chown=www-data:www-data . .
  85. # 创建必要目录
  86. RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache
  87. # 设置运行时可写目录权限,避免 chown -R /app 复制出巨大镜像层
  88. RUN chmod -R 775 /app/storage /app/bootstrap/cache
  89. # 通用入口脚本(queue worker 模式仅执行命令,不启动 nginx)
  90. COPY docker-entrypoint.sh /usr/local/bin/
  91. RUN chmod +x /usr/local/bin/docker-entrypoint.sh
  92. ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
  93. # ========================================
  94. # 第三阶段:App Runtime(Nginx + PHP-FPM)
  95. # ========================================
  96. FROM base-runtime AS app-runtime
  97. # 安装 Nginx(仅 Web API 容器需要)
  98. RUN apk add --no-cache nginx && \
  99. mkdir -p /run/nginx /var/log/nginx
  100. # 从第一阶段复制构建好的前端资源
  101. COPY --from=frontend-builder /app/public/build ./public/build
  102. # 缓存路由和视图(不缓存配置,配置在运行时从 .env 读取)
  103. RUN php artisan route:cache && \
  104. php artisan view:cache && \
  105. php artisan filament:upgrade || true
  106. # 复制 Nginx 和 PHP-FPM 配置
  107. COPY docker/nginx.conf /etc/nginx/nginx.conf
  108. COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
  109. EXPOSE 8000
  110. # 健康检查(检查 Nginx 是否响应)
  111. HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  112. CMD curl -f http://127.0.0.1:8000/health || exit 1
  113. CMD ["nginx", "-g", "daemon off;"]
  114. # ========================================
  115. # 第四阶段:PDF Worker Runtime(无 Nginx/无前端构建)
  116. # ========================================
  117. FROM base-runtime AS pdfworker
  118. CMD ["php", "artisan", "queue:work", "--queue=pdf", "--sleep=3", "--tries=2", "--max-time=300", "--max-jobs=10"]
  119. # ========================================
  120. # 第五阶段:Slim Runtime(不安装 Chromium,配合 Gotenberg/远端 PDF worker)
  121. # ========================================
  122. FROM php:8.3-fpm-alpine AS base-runtime-local
  123. RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
  124. apk add --no-cache \
  125. git \
  126. curl \
  127. ca-certificates \
  128. libzip-dev \
  129. libpng-dev \
  130. oniguruma-dev \
  131. libjpeg-turbo-dev \
  132. freetype-dev \
  133. icu-dev \
  134. sqlite-dev \
  135. # 字体(HTML渲染/回退字体)
  136. ttf-freefont \
  137. font-noto-cjk \
  138. font-noto \
  139. ttf-dejavu \
  140. fontconfig \
  141. # Node.js(KaTeX 服务端渲染需要)
  142. nodejs \
  143. npm \
  144. && rm -rf /var/cache/apk/* \
  145. && fc-cache -fv
  146. RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
  147. docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_sqlite gd zip intl pcntl
  148. RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
  149. && pecl install redis \
  150. && docker-php-ext-enable redis \
  151. && apk del .build-deps
  152. COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
  153. RUN composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
  154. WORKDIR /app
  155. COPY composer.json composer.lock ./
  156. RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --no-scripts
  157. RUN npm config set registry https://registry.npmmirror.com && \
  158. npm install -g katex@0.16.9 && \
  159. npm cache clean --force
  160. RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && \
  161. chmod -R 775 storage bootstrap/cache
  162. COPY docker-entrypoint.sh /usr/local/bin/
  163. RUN chmod +x /usr/local/bin/docker-entrypoint.sh
  164. ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
  165. # 热加载 app 目标:源码由 mount compose 挂载,不复制项目代码
  166. FROM base-runtime-local AS app-runtime-local-hot
  167. RUN apk add --no-cache nginx && \
  168. mkdir -p /run/nginx /var/log/nginx
  169. COPY --from=frontend-builder /app/public/build ./public/build
  170. COPY docker/nginx.conf /etc/nginx/nginx.conf
  171. COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
  172. EXPOSE 8000
  173. HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  174. CMD curl -f http://127.0.0.1:8000/health || exit 1
  175. CMD ["nginx", "-g", "daemon off;"]
  176. # 热加载 worker 目标:源码由 mount compose 挂载
  177. FROM base-runtime-local AS worker-local-hot
  178. CMD ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"]
  179. # Slim app 目标(无 Chromium)
  180. FROM base-runtime-local AS app-runtime-local
  181. RUN apk add --no-cache nginx && \
  182. mkdir -p /run/nginx /var/log/nginx
  183. COPY --chown=www-data:www-data . .
  184. COPY --from=frontend-builder /app/public/build ./public/build
  185. RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && \
  186. chmod -R 775 storage bootstrap/cache
  187. RUN php artisan route:cache && \
  188. php artisan view:cache && \
  189. php artisan filament:upgrade || true
  190. COPY docker/nginx.conf /etc/nginx/nginx.conf
  191. COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
  192. EXPOSE 8000
  193. HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  194. CMD curl -f http://127.0.0.1:8000/health || exit 1
  195. CMD ["nginx", "-g", "daemon off;"]
  196. # Slim worker 目标(无 Chromium)
  197. FROM base-runtime-local AS worker-local
  198. COPY --chown=www-data:www-data . .
  199. RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache && \
  200. chmod -R 775 storage bootstrap/cache
  201. CMD ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"]
  202. # API 服务器目标(无 Chromium,PDF 走 Gotenberg/远端 worker 策略)
  203. FROM app-runtime-local AS app-runtime-api
  204. # API 服务器代码挂载目标(用于测试/联调热更新)
  205. FROM app-runtime-local-hot AS app-runtime-api-hot