AWS Lambda 上的权限问题,无法生成子进程
Permissions trouble on AWS Lambda, can't spawn child process
所以我创建了这个漂亮的小 lambda,它在本地运行得很好,但实际上在野外运行时就没那么好了。
lambda 获取一个事件,在事件源中使用 html,将 html 转换为 PDF(使用 html-pdf 节点模块),将该 pdf 传递给一个 s3 存储桶,然后返回一个已签名的 url,该 url 将在 60 秒后过期。
或者至少这是应该发生的事情(同样,在本地工作)。在 Lambda 上测试时,出现以下错误:
{
"errorMessage": "spawn EACCES",
"errorType": "Error",
"stackTrace": [
"exports._errnoException (util.js:870:11)",
"ChildProcess.spawn (internal/child_process.js:298:11)",
"Object.exports.spawn (child_process.js:362:9)",
"PDF.PdfExec [as exec] (/var/task/node_modules/html-pdf/lib/pdf.js:87:28)",
"PDF.PdfToFile [as toFile] (/var/task/node_modules/html-pdf/lib/pdf.js:83:8)",
"/var/task/index.js:72:43",
"Promise._execute (/var/task/node_modules/bluebird/js/release/debuggability.js:272:9)",
"Promise._resolveFromExecutor (/var/task/node_modules/bluebird/js/release/promise.js:473:18)",
"new Promise (/var/task/node_modules/bluebird/js/release/promise.js:77:14)",
"createPDF (/var/task/index.js:71:19)",
"main (/var/task/index.js:50:5)"
]
}
这是代码本身(未编译,有一个方便的 gulp 任务)
if(typeof regeneratorRuntime === 'undefined') {
require("babel/polyfill")
}
import fs from 'fs'
import pdf from 'html-pdf'
import md5 from 'md5'
import AWS from 'aws-sdk'
import Promise from 'bluebird'
import moment from 'moment'
const tempDir = '/tmp'
const config = require('./config')
const s3 = new AWS.S3()
export const main = (event, context) => {
console.log("Got event: ", event)
AWS.config.update({
accessKeyId: config.awsKey,
secretAccessKey: config.awsSecret,
region: 'us-east-1'
})
const filename = md5(event.html) + ".pdf"
createPDF(event.html, filename).then(function(result) {
uploadToS3(filename, result.filename).then(function(result) {
getOneTimeUrl(filename).then(function(result) {
return context.succeed(result)
}, function(err) {
console.log(err)
return context.fail(err)
})
}, function(err) {
console.log(err)
return context.fail(err)
})
}, function(err) {
console.log(err)
return context.fail(err)
})
}
const createPDF = (html, filename) => {
console.log("Creating PDF")
var promise = new Promise(function(resolve, reject) {
pdf.create(html).toFile(filename, function(err, res) {
if (err) {
reject(err)
} else {
resolve(res)
}
})
})
return promise
}
const uploadToS3 = (filename, filePath) => {
console.log("Pushing to S3")
var promise = new Promise(function(resolve, reject) {
var fileToUpload = fs.createReadStream(filePath)
var expiryDate = moment().add(1, 'm').toDate()
var uploadParams = {
Bucket: config.pdfBucket,
Key: filename,
Body: fileToUpload
}
s3.upload(uploadParams, function(err, data) {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
return promise
}
const getOneTimeUrl = (filename) => {
var promise = new Promise(function(resolve, reject) {
var params = {
Bucket: config.pdfBucket,
Key: filename,
Expires: 60
}
s3.getSignedUrl('getObject', params, function(err, url) {
if (err) {
reject(err)
} else {
resolve(url)
}
})
})
return promise
}
似乎是 html-pdf 中的问题。我认为这可能是 PhantomJS(html-pdf 取决于)的问题,因为我在这里做了一些阅读:https://engineering.fundingcircle.com/blog/2015/04/09/aws-lambda-for-great-victory/,但是,由于 Lambda 已将最大 zip 大小提高到 50mb,我不上传二进制文件时遇到问题。
有什么想法吗?
html-pdf 在后台使用 phantomjs,安装时需要编译一些二进制文件。我猜你的问题是你正在部署那些本地编译的二进制文件,但 Lambda 需要在 Amazon Linux.
上编译的二进制文件
您可以通过在 运行 Amazon Linux 的 EC2 实例上构建部署包来解决此问题,然后例如直接从那里部署它,就像在 this tutorial.
中解释的那样
另请查看 中类似的问题。
所以我创建了这个漂亮的小 lambda,它在本地运行得很好,但实际上在野外运行时就没那么好了。
lambda 获取一个事件,在事件源中使用 html,将 html 转换为 PDF(使用 html-pdf 节点模块),将该 pdf 传递给一个 s3 存储桶,然后返回一个已签名的 url,该 url 将在 60 秒后过期。
或者至少这是应该发生的事情(同样,在本地工作)。在 Lambda 上测试时,出现以下错误:
{
"errorMessage": "spawn EACCES",
"errorType": "Error",
"stackTrace": [
"exports._errnoException (util.js:870:11)",
"ChildProcess.spawn (internal/child_process.js:298:11)",
"Object.exports.spawn (child_process.js:362:9)",
"PDF.PdfExec [as exec] (/var/task/node_modules/html-pdf/lib/pdf.js:87:28)",
"PDF.PdfToFile [as toFile] (/var/task/node_modules/html-pdf/lib/pdf.js:83:8)",
"/var/task/index.js:72:43",
"Promise._execute (/var/task/node_modules/bluebird/js/release/debuggability.js:272:9)",
"Promise._resolveFromExecutor (/var/task/node_modules/bluebird/js/release/promise.js:473:18)",
"new Promise (/var/task/node_modules/bluebird/js/release/promise.js:77:14)",
"createPDF (/var/task/index.js:71:19)",
"main (/var/task/index.js:50:5)"
]
}
这是代码本身(未编译,有一个方便的 gulp 任务)
if(typeof regeneratorRuntime === 'undefined') {
require("babel/polyfill")
}
import fs from 'fs'
import pdf from 'html-pdf'
import md5 from 'md5'
import AWS from 'aws-sdk'
import Promise from 'bluebird'
import moment from 'moment'
const tempDir = '/tmp'
const config = require('./config')
const s3 = new AWS.S3()
export const main = (event, context) => {
console.log("Got event: ", event)
AWS.config.update({
accessKeyId: config.awsKey,
secretAccessKey: config.awsSecret,
region: 'us-east-1'
})
const filename = md5(event.html) + ".pdf"
createPDF(event.html, filename).then(function(result) {
uploadToS3(filename, result.filename).then(function(result) {
getOneTimeUrl(filename).then(function(result) {
return context.succeed(result)
}, function(err) {
console.log(err)
return context.fail(err)
})
}, function(err) {
console.log(err)
return context.fail(err)
})
}, function(err) {
console.log(err)
return context.fail(err)
})
}
const createPDF = (html, filename) => {
console.log("Creating PDF")
var promise = new Promise(function(resolve, reject) {
pdf.create(html).toFile(filename, function(err, res) {
if (err) {
reject(err)
} else {
resolve(res)
}
})
})
return promise
}
const uploadToS3 = (filename, filePath) => {
console.log("Pushing to S3")
var promise = new Promise(function(resolve, reject) {
var fileToUpload = fs.createReadStream(filePath)
var expiryDate = moment().add(1, 'm').toDate()
var uploadParams = {
Bucket: config.pdfBucket,
Key: filename,
Body: fileToUpload
}
s3.upload(uploadParams, function(err, data) {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
return promise
}
const getOneTimeUrl = (filename) => {
var promise = new Promise(function(resolve, reject) {
var params = {
Bucket: config.pdfBucket,
Key: filename,
Expires: 60
}
s3.getSignedUrl('getObject', params, function(err, url) {
if (err) {
reject(err)
} else {
resolve(url)
}
})
})
return promise
}
似乎是 html-pdf 中的问题。我认为这可能是 PhantomJS(html-pdf 取决于)的问题,因为我在这里做了一些阅读:https://engineering.fundingcircle.com/blog/2015/04/09/aws-lambda-for-great-victory/,但是,由于 Lambda 已将最大 zip 大小提高到 50mb,我不上传二进制文件时遇到问题。
有什么想法吗?
html-pdf 在后台使用 phantomjs,安装时需要编译一些二进制文件。我猜你的问题是你正在部署那些本地编译的二进制文件,但 Lambda 需要在 Amazon Linux.
上编译的二进制文件您可以通过在 运行 Amazon Linux 的 EC2 实例上构建部署包来解决此问题,然后例如直接从那里部署它,就像在 this tutorial.
中解释的那样另请查看