本篇教程,我们将共同了解如何将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
$ npm install -g serverless
$ serverless create --template aws-nodejs
复制代码
创建 Lambda 函数:
$ mkdir function/handler.js
$ cat function/handler.js
'use strict';
const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.identity = (event, context, callback) => {
const params = {
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;
}
const response = {
statusCode: 200,
body: JSON.stringify(result.Item),
};
callback(null, response);
});
};
复制代码
无服务器定义文件:
$ cat serverless.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}
复制代码
部署该栈:
$ serverless deploy --region eu-west-1
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service aws-node-get-dynamodb.zip file to S3 (7.38 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............
Serverless: Stack update finished...
Service Information
service: aws-node-get-dynamodb
stage: dev
region: eu-west-1
stack: aws-node-get-dynamodb-dev
resources: 12
api keys:
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: Run the "serverless" command to setup monitoring, troubleshooting and testing.
复制代码
现在我们的技术栈已经部署完成,接下来就是向 DynamoDB 中写入一个条目。
由于本文的重点在于迁移,因此我将哈希键硬编码至 ruan.bekker 当中,下面在 DynamoDB 中创建该条目:
$ aws dynamodb put-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 \
--lang node person \
--prefix=ruanbekker \
--gateway https://openfaas.ruan.dev
$ mv person.yml stack.yml
复制代码
在本示例中,我会将 AWS Access Keys 与 Secret Keys 创建为 OpenFaaS secrets:
$ faas-cli secret create my-aws-secret-key --from-literal="your-access-key"
$ faas-cli secret create my-aws-access-key --from-literal="your-secret-key"
复制代码
在我们的 package.json 当中提供 aws-sdk 依赖项,并借此与 AWS 进行交互:
$ cat person/package.json
{
"name": "function",
"version": "1.0.0",
"description": "",
"main": "handler.js",
"scripts": {
"test": "echo \"Error: no test specified\" && 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=$(aws lambda get-function --function-name serverless-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.js person/handler.js
复制代码
接下来,我们需要修改处理程序以纳入各 secrets 与环境变量:
$ cat person/handler.js
'use strict';
const fs = require('fs');
const secretAK = "/var/openfaas/secrets/my-aws-access-key";
const secretSK = "/var/openfaas/secrets/my-aws-secret-key";
const accessKey = fs.readFileSync(secretAK, "utf-8");
const secretKey = fs.readFileSync(secretSK, "utf-8");
const AWS = require('aws-sdk');
AWS.config.update({
credentials: new AWS.Credentials ({
region: process.env.AWS_REGION,
accessKeyId: accessKey,
secretAccessKey: secretKey
})
})
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports = (context, callback) => {
const params = {
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;
}
const response = result.Item;
callback(null, response);
});
};
复制代码
部署 OpenFaaS 函数:
$ export OPENFAAS_URL=https://openfaas.ruan.dev
$ faas-cli up
Deploying: person.
Deployed. 202 Accepted.
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/
评论