Docker部署Puppeteer生成Next.js页面PDF遇超时错误求助
问题描述
目前只能通过Puppeteer生成Next.js预订页面的PDF:用户点击下载手册时收集邮箱并发送PDF下载链接,PDF在卖家发布或更新可预订行程时预生成。
曾尝试PDF类库,但页面设计学习成本高、处理动态数据和多格式图片能力不足,且暂无时间投入非核心功能开发,因此转回Puppeteer——它可通过常规CSS完成PDF设计,本地运行正常,但耗时三天仍无法在Docker中部署成功。
Puppeteer仅负责生成PDF并向主API返回响应,Docker仅用于托管Puppeteer,与主应用API分离。
Puppeteer代码
export const httpCreatePDF = async(req: Request, res:Response)=>{ const url = req.body.url; const prevPublicURL = req.body.prevPublicURL; const tripId = req.body.tripId; res.sendStatus(200); try { const browser = await puppeteer.launch({ headless: true, executablePath: 'google-chrome-stable', args: ['--no-sandbox', ] }); const page = await browser.newPage(); await page.goto(url, {waitUntil: 'networkidle0',}); page.setDefaultTimeout(5000) const pdf = await page.pdf({ format: 'A4', }); await browser.close(); // 删除旧PDF(如果存在) if(prevPublicURL){ await deleteAllFiles({filesToDelete: [prevPublicURL]}); }; // 上传新PDF到Cloudinary const uploadResponse = await cloudinaryBufferUpload(pdf) as any; const response = { pdfImageURL: uploadResponse?.secure_url, pdfPublicURL: uploadResponse?.public_id, tripId: tripId } const webHookReq = await axios.post(sanitizedConfig.WEBHOOK_URL_PDF, response); if(webHookReq?.status === 200){ return res.sendStatus(200); } return res.sendStatus(200) } catch (error) { throw error } }
Dockerfile
FROM node:slim WORKDIR /app # 复制配置文件 COPY package*.json ./ COPY tsconfig.json ./ # 复制源码 COPY src /app/src RUN npm install axios bcrypt cloudinary cookie-parser cors dotenv express jsonwebtoken mongoose puppeteer RUN npm install typescript --save-dev RUN npm run build FROM node:slim RUN apt-get update && apt-get install -y gnupg ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true RUN apt-get update \ && apt-get install -y wget gnupg \ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ && apt-get update \ && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \ --no-install-recommends \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY package*.json ./ RUN npm install axios bcrypt cloudinary cookie-parser cors dotenv express jsonwebtoken mongoose puppeteer COPY --from=0 /app/build ./build RUN npm uninstall typescript USER node CMD [ "npm", "start"] EXPOSE 6000
Docker Compose
version: "3" services: pdfapi: build: . image: kings19/pdf-api ports: - "6000:6000" env_file: - /src/services/Env/.env
部署后错误日志
at process.processTicksAndRejections (node:internal/process /task_queues:95:5) at fulfilled (/app/build/src/controllers/pdf.js:5:58) at Generator.next (<anonymous>) at /app/build/src/controllers/pdf.js:31:20 at CdpPage.goto (/app/node_modules/puppeteer-core/lib/cjs/puppeteer/api/Page.js:724:43) at CdpFrame.<anonymous> (/app/node_modules/puppeteer-core/lib/cjs/puppeteer/util/decorators.js:108:27) at CdpFrame.goto (/app/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/Frame.js:140:29) at new LifecycleWatcher (/app/node_modules/puppeteer-core/lib/cjs/puppeteer/cdp/LifecycleWatcher.js:75:60) at Deferred.create (/app/node_modules/puppeteer-core/lib/cjs/puppeteer/util/Deferred.js:68:16) at new Deferred (/app/node_modules/puppeteer-core/lib/cjs/puppeteer/util/Deferred.js:26:34) TimeoutError: Navigation timeout of 30000 ms exceeded this.#timeoutError = new Errors_js_1.TimeoutError(opts.message); /app/node_modules/puppeteer-core/lib/cjs/puppeteer/util/Deferred.js:26
更新信息
经测试,Puppeteer可正常生成谷歌页面的PDF,但无法生成我的Next.js页面;本地可正常生成该页面的PDF,部署后则持续超时。目标页面为SSR页面,托管在Render免费账号,链接为:
https://bookingapp-mocha.vercel.app/builder/live/64e3aaba042c8f73316d13f1-Experience-the-Cairo-vibes-at-Night
解决方案
针对Docker中Puppeteer访问Next.js SSR页面超时的问题,按以下步骤修复:
1. 调整Puppeteer启动参数与导航配置
- 替换Chrome启动参数,解决容器资源限制与网络适配问题:
const browser = await puppeteer.launch({ headless: 'new', // 使用新版无头模式,兼容性更强 executablePath: 'google-chrome-stable', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', // 解决容器内存不足导致的Chrome崩溃 '--disable-gpu', '--disable-software-rasterizer', '--window-size=1920,1080', // 固定窗口尺寸,避免布局异常 '--single-process' // 单进程模式,减少资源占用 ], defaultViewport: { width: 1920, height: 1080 } }); - 修改页面导航策略,适配Render免费版的冷启动延迟:
await page.goto(url, { waitUntil: 'domcontentloaded', // 比networkidle0更宽松,适合动态页面 timeout: 60000 // 延长超时时间到60秒 }); // 额外等待页面核心元素加载完成 await page.waitForSelector('body', { timeout: 30000 }); - 关键修复:将
res.sendStatus(200)移到try块的成功逻辑末尾,避免提前返回响应中断后续PDF生成流程。
2. 优化Docker镜像配置
合并多阶段构建步骤,减少依赖冗余与镜像体积:
# 构建阶段 FROM node:18-slim AS builder WORKDIR /app COPY package*.json tsconfig.json ./ COPY src ./src RUN npm ci RUN npm install typescript --save-dev RUN npm run build # 运行阶段 FROM node:18-slim WORKDIR /app ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true RUN apt-get update && apt-get install -y --no-install-recommends \ google-chrome-stable \ fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \ libxss1 \ && rm -rf /var/lib/apt/lists/* COPY package*.json ./ RUN npm ci --omit=dev COPY --from=builder /app/build ./build USER node CMD ["npm", "start"] EXPOSE 6000
- 使用
node:18-slim提升兼容性,用npm ci确保依赖版本一致,避免随机bug。
3. 适配Render免费版页面限制
Render免费版存在冷启动延迟与IP访问限制,添加以下配置:
// 设置浏览器UA,模拟正常用户请求 await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36');
- 如果页面有反爬机制,可在Render后台将Puppeteer容器的IP添加到白名单。
4. 增加日志排查
在代码中添加关键节点日志,定位超时环节:
console.log(`开始访问目标页面: ${url}`); try { await page.goto(url, { ... }); console.log('页面加载完成'); const pdf = await page.pdf({ ... }); console.log('PDF生成成功'); // 后续上传与回调逻辑 } catch (error) { console.error('错误详情:', error); throw error; }
通过docker logs <容器ID>查看日志,明确是页面加载超时还是PDF生成环节出错。
内容的提问来源于stack exchange,提问作者david
相关产品推荐
相关产品推荐

