最近はやっているTerraformを利用して、AWS Elasticsearchを自動構築してみたので、その内容をまとめたいと思います。
ややこしいことはいいから、ソースコードよこせって方はこちらになります。
Kibanaを外部に公開するためにCognito認証を有効にして、Kibanaをインターネット経由でアクセスできるように構築しました。。
Elasticsearchへのindex登録についてはPrivate Subnet経由のみ可能になっています。
インフラ構成
各種AWSリソースの構成は、以下のようになります。
VPC
VPCはアプリケーションリソースを配置するネットワーク空間であり、Network Rangeの指定によって配置できるインスタンスの数が制限されます。
今回は、"10.0.0.0/16"にてVPCを設定します。
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "true"
enable_classiclink = "false"
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${var.PREFIX}-${var.ENV}-VPC"))
)
}
Subnet
外部からアクセスを制御するネットワーク帯を分けるため、SubnetをPrivate/Publicに作成します。
Public Subnet内のインスタンスにはインターネット経由でアクセスをすることができます。
また、本来はマルチAZまたがるSubnetを作成し、各種インスタンスを冗長化させるのが一般的ですが、今回はシングルインスタンスでの構築のためAZは"a"のみを利用しています。
// public subnet
resource "aws_subnet" "nginx_public_0" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = var.AZ[0]
map_public_ip_on_launch = true
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${var.PREFIX}-${var.ENV}-PUBLIC-0-SN"))
)
}
// private subnet
resource "aws_subnet" "es_private_0" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = var.AZ[0]
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${var.PREFIX}-${var.ENV}-PRIVATE-0-SN"))
)
}
Security Group
Security Groupを作成し、「IP/SG単位でのアクセスコントロール」「Protocol/TCP Port単位でのアクセスコントロール」を行います。
項目 | 値 |
elasticsearch security group | Inbound 443 from nginx & spring security group 443 from nginx & spring security group |
nginx security group | Inbound 22 from 0.0.0.0/0 443 from 0.0.0.0/0 |
spring security group | Inbound 22 from 0.0.0.0/0 |
resource "aws_security_group" "nginx_sg" {
name = "${var.PREFIX}-${var.ENV}-NGINX-SG"
description = "Nginx security group"
vpc_id = var.VPC_ID
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${var.PREFIX}-${var.ENV}-NGINX-SG"))
)
}
resource "aws_security_group" "spring_sg" {
name = "${var.PREFIX}-${var.ENV}-SPRING-SG"
description = "Spring security group"
vpc_id = var.VPC_ID
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${var.PREFIX}-${var.ENV}-SPRING-SG"))
)
}
resource "aws_security_group" "es_sg" {
name = "${var.PREFIX}-${var.ENV}-ES-SG"
description = "Elasticsearch security group"
vpc_id = var.VPC_ID
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${var.PREFIX}-${var.ENV}-ES-SG"))
)
}
resource "aws_security_group_rule" "nginx_sg_internet_ssh_rule" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
security_group_id = aws_security_group.nginx_sg.id
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "nginx_sg_internet_https_rule" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
security_group_id = aws_security_group.nginx_sg.id
cidr_blocks = ["0.0.0.0/0"]
}
resource "aws_security_group_rule" "spring_sg_nginx_ssh_rule" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
security_group_id = aws_security_group.spring_sg.id
source_security_group_id = aws_security_group.nginx_sg.id
}
resource "aws_security_group_rule" "es_sg_spring_https_rule" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
security_group_id = aws_security_group.es_sg.id
source_security_group_id = aws_security_group.spring_sg.id
}
resource "aws_security_group_rule" "es_sg_nginx_https_rule" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
security_group_id = aws_security_group.es_sg.id
source_security_group_id = aws_security_group.nginx_sg.id
}
Elasticsearch
SecElasticsearch/Kibanaについては、上記で設定した内容を利用した設定を進める。
Cognito認証を利用する設定をしているが、その点における設定については、Githubのソースコードをご確認ください。
resource "aws_elasticsearch_domain" "es" {
domain_name = "${lower(var.PREFIX)}-elasticsearch"
elasticsearch_version = "7.10"
cluster_config {
instance_type = var.ES_INSTANCE
}
ebs_options {
ebs_enabled = true
volume_type = "gp2"
volume_size = var.ES_VOLUME_GB
}
vpc_options {
subnet_ids = var.SUBNET_IDS
security_group_ids = var.SECURITY_GROUPS
}
advanced_options = {
"rest.action.multi.allow_explicit_index" = "true"
}
access_policies = data.aws_iam_policy_document.es_access_policy.json
cognito_options {
enabled = true
user_pool_id = lookup(var.COGNITO_MAP, "user_pool")
identity_pool_id = lookup(var.COGNITO_MAP, "identity_pool")
role_arn = aws_iam_role.cognito_es_role.arn
}
domain_endpoint_options {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
encrypt_at_rest {
enabled = var.ES_ENCRYPTION
}
snapshot_options {
automated_snapshot_start_hour = 23
}
tags = merge(
var.DEFAULT_TAGS,
map("Name", lower("${lower(var.PREFIX)}-${lower(var.ENV)}-elastic-search"))
)
depends_on = [aws_iam_service_linked_role.es, aws_iam_role_policy_attachment.cognito_es_attach]
}
Logstash(EC2インスタンス)
こちらはAWSリソースの設定というより、Logstashを利用してElasticsearchにログを連携する方法を紹介する。
DockerfileにてLogstashを構築しますが、その中でLogstashのlogstash-output-amazon-esプラグインをインストールします。
FROM docker.elastic.co/logstash/logstash:7.7.0
RUN logstash-plugin install logstash-output-amazon_es
そして、Logstashのpipelineを定義を決めるわけですが、outputの中では、ROLE権限を利用するためにamazon_esのディレクティブを利用する必要があります。
利用しない場合は、403のAccess Deniedのエラーが発生してしまします。
input {
tcp {
mode => "server"
host => "0.0.0.0"
port => 5044
}
}
filter {
json {
source => "message"
}
}
output {
amazon_es {
hosts => "${ELASTICSEARCH_HOST}"
index => "${LOG_INDEX_NAME}"
region => "ap-northeast-1"
}
}
まとめ
ここまでソースコードを利用しながら説明しましたが、実際のソースコードを利用したい場合は、こちらのソースコードを利用してみてください。
以上、TerraformでElasticsearchを構築して各種サービスの管理に役立てていただければと思います。
コメント
Great content! Keep up the good work!