AWS CDKでAmazon Elasticsearch Serviceのドメイン(クラスタ)を作ってみた
はじめに
AWS CDKで、Amazon Elasticsearch Serviceのドメイン(クラスタ)を作ってみました。
シンプルにシングルノードでElasticsearchクラスタを作っていますが、設定値を少し変えれば、複数台の専用マスターノードとデータノードからなるクラスタを構築できます。
今回のソースコードはこちらで公開しています。
https://github.com/shoito/aws-cdk-elasticsearch
バージョン情報
1 2 3 4 5 6 7 8 9 10 | $ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G103 $ cdk --version 1.14.0 (build 261a1bf) $ node --version v10.15.3 |
プロジェクトの設定
プロジェクトのファイルは以下のようになります。
1 2 3 4 5 6 7 | $ tree -L 1 . ├── cdk.json ├── index.ts ├── package-lock.json ├── package.json └── tsconfig.json |
package.jsonのハイライト部分ですが、synth, deploy, destroyを簡単に実行できるようにしています。 -c sourceIp=`curl -s https://checkip.amazonaws.com` の部分は、手元のPCからElasticsearchやKibanaのエンドポイントにアクセスするために、CDKのContextを利用して、ローカルPCのIPアドレスをCDKスタックに渡すためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "name" : "aws-cdk-elasticsearch" , "version" : "0.1.0" , "license" : "MIT" , "scripts" : { "build" : "tsc" , "watch" : "tsc -w" , "cdk" : "cdk" , "synth" : "cdk synth --no-staging -c stage=dev -c sourceIp=`curl -s https://checkip.amazonaws.com` > template.yaml" , "destroy" : "cdk destroy -c stage=dev" }, ...省略 } |
次にcdk.jsonですが、コンテキスト変数を用いて、Elasticsearch Serviceのノード数などのパラメータを定義しています。
後ほど紹介するindex.tsのコード内から、こちらで定義されている値を利用します。
devやprodなど、ステージ毎に、Elasticsearch Serviceをカスタマイズしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | { "app" : "npx ts-node index.ts" , "context" : { "es" : { "version" : "7.1" }, "dev" : { "es" : { "domainName" : "test-dev-domain" , "instanceType" : "t2.small.elasticsearch" , "instanceCount" : 1, "volumeSize" : 10 } }, "prod" : { "es" : { "domainName" : "test-prod-domain" , ...省略 } } } |
クラスタの作成
今回は検証のため、Elasticsearchクラスタはシングルノードで構築します。
なお、マルチノード構成に必要な設定はコードコメントで残してますので、何か参考になればと。
Elasticsearch Serviceは CfnDomainクラス
を用いて作成します。
Amazon Elasticsearch Service Construct Library
https://docs.aws.amazon.com/cdk/api/latest/typescript/api/aws-elasticsearch.html
以下では、ESDomainStackクラスと上記cdk.jsonからのコンテキスト変数esの型として、ESContextインターフェースを定義しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import cdk = require( '@aws-cdk/core' ); import { CfnDomain } from '@aws-cdk/aws-elasticsearch' ; // ステージ毎に、ノード数やデータの暗号化有無などを可変にできるように、cdk.jsonのContextを用いる。 // cdk.jsonのcontextで定義しているes contextの型定義。 interface ESContext { // Elasticsearchのバージョン readonly version: string; // Elasticsearch Serviceのドメイン名(クラスタ名) readonly domainName: string; // 専用マスターノードのインスタンスタイプ readonly masterInstanceType: string; // データノードのインスタンスタイプ readonly instanceType: string; // データノードのノード数 readonly instanceCount: number; // ボリュームサイズ readonly volumeSize: number; // アベイラビリティゾーン数 readonly availabilityZoneCount: 1 | 2 | 3; // AZ分散有無 readonly zoneAwareness: boolean; // 専用マスタノードの有無 readonly dedicatedMaster: boolean; // データ暗号化の有無 readonly encryption: boolean; } class ESDomainStack extends cdk.Stack { // LambdaなどからElasticsearch Serviceエンドポイントの参照用 public endpoint: string; constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super (scope, id, props); // cdkコマンドで、-c stage=devのように、devやprodを指定する const stage: string = this .node.tryGetContext( 'stage' ); // Elasticsearchのバージョン const esVersion: string = this .node.tryGetContext( 'es' ).version; // cdk.jsonからesコンテキストのオブジェクトを取得する const esContext: ESContext = this .node.tryGetContext(stage).es; // アクセスポリシー設定のため、cdk deployコマンド実行時にパラメーターで自身のIPを渡す -c sourceIp=`curl -s https://checkip.amazonaws.com` const sourceIp: string = this .node.tryGetContext( 'sourceIp' ); const domain = new CfnDomain( this , esContext.domainName || 'domain' , { // アクセスポリシー設定 accessPolicies: { Version: '2012-10-17' , Statement: [ { Effect: 'Allow' , Principal: { AWS: [ '*' ] }, Action: [ 'es:*' ], Resource: `arn:aws:es:${cdk.Stack.of( this ).region}:${cdk.Stack.of( this ).account}:domain/${esContext.domainName}/*`, Condition: { IpAddress: { // 自身のIPからElasticsearchやKibanaのエンドポイントにアクセスできるようにする 'aws:SourceIp' : `${sourceIp || '127.0.0.1' }` } } }, { Effect: 'Allow' , Principal: { AWS: [ // 自身のAWSアカウント環境からのElasticsearchへの操作を許可する cdk.Stack.of( this ).account ] }, Action: [ 'es:*' ], Resource: `arn:aws:es:${cdk.Stack.of( this ).region}:${cdk.Stack.of( this ).account}:domain/${esContext.domainName}/*` } ] }, domainName: esContext.domainName, ebsOptions: { ebsEnabled: true , volumeSize: esContext.volumeSize, volumeType: 'gp2' , }, elasticsearchClusterConfig: { instanceCount: esContext.instanceCount, // T2 インスタンスタイプは、保管時のデータの暗号化をサポートしていない instanceType: esContext.instanceType, // 専用マスターノードを使う構成にしたい場合は以下のオプションを設定する // dedicatedMasterEnabled: true, // dedicatedMasterCount: 3, // dedicatedMasterType: esContext.masterInstanceType, // zoneAwarenessEnabled: true, // zoneAwarenessConfig: { // availabilityZoneCount: esContext.availabilityZoneCount // } }, elasticsearchVersion: esVersion, encryptionAtRestOptions: { enabled: esContext.encryption // kmsKeyId: '' }, nodeToNodeEncryptionOptions: { enabled: false }, snapshotOptions: { automatedSnapshotStartHour: 0 }, // tags: [], // vpcOptions: { // subnetIds: [], // securityGroupIds: [] // }, }); this .endpoint = domain.attrDomainEndpoint; } } const app = new cdk.App(); new ESDomainStack(app, "ESDomainStack" ); |
デプロイ
CDKスタックをビルド・デプロイします。
なお、私の環境ではデプロイに11分程度かかりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | $ npm i $ npm run build > aws-cdk-elasticsearch@0.1.0 build /Users/shoito/workspaces/aws-cdk-elasticsearch > tsc # cdk bootstrapを過去に実行していれば不要 $ cdk bootstrap -c stage=dev ⏳ Bootstrapping environment aws: //123456789/ap-northeast-1 ... CDKToolkit: creating CloudFormation changeset... 0 /1 | 17:16:36 | UPDATE_COMPLETE_CLEA | AWS::CloudFormation::Stack | CDKToolkit 1 /1 | 17:16:36 | UPDATE_COMPLETE | AWS::CloudFormation::Stack | CDKToolkit ✅ Environment aws: //123456789/ap-northeast-1 bootstrapped. $ npm run deploy > aws-cdk-elasticsearch@0.1.0 deploy /Users/shoito/workspaces/aws-cdk-elasticsearch > cdk deploy -c stage=dev -c sourceIp=`curl -s https: //checkip .amazonaws.com` ESDomainStack: deploying... ESDomainStack: creating CloudFormation changeset... 0 /3 | 17:25:17 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata 0 /3 | 17:25:17 | CREATE_IN_PROGRESS | AWS::Elasticsearch::Domain | test -dev-domain (testdevdo main) 0 /3 | 17:25:19 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata Resource creat ion Initiated 1 /3 | 17:25:19 | CREATE_COMPLETE | AWS::CDK::Metadata | CDKMetadata 1 /3 | 17:25:19 | CREATE_IN_PROGRESS | AWS::Elasticsearch::Domain | test -dev-domain (testdevdomain) Resource creation Initiated 1 /3 Currently in progress: testdevdomain 2 /3 | 17:36:23 | CREATE_COMPLETE | AWS::Elasticsearch::Domain | test -dev-domain (testdevdomain) 3 /3 | 17:36:25 | CREATE_COMPLETE | AWS::CloudFormation::Stack | ESDomainStack ✅ ESDomainStack Stack ARN: arn:aws:cloudformation:ap-northeast-1:123456789:stack /ESDomainStack/f479bb70-f700-11e9-b8c5-0e36924addac |
デプロイ確認
AWSマネジメントコンソールからAmazon Elasticsearch Serviceにアクセスします。
するとこのようにドメインやElasticsearch、Kibanaのエンドポイントなどが作られていることが確認できます。
Kibanaのエンドポイントにアクセスし、サンプルデータを読み込ませたUI
Elasticsearch Serviceドメイン設定確認画面
削除
不要になった場合は、cdk destroyコマンドでスタックを削除します。
ここでは、npm run destroyで間接的に上記コマンドを呼んでいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ npm run destroy > aws-cdk-elasticsearch@0.1.0 destroy /Users/shoito/workspaces/aws-cdk-elasticsearch > cdk destroy -c stage=dev Are you sure you want to delete: ESDomainStack (y /n )? y ESDomainStack: destroying... 0 | 18:27:58 | DELETE_IN_PROGRESS | AWS::CloudFormation::Stack | ESDomainStack User Initiated 0 | 18:27:59 | DELETE_IN_PROGRESS | AWS::Elasticsearch::Domain | test -dev-domain (testdevdomain) 0 | 18:27:59 | DELETE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata 1 | 18:28:01 | DELETE_COMPLETE | AWS::CDK::Metadata | CDKMetadata 1 Currently in progress: ESDomainStack, testdevdomain ✅ ESDomainStack: destroyed |
さいごに
CDKで、Amazon Elasticsearch Serviceのクラスタを構築してみました。
クラスタを構築するだけならそこまで手間取らなかったのですが、手元のPCから接続できるようにアクセスポリシーを設定する部分で悩みました。
今回のようにCDKでElasticsearchクラスタを構築する情報が少なかったので、誰かの参考になればと思います。