30

如何将 AWS Node.js Lambda 函数迁移至 OpenFaaS?

 4 years ago
source link: https://www.tuicool.com/articles/ZrUn2u6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本篇教程,我们将共同了解如何将 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:

B3yemuU.png!web

应用程序

我们在 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/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK