
AWS CDKでAmazon Elasticsearch Serviceのドメイン(クラスタ)を作ってみた
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
AWS CDKで、Amazon Elasticsearch Serviceのドメイン(クラスタ)を作ってみました。 シンプルにシングルノードでElasticsearchクラスタを作っていますが、設定値を少し変えれば、複数台の専用マスターノードとデータノードからなるクラスタを構築できます。
今回のソースコードはこちらで公開しています。 https://github.com/shoito/aws-cdk-elasticsearch
バージョン情報
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G103 $ cdk --version 1.14.0 (build 261a1bf) $ node --version v10.15.3
プロジェクトの設定
プロジェクトのファイルは以下のようになります。
$ 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スタックに渡すためのものです。
{
"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",
"deploy": "cdk deploy -c stage=dev -c sourceIp=`curl -s https://checkip.amazonaws.com`",
"destroy": "cdk destroy -c stage=dev"
},
...省略
}
次にcdk.jsonですが、コンテキスト変数を用いて、Elasticsearch Serviceのノード数などのパラメータを定義しています。 後ほど紹介するindex.tsのコード内から、こちらで定義されている値を利用します。 devやprodなど、ステージ毎に、Elasticsearch Serviceをカスタマイズしています。
{
"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インターフェースを定義しています。
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 インスタンスタイプは、保管時のデータの暗号化をサポートしていない
// https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/aes-supported-instance-types.html
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分程度かかりました。
$ 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で間接的に上記コマンドを呼んでいます。
$ 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クラスタを構築する情報が少なかったので、誰かの参考になればと思います。








