How to get headless chrome (Puppeteer) inside docker image working on AWS Lambda

Viewed 889

I want to create a function to create a pdf from a HTML file and trigger it from AWS Lambda. I have put the function inside a docker image and deployed the docker image to AWS Lambda. It is working fine when I run the docker locally, but when I deploy it to AWS Lambda, it does not work.

My app.js which is responsible for creating the pdf file:

const fs = require('fs');

const puppeteer = require('puppeteer');
const path = require('path');
const chromium = require('chrome-aws-lambda');

const buildPaths = {
    buildPathHtml: path.resolve('./index.html'),
    buildPathPdf: path.resolve('./my-file.pdf')
};

const { buildPathHtml, buildPathPdf } = buildPaths;

const isFileExist = (filePath) => {
    try {
        fs.statSync(filePath);
        return true;
    } catch (error) {
        return false;
    }
};

const printPdf = async () => {
    if (!isFileExist(buildPathHtml)) {
        throw new Error('HTML file does not exist.');
    }
    console.log('Creating browser...');
    browser = await chromium.puppeteer.launch({
        args: ['--no-sandbox', '--disable-dev-shm-usage', '--disable-web-security'],
        executablePath: await chromium.executablePath,
        headless: true,
        });
    console.log('Browser created. Creating page...');
    const page = await browser.newPage();
    console.log('Page created. Going to html file path...');
    await page.goto(`file:${buildPathHtml}`, { waitUntil: 'networkidle0' });
    console.log('Went to html file path. Creating pdf...');
    const pdf = await page.pdf({
        format: 'A4',
        printBackground: true,
        landscape: true,
        displayHeaderFooter: true,
        headerTemplate: ``,
        footerTemplate: `
            <div style="font-size: 10px; font-family: 'Raleway'; font-weight: bold; width: 1000px; text-align: center; color: grey; padding-left: 10px;">
                <span>Page: </span>
                <span class="pageNumber"></span> / <span class="totalPages"></span>
                <span> - Date: </span>
                <span class="date"></span>
            </div>`,
        margin: {
            top: '20px',
            right: '20px',
            bottom: '40px',
            left: '20px'
        }
    });
    console.log('Pdf created. Closing the browser...');
    await browser.close();
    console.log('Succesfully created the PDF');
    fs.writeFileSync(buildPathPdf, pdf);
    return {
        headers: {
            "Content-type": "application/pdf",
        },
        statusCode: 200,
        body: pdf.toString("base64"),
        isBase64Encoded: true,
        };
    
};

exports.lambdaHandler = async (event) => {
  try {
        console.log('Handler invoked. Event: ', event);
        const response = await printPdf();
        console.log('Response generated. response: ', response);
        return response;
    } catch (error) {
        console.log('Error generating PDF', error);
    }
}

exports.lambdaHandler();

This is my Dockerfile:

FROM amazon/aws-lambda-nodejs:12

COPY google-chrome.repo /etc/yum.repos.d/
RUN yum -y update google-chrome-stable
RUN yum -y install google-chrome-stable
COPY . ./
RUN npm ci --quiet 

CMD [ "app.lambdaHandler" ]

I build the docker with this command:

docker build -t my-function .

Then I run it like this:

docker run -p 9000:8080 my-function:latest

Now to test it locally I run this command:

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'

And it works fine. But when I deploy it to AWS Lambda and invoke a test from there I get this error:

START RequestId: 3de1cb55-eee5-4c82-a3c4-4dabf7eb9570 Version: $LATEST
2021-03-18T22:06:30.748Z    3de1cb55-eee5-4c82-a3c4-4dabf7eb9570    INFO    Handler invoked. Event:  {}
2021-03-18T22:06:30.750Z    3de1cb55-eee5-4c82-a3c4-4dabf7eb9570    INFO    Creating browser...
2021-03-18T22:06:36.269Z    3de1cb55-eee5-4c82-a3c4-4dabf7eb9570    INFO    Error generating PDF Error: Protocol error (Target.setDiscoverTargets): Target closed.
    at /var/task/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:71:63
    at new Promise (<anonymous>)
    at Connection.send (/var/task/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:70:16)
    at Function.create (/var/task/node_modules/puppeteer/lib/cjs/puppeteer/common/Browser.js:116:26)
    at ChromeLauncher.launch (/var/task/node_modules/puppeteer/lib/cjs/puppeteer/node/Launcher.js:101:56)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async printPdf (/var/task/app.js:27:12)
    at async Runtime.exports.lambdaHandler [as handler] (/var/task/app.js:75:20)
END RequestId: 3de1cb55-eee5-4c82-a3c4-4dabf7eb9570
REPORT RequestId: 3de1cb55-eee5-4c82-a3c4-4dabf7eb9570  Duration: 5540.02 ms    Billed Duration: 6366 ms    Memory Size: 512 MB Max Memory Used: 377 MB Init Duration: 825.77 ms

It seems that google-chrome-stable has not been successfully installed in AWS Lambda environment. Any ideas how to fix it?

0 Answers
Related