如何优化Puppeteer Docker镜像大小以解决Render部署失败问题?
问题描述
我按照网上教程为正在开发的应用构建了用于生成PDF的Puppeteer Docker镜像,并搭建了独立API。本地构建启动正常,但镜像体积达2GB,部署到Render时无报错但失败,推测是免费账户的镜像大小限制导致该问题。
原Dockerfile:
FROM node:18 RUN apt-get update && apt-get install gnupg wget -y && \ wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && \ 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 google-chrome-stable -y --no-install-recommends && \ rm -rf /var/lib/apt/lists/* ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true RUN groupadd -r pptruser && useradd -rm -g pptruser -G audio,video pptruser WORKDIR /app # copy configs to /app folder COPY package*.json ./ COPY tsconfig.json ./ # copy source code to /app/src folder COPY src /app/src RUN npm install axios bcrypt cloudinary cookie-parser cors dotenv express jsonwebtoken puppeteer RUN npm install typescript --save-dev RUN npm run build FROM node:18 RUN apt-get update && apt-get install gnupg wget -y && \ wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && \ 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 google-chrome-stable -y --no-install-recommends && \ rm -rf /var/lib/apt/lists/* ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true RUN groupadd -r pptruser && useradd -rm -g pptruser -G audio,video pptruser WORKDIR /app COPY package*.json ./ RUN npm install axios bcrypt cloudinary cookie-parser cors dotenv express jsonwebtoken puppeteer COPY --from=0 /app/build ./build RUN npm uninstall typescript USER pptruser CMD [ "npm", "start"] EXPOSE 6000
PDF生成代码:
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); const browser = await puppeteer.launch({ headless: 'new', executablePath: '/usr/bin/google-chrome', ignoreDefaultArgs: ['--disable-extensions'], args: ['--no-sandbox', '--disable-setuid-sandbox'], }); const page = await browser.newPage(); try { await page.goto(url, {waitUntil: 'networkidle0'}); const pdf = await page.pdf( { format: 'A4', }); await browser.close(); //delete prev pdf if it exist in cloudinary if(prevPublicURL){ await deleteAllFiles({filesToDelete: [prevPublicURL]}); }; //upload the file to 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 } else{ await axios.post(sanitizedConfig.WEBHOOK_URL_PDF, response); } return } catch (error) { await browser.close(); throw error } }
优化方案
1. 复用Chrome安装层,避免重复构建
原Dockerfile在构建和运行阶段重复安装Chrome,直接导致镜像体积翻倍。将Chrome安装流程抽离为基础镜像层,供后续阶段复用。
2. 使用轻量Node镜像
替换完整版node:18为node:18-slim,该镜像基于精简版Debian,体积更小且能满足运行需求。
3. 分离构建与运行依赖
构建阶段安装完整依赖(含开发依赖如typescript),运行阶段仅安装生产依赖,同时用npm ci替代npm install,保证依赖版本一致且清理缓存。
4. 合并冗余命令,清理临时文件
将多个RUN命令合并,减少镜像层数;及时清理apt、npm缓存及无用工具(如gnupg、wget),避免残留文件占用空间。
优化后的Dockerfile
# 基础镜像:安装Chrome及依赖,供构建/运行阶段复用 FROM node:18-slim as base RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg wget ca-certificates \ && wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg \ && 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 --no-install-recommends google-chrome-stable \ && apt-get purge -y --auto-remove gnupg wget \ && rm -rf /var/lib/apt/lists/* ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true # 创建非特权用户 RUN groupadd -r pptruser && useradd -rm -g pptruser -G audio,video pptruser # 构建阶段:编译TypeScript代码 FROM base as builder WORKDIR /app COPY package*.json tsconfig.json ./ COPY src ./src # 安装所有依赖(含开发依赖) RUN npm ci RUN npm run build # 运行阶段:仅保留生产依赖与编译后代码 FROM base WORKDIR /app # 复制依赖配置,仅安装生产依赖 COPY package*.json ./ RUN npm ci --only=production && npm cache clean --force # 从构建阶段复制编译产物 COPY --from=builder /app/build ./build USER pptruser CMD ["npm", "start"] EXPOSE 6000
额外优化建议
- 清理
package.json中未使用的依赖,进一步缩减体积。 - 调整Puppeteer启动参数,适配容器环境并减少资源占用:
const browser = await puppeteer.launch({ headless: 'new', executablePath: '/usr/bin/google-chrome', ignoreDefaultArgs: ['--disable-extensions'], args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', // 避免共享内存不足 '--disable-gpu', // 禁用GPU,减少资源占用 '--single-process' // 单进程模式适配容器 ], });
内容的提问来源于stack exchange,提问作者david
相关产品推荐
相关产品推荐

