在开发无服务器应用的时候,通过纯手工方式创建各种资源的话,效率会非常低,因此需要借助一些框架来提高开发效率。下面介绍如何在serverless中创建并使用AWS Elasticsearch/AWS OpenSearch Service。

在serverless中定义AWS Elasticsearch
首先在serverless.yml中添加:
service: service-04
provider:
name: aws
region: eu-west-1
runtime: nodejs14.x
lambdaHashingVersion: 20201221
profile: default
resources:
Resources:
elasticSearch:
Type: AWS::Elasticsearch::Domain
Properties:
DomainName: my-es-instance
EBSOptions:
EBSEnabled: true
VolumeType: gp2
VolumeSize: 10
ElasticsearchClusterConfig:
InstanceType: t2.small.elasticsearch
InstanceCount: 1
DedicatedMasterEnabled: false
ZoneAwarenessEnabled: false
ElasticsearchVersion: 7.10
然后运行如下命令就会创建AWS Elasticsearch domain:
sls deploy
在serverless中定义AWS OpenSearchService
service: service-04
provider:
name: aws
region: eu-west-1
runtime: nodejs14.x
lambdaHashingVersion: 20201221
profile: default
resources:
Resources:
openSearch:
Type: AWS::OpenSearchService::Domain
Properties:
DomainName: my-os-instance
EBSOptions:
EBSEnabled: true
VolumeType: gp2
VolumeSize: 10
ClusterConfig:
InstanceType: t2.small.search
InstanceCount: 1
DedicatedMasterEnabled: false
ZoneAwarenessEnabled: false
EngineVersion: "OpenSearch_1.2"
AccessPolicies:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
AWS: "*"
Action: "es:*"
Resource: "arn:aws:es:eu-west-1:454469586046:domain/*"
Condition:
IpAddress:
aws:SourceIp:
- "YOUR_IP_V4_ADDRESS/16",
- "[YOUR_IP_V6_ADDRESS]/48",
在Lambda中访问OpenSearch
由于ElasticSearch / OpenSearch自身不带认证/授权功能,因此如果自己在一个VM中配置ES/OS的话,可以直接通过官网API进行访问。但一旦部署到AWS中,AWS会在OpenSearch上添加额外的认证功能。这也是为什么在默认配置下,外网无法直接访问OS的原因。
如果想要从外网访问OS,可以添加IP来实现。
但如果要在Lambda中访问OS的话,就需要定义其访问权限了。详细的讨论请参考:
serverless.yml
provider:
name: aws
runtime: nodejs14.x
stage: ${opt:stage,'dev'}
region: eu-west-1
iam:
role:
statements:
- Effect: Allow
Action:
- es:ESHttpPost
- es:ESHttpPut
- es:ESHttpDelete
- es:ESHttpPatch
- es:ESHttpGet
Resource:
- "arn:aws:es:${self:provider.region}:*:domain/*"
如果在部署Lambda的时候选择了单独打包,则需要在专门的Search API定义中指定依赖库:
search:
handler: src/functions/Search.handler
package:
patterns:
- 'node_modules/@aws-sdk/credential-provider-ini/**'
- 'node_modules/@aws-sdk/credential-provider-node/**'
- 'node_modules/@aws-sdk/credential-provider-process/**'
- 'node_modules/@aws-sdk/is-array-buffer/**'
- 'node_modules/@aws-sdk/util-base64-node/**'
- 'node_modules/@aws-sdk/util-body-length-node/**'
- 'node_modules/@aws-sdk/util-buffer-from/**'
- 'node_modules/@aws-sdk/util-config-provider/**'
- 'node_modules/@aws-sdk/util-utf8-node/**'
- 'node_modules/@aws-sdk/util-uri-escape/**'
- 'node_modules/@opensearch-project/opensearch/**'
- 'node_modules/aws4/**'
- 'node_modules/aws-opensearch-connector/**'
- 'node_modules/debug/**'
- 'node_modules/hpagent/**'
- 'node_modules/ms/**'
- 'node_modules/secure-json-parse/**'
- 'node_modules/tslib/**'
- 'node_modules/uuid/**'
description: 'Search'
memorySize: 256
events:
- http:
path: api/search
method: post
cors: true
TypeScript代码
import { Client } from '@opensearch-project/opensearch';
import { defaultProvider } from '@aws-sdk/credential-provider-node';
import createAwsOpensearchConnector from 'aws-opensearch-connector';
let client: Client;
const OpenSearch = {
async init() {
const awsCredentials = await defaultProvider()();
const connector = createAwsOpensearchConnector({
credentials: awsCredentials,
region: process.env.region ?? 'eu-west-1',
getCredentials: (cb: () => void) => cb(),
});
client = new Client({
...connector,
node: searchUrl,
});
},
async facetSearch(query: unknown) {
if (!client) {
await this.init();
}
const response = await client.search({
index: searchIndex,
body: {
query,
aggs: Facets,
},
});
return response.body;
},
// ...
}
如果提示未声明aws-opensearch-connector.d.ts的话,可以将这行:
import createAwsOpensearchConnector from 'aws-opensearch-connector';
替换为:
// eslint-disable-next-line @typescript-eslint/no-var-requires
const createAwsOpensearchConnector = require('aws-opensearch-connector');