serverless教程四:使用AWS Elasticsearch/OpenSearch


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

serverless教程

在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');

文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !
  目录