如何将 AWS Node.js Lambda 函数迁移至 OpenFaaS?
本篇教程,我们将共同了解如何将 AWS Lambda 函数(Node.js)迁移至 OpenFaaS。
为什么要迁移至 OpenFaas?
云函数服务确实优点很多,不仅成本低廉,而且适合大部分用例的实际需求。但在另一方面,OpenFaaS 相较于云函数服务也拥有不少独特优势。
下面,我先聊聊自己在使用 OpenFaaS 中的具体感受:
- 可以在自有基础设施上托管函数以满足本地化标准。
- 确保函数托管在符合用例特性的资源之上(包括 CPU、内存以及 GPU 密集型任务)。
- 可以使用现有 Kubernetes 或者 Docker Swarm 集群部署 OpenFaaS。
- 对 TTL 没有任何限制,可以长期保持函数运行。
- 确保用户不致锁定于特定云服务供应商处。
- 拥有一整套函数库以及为其提供贡献的活跃社区,能够为项目提供巨大助益。
- 默认提供自动规模伸缩功能。
- 支持一系列编程语言选项,甚至能够使用 bash 脚本,极大提升使用体验!
- 极易学习,而且使用感受也非常友好。
- Cli 客户端与 faas-cil 的存在又让 OpenFaaS 的使用难度进一步降低。
- Grafana、Prometheus 以及 ALertManager 可在框架中开箱即用,允许大家轻松查看函数指标并设置警报机制。
根据实际体验,我之前已经建立起一套 Docker Swarm 集群,其中的资源由云服务供应商管理,同时拥有监控、高可用性以及自我修复机制。
现在,我可以在这套集群设置之上使用 OpenFaaS,而且完美匹配实际用例。
架构
终极目标是将 AWS Lambda Function 迁移至 OpenFaaS:
应用程序
我们在 AWS 中的无服务器应用程序包含 API 网关、DynamoDB 以及 Lambda(Node.js)。
在示例中,我会尽量控制应用程序的复杂度,因此其功能非常简单:当我在 API 网关资源上发出 GET 请求时,在 DynamoDB 表上执行 GetItem。
在这种情况下,我将哈希键值硬编码至 ruan.bekker 中。
整个流程如下所示:
复制代码
-> API: /dev/person, -> Lambda calls DynamoDB: {"id":"ruan.bekker"}, -> Response: {"id":"ruan.bekker","name":"ruan", ...}
AWS 设置
为了完全透明,我将使用无服务器方式设置整个 AWS 栈:
复制代码
$mkdir-p ~/dev/aws-node-get-dynamodb\ &&cd~/dev/aws-node-get-dynamodb $npminstall-gserverless $serverlesscreate--templateaws-nodejs
创建 Lambda 函数:
复制代码
$ mkdirfunction/handler.js $catfunction/handler.js 'usestrict'; constAWS=require('aws-sdk'); constdynamoDb =newAWS.DynamoDB.DocumentClient(); module.exports.identity =(event, context, callback) =>{ constparams = { TableName: process.env.DYNAMODB_TABLE, Key: { id:'ruan.bekker', }, }; dynamoDb.get(params,(error, result) =>{ if(error) { console.error(error); callback(null, { statusCode: error.statusCode ||501, headers: {'Content-Type':'text/plain'}, body:'GetItem Failed', }); return; } constresponse = { statusCode:200, body:JSON.stringify(result.Item), }; callback(null, response); }); };
无服务器定义文件:
复制代码
$catserverless.yml service:aws-node-get-dynamodb frameworkVersion:">=1.1.0 <2.0.0" provider: name:aws runtime:nodejs10.x environment: DYNAMODB_TABLE:my-dynamodb-table iamRoleStatements: - Effect:Allow Action: - dynamodb:GetItem Resource:"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}" functions: get: handler:functions/handler.identity events: - http: path:person method:get cors:true resources: Resources: TodosDynamoDbTable: Type:'AWS::DynamoDB::Table' DeletionPolicy:Retain Properties: AttributeDefinitions: - AttributeName:id AttributeType:S KeySchema: - AttributeName:id KeyType:HASH ProvisionedThroughput: ReadCapacityUnits:1 WriteCapacityUnits:1 TableName:${self:provider.environment.DYNAMODB_TABLE}
部署该栈:
复制代码
$serverlessdeploy--regioneu-west-1 Serverless:Packagingservice... Serverless:Excludingdevelopmentdependencies... Serverless:UploadingCloudFormationfiletoS3... Serverless:Uploadingartifacts... Serverless:Uploadingserviceaws-node-get-dynamodb.zipfiletoS3(7.38MB)... Serverless:Validatingtemplate... Serverless:UpdatingStack... Serverless:CheckingStackupdateprogress... .............. Serverless:Stackupdatefinished... ServiceInformation service:aws-node-get-dynamodb stage:dev region:eu-west-1 stack:aws-node-get-dynamodb-dev resources:12 apikeys: None endpoints: GET-https://xx.execute-api.eu-west-1.amazonaws.com/dev/person functions: get:aws-node-get-dynamodb-dev-get layers: None Serverless:Runthe"serverless"commandtosetupmonitoring,troubleshootingandtesting.
现在我们的技术栈已经部署完成,接下来就是向 DynamoDB 中写入一个条目。
由于本文的重点在于迁移,因此我将哈希键硬编码至 ruan.bekker 当中,下面在 DynamoDB 中创建该条目:
复制代码
$ aws dynamodbput-item\ --table-name my-dynamodb-table --item \ ' { "id": {"S":"ruan.bekker"}, "name": {"S":"ruan"}, "surname": {"S":"bekker"}, "country": {"S":"south africa"}, "age": {"N":"32"} }
发送一条指向该 API 网关 URL 的 GET 请求:
复制代码
$ curl https://xx.execute-api.eu-west-1.amazonaws.com/dev/person {"id":"ruan.bekker","surname":"bekker","name":"ruan","country":"south africa","age":32}
可以看到,现在我们已经能够在 DynamoDB 中检索到该条目。
设置 OpenFaaZS 函数
创建一个新的 Node.js OpenFaaS 函数(请注意,设置当中使用了镜像前缀与网关 url,如下所示):
复制代码
$ mkdir -p ~/dev/lambda-to-openfaas-migration\ &&cd~/dev/lambda-to-openfaas-migration $ faas-cli new \ --langnode person \ --prefix=ruanbekker\ --gatewayhttps://openfaas.ruan.dev $ mv person.yml stack.yml
在本示例中,我会将 AWS Access Keys 与 Secret Keys 创建为 OpenFaaS secrets:
复制代码
$ faas-clisecretcreate my-aws-secret-key--from-literal="your-access-key" $ faas-clisecretcreate my-aws-access-key--from-literal="your-secret-key"
在我们的 package.json 当中提供 aws-sdk 依赖项,并借此与 AWS 进行交互:
复制代码
$catperson/package.json { "name":"function", "version":"1.0.0", "description":"", "main":"handler.js", "scripts": { "test":"echo \"Error:notestspecified\" && exit 1" }, "keywords": [], "author":"", "license":"ISC", "dependencies": { "aws-sdk":"latest" } }
我们的栈定义:
复制代码
$ cat stack.yml provider: name:openfaas gateway:https://openfaas.ruan.dev functions: person: lang:node handler:./person image:ruanbekker/person:latest environment: content_type:application/json DYNAMODB_TABLE:my-dynamodb-table AWS_REGION:eu-west-1 secrets: - my-aws-access-key - my-aws-secret-key
我们的初始设置中仍包含 AWS Lambda 函数代码,但到这里的栈已经设置完成,而且无需任何本地复本。
下面,我们需要下载 Lambda 部署软件包:
复制代码
$ mkdir aws-lambda \ && cd aws-lambda $ lambda_url=$(awslambdaget-function--function-nameserverless-rest-api-with-dynamodb-dev-get|jq-r.Code.Location) $ curl -o deployment_package.zip"${lambda_url}"
提取该部署软件包并利用由此得到的 OpenFaaS 处理程序替换原 Lambda 函数处理程序:
复制代码
$ unzip deployment_package.zip $ cd .. $ mv aws-lambda/function/handler.jsperson/handler.js
接下来,我们需要修改处理程序以纳入各 secrets 与环境变量:
复制代码
$ cat person/handler.js 'use strict'; constfs =require('fs'); constsecretAK ="/var/openfaas/secrets/my-aws-access-key"; constsecretSK ="/var/openfaas/secrets/my-aws-secret-key"; constaccessKey = fs.readFileSync(secretAK,"utf-8"); constsecretKey = fs.readFileSync(secretSK,"utf-8"); constAWS =require('aws-sdk'); AWS.config.update({ credentials:newAWS.Credentials ({ region: process.env.AWS_REGION, accessKeyId: accessKey, secretAccessKey: secretKey }) }) constdynamoDb =newAWS.DynamoDB.DocumentClient(); module.exports =(context, callback) =>{ constparams = { TableName: process.env.DYNAMODB_TABLE, Key: { id:'ruan.bekker', }, }; dynamoDb.get(params, (error, result) => { if(error) { console.error(error); callback(null, { statusCode: error.statusCode ||501, headers: {'Content-Type':'text/plain'}, body:'GetItem Failed', }); return; } constresponse = result.Item; callback(null, response); }); };
部署 OpenFaaS 函数:
复制代码
$ export OPENFAAS_URL=https://openfaas.ruan.dev $ faas-cli up Deploying:person. Deployed.202Accepted. URL:https://openfaas.ruan.dev/function/person
现在,我们需要在 OpenFaaS API 网关 URL 上通过 GET 请求测试新创建的函数:
复制代码
$ curl https://openfaas.ruan.dev/function/person {"id":"ruan.bekker","surname":"bekker","name":"ruan","country":"south africa","age":32}
搞定,现在我们已经将 AWS Lambda Function 迁移至 OpenFaaS。
原文链接:
https://sysadmins.co.za/migrate-your-aws-node-js-lambda-function-to-openfaas/