How to Set Up Hazelcast IMDG on AWS ECS
Optimize your relational database with this tutorial for installing and configuring open source Hazelcast IMDG.
Join the DZone community and get the full member experience.
Join For FreeNote: This blog was originally published in December 2018, but has since been updated with the latest information.
Amazon ECS (Elastic Container Service) is a container orchestration service that supports Docker containers. Hazelcast, starting from version 4.0.2, fully supports the AWS ECS environment in both models: AWS Fargate and EC2. Thanks to the Hazelcast AWS plugin, with just a few steps, you can set up your Hazelcast cluster. This post presents this process step-by-step and the source code for this example can be found here.
TL;DR
Below is the simplest Hazelcast configuration (hazelcast.yaml
file) you can use for the automatic AWS ECS discovery.
hazelcast: network: join: multicast: enabled: false aws: enabled: true interfaces: enabled: true interfaces: - 10.0.*.*
A few comments:
- make sure to change
10.0.*.*
to the CIDR of your VPC network - make sure the IAM Role attached to your task has the following permissions:
ecs:ListTasks
ecs:DescribeTasks
ec2:DescribeNetworkInterfaces
(needed only for public IP discovery)
- you can use two parameters in the
aws
part to narrow down the discovery:family
service-name
- if you use Hazelcast embedded into your application, make sure you use
hazelcast-all
version >4.0.2
You can read more about Hazelcast AWS ECS discovery in the official documentation.
Supported Features
Hazelcast AWS ECS integration supports the following features:
- Dynamic auto-discovery – Hazelcast automatically discovers other members to form one cluster
- Zone Aware – Hazelcast automatically stores its partition backups on a member that is located in a different availability zone, so in case of AWS zone failure, you don’t encounter any data loss
- Fargate and EC2 – Hazelcast works correctly no matter if your ECS cluster is used in the Fargate or EC2 mode
- Autoscaling – Hazelcast works correctly if you scale up and down the instances and, assuming you configured health checks, you will never encounter any data loss
Step-by-step Guide
Hazelcast can be used in the embedded and client-server topologies. Both are well supported by the Hazelcast AWS plugin. Starting from scratch, you’ll need the following steps to create a Hazelcast cluster in the ECS environment.
- Define Hazelcast ECS Configuration
- Use Hazelcast Configuration
- Create ECS Cluster
- Create Security Group
- Create IAM Role
- Configure CloudWatch
- Create Task Definition
- Create ECS Service
- Tear Down
Note that in this guide, we’ll use Fargate as the backend, but in the same way you can configure ECS with EC2.
Define Hazelcast ECS Configuration
To enable Hazelcast AWS ECS discovery, you need to have hazlecast-aws
on your classpath, so:
- if you deploy Hazelcast embedded, make sure you have
hazelcast-all
version >4.0.2
in your dependencies - if you deploy Hazelcast server, make sure you use Hazelcast Docker image
hazelcast/hazelcast
version >4.0.2
Then, here’s the minimal hazelcast.yaml
configuration you can use:
hazelcast: network: join: multicast: enabled: false aws: enabled: true interfaces: enabled: true interfaces: - 10.0.*.*
Note that you need to change 10.0.*.*
to the VPC you plan to use for your ECS Tasks. To list all your available VPCs, execute the following command.
aws ec2 describe-vpcs --query 'Vpcs[*].[VpcId,CidrBlock]' [ [ "vpc-0ae005a4a2835bbb5", "172.18.0.0/16" ], [ "vpc-0681043d6f49b039a", "10.0.0.0/16" ] ]
For the next steps, let’s assume that we always use the VPC with ID vpc-0681043d6f49b039a
(CIDR vpc-10.0.0.0/16
). For the sake of simplicity, let’s put it as an environment variable:
export VPC_ID='vpc-0681043d6f49b039a'
Now, you need to make sure Hazelcast uses this configuration.
Use Hazelcast Configuration
AWS ECS does not support any simple way of injecting configuration files into your container. In Kubernetes, you could define ConfigMap, but unfortunately, such a concept does not exist in ECS.
If you use Hazelcast embedded, then you can define Hazelcast configuration inside your application container. You can see an example of how to do it here. If you just want to play now, you can use the already built image hazelcastguides/hazelcast-embedded-ecs
.
If you want to set up a Hazelcast server, then you have two options:
- build your own Hazelcast Docker image with Hazelcast configuration baked-in (as described here)
- use AWS EFS File System (as described here):
- create AWS EFS Volume
- copy
hazelcast.yaml
- mount EFS in your ECS Task Definition
- specify env variable
JAVA_OPTS=-Dhazelcast.config=<hz-config-file-path>
If you use Hazelcast in client-server mode, then you can use the following ECS Hazelcast Client Configuration inside your application to connect to your cluster.
No matter which option you chose, you should now have a Docker image with Hazelcast which you can use in further steps.
Create ECS Cluster
Assuming you have an AWS Account and AWS command line tool configured, you can create an ECS cluster with the following command.
aws ecs create-cluster --cluster-name test-cluster
We will use this cluster for all further steps in this guide.
Create Security Group
By default, Hazelcast uses port 5701 for all the communication. Therefore, we need to create an appropriate security group.
aws ec2 create-security-group \ --group-name hazelcast-security-group \ --description "Hazelcast Security Group" \ --vpc-id ${VPC_ID} { "GroupId": "sg-0cf1116753b96cdce" }
Let’s define the security group id as an environment variable.
export SECURITY_GROUP_ID='sg-0cf1116753b96cdce'
Next, we need to open port 5701 for TCP communication.
aws ec2 authorize-security-group-ingress \ --group-id ${SECURITY_GROUP_ID} \ --protocol tcp \ --port 5701 \ --cidr 0.0.0.0/0
That is enough for Hazelcast communication.
Create IAM Role
Hazelcast ECS Discovery uses AWS API to discover Hazelcast members. That is why we need to grant certain permissions.
First, create a file policy.json
.
x
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:DescribeNetworkInterfaces",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ecs:ListTasks",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ecs:DescribeTasks",
"Resource": "*"
}
]
}
Then, we can create an IAM Role we will later use by ECS tasks.
xxxxxxxxxx
aws iam create-policy \
--policy-name hazelcast-ecs-policy \
--policy-document file://policy.json
{
"Policy": {
"PolicyName": "hazelcast-ecs-policy",
"PolicyId": "ANPAZV4HIPQ4QUIH45BXB",
"Arn": "arn:aws:iam::665466731577:policy/hazelcast-ecs-policy",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"CreateDate": "2020-06-29T09:52:05Z",
"UpdateDate": "2020-06-29T09:52:05Z"
}
}
Let’s define this policy ARN as an environment variable.
export POLICY_ARN='arn:aws:iam::665466731577:policy/hazelcast-ecs-policy'
Next, let’s define a new IAM Role. We need a file role-policy.json
.
xxxxxxxxxx
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Then, we can execute the following command.
xxxxxxxxxx
aws iam create-role \
--role-name hazelcast-ecs-role \
--assume-role-policy-document file://role-policy.json
{
"Role": {
"Path": "/",
"RoleName": "hazelcast-ecs-role",
"RoleId": "AROAZV4HIPQ47NJGCHJ2A",
"Arn": "arn:aws:iam::665466731577:role/hazelcast-ecs-role",
"CreateDate": "2020-06-29T09:58:06Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
}
}
Define an environment variable with the role ARN.
export TASK_ROLE_ARN='arn:aws:iam::665466731577:role/hazelcast-ecs-role'
Finally, let’s attach the defined policy to the created role.
aws iam attach-role-policy --role-name hazelcast-ecs-role --policy-arn ${POLICY_ARN}
Configure CloudWatch
If you want to read logs from Hazelcast (and your application), then you need to create AWS CloudWatch Group. Note that this step is not required, but highly recommended. If you skip it, you also need to remove CloudWatch entry from task-definition.json
in the later step and then you won’t be able to see any logs.
To create a CloudWatch group, execute the following command.
aws logs create-log-group --log-group-name /ecs/hazelcast
To allow ECS tasks to write to the CloudWatch log group, you also need to create the following ECS role.
xxxxxxxxxx
aws iam create-role \
--role-name ecs-execution-role \
--assume-role-policy-document file://role-policy.json
{
"Role": {
"Path": "/",
"RoleName": "ecs-execution-role",
"RoleId": "AROAZV4HIPQ4SGDIWGYRK",
"Arn": "arn:aws:iam::665466731577:role/ecs-execution-role",
"CreateDate": "2020-06-29T10:44:01Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
}
}
Export this role as an environment variable.
export EXECUTION_ROLE_ARN='arn:aws:iam::665466731577:role/ecs-execution-role'
Finally, attach AmazonECSTaskExecutionRolePolicy
to the created role.
xxxxxxxxxx
aws iam attach-role-policy \
--role-name ecs-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
Create Task Definition
To create an ECS task definition, you need first to define the following task-definition.json
file.
xxxxxxxxxx
{
"family": "hazelcast",
"networkMode": "awsvpc",
"taskRoleArn": "TASK_ROLE_ARN",
"executionRoleArn": "EXECUTION_ROLE_ARN",
"containerDefinitions": [
{
"name": "hazelcast",
"image": "hazelcastguides/hazelcast-embedded-ecs",
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/hazelcast",
"awslogs-region": "REGION",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "256",
"memory": "1024"
}
Note that you need to modify the following parts:
TASK_ROLE_ARN
– ARN of the role created in the “Create IAM Role” stepEXECUTION_ROLE_ARN
– ARN of the Role created in the “Configure AWS CloudWatch” stepREGION
– the region that you use for your CloudWatch and ECS Cluster- (optionally)
image
– Docker image you created in the previous step
Then, you can create the task definition with the following command.
aws ecs register-task-definition --cli-input-json file://task-definition.json
Create ECS Service
Having ECS Task Definition specified, we can finally create ECS Tasks or ECS Service (which in turn creates ECS tasks). First, choose the subnet in which your service should operate.
xxxxxxxxxx
aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=${VPC_ID}" \
--query 'Subnets[*].[SubnetId,CidrBlock]'
[
[
"subnet-0f042c997bad8e2b9",
"10.0.1.0/24"
]
]
export SUBNET_ID='subnet-0f042c997bad8e2b9'
Then, create a service with 3 application replicas.
xxxxxxxxxx
aws ecs create-service --cluster test-cluster \
--service-name hazelcast \
--task-definition hazelcast \
--launch-type=FARGATE \
--network-configuration "awsvpcConfiguration={subnets=["${SUBNET_ID}"],securityGroups=["${SECURITY_GROUP_ID}"],assignPublicIp=ENABLED}" \
--desired-count 3
You can check that the related tasks were created. If you don’t see any tasks, then wait a moment and check the tasks again.
xxxxxxxxxx
aws ecs list-tasks --cluster test-cluster --service hazelcast
{
"taskArns": [
"arn:aws:ecs:eu-central-1:665466731577:task/2154b675-df19-459e-a95e-8466f5d4bb59",
"arn:aws:ecs:eu-central-1:665466731577:task/7c3adb1d-6850-4f86-9eb1-7eed5bc75235",
"arn:aws:ecs:eu-central-1:665466731577:task/99de6977-949d-489c-9c5a-44c763edfaaf"
]
}
Finally, you can also examine the logs of one of the tasks and you should see that the Hazelcast cluster was successfully formed.
xxxxxxxxxx
aws logs get-log-events \
--log-group-name /ecs/hazelcast \
--log-stream-name ecs/hazelcast/2154b675-df19-459e-a95e-8466f5d4bb59 \
--output text --query 'events[*].[message]'
...
Members {size:3, ver:3} [
Member [10.0.1.4]:5701 - dae44d3d-882b-4f3a-aff3-09721b737276
Member [10.0.1.6]:5701 - 3874314e-30f9-4f10-9f7d-11a0a32dc16b
Member [10.0.1.220]:5701 - 9009d19a-63b1-4e90-b4de-48e877bb7086 this
]
...
Tear Down
To delete the service, execute the following commands.
xxxxxxxxxx
aws ecs update-service \
--cluster test-cluster \
--service hazelcast \
--desired-count 0
aws ecs delete-service \
--cluster test-cluster \
--service hazelcast
To delete all other resources we created in the post, execute the following commands.
xxxxxxxxxx
aws ecs deregister-task-definition --task-definition hazelcast:1
aws iam detach-role-policy \
--role-name ecs-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
aws iam delete-role --role-name ecs-execution-role
aws logs delete-log-group --log-group-name /ecs/hazelcast
aws iam detach-role-policy --role-name hazelcast-ecs-role --policy-arn ${POLICY_ARN}
aws iam delete-role --role-name hazelcast-ecs-role
aws iam delete-policy --policy-arn ${POLICY_ARN}
aws ec2 delete-security-group --group-id ${SECURITY_GROUP_ID}
aws ecs delete-cluster --cluster test-cluster
Conclusion
Hazelcast supports automatic member discovery in AWS ECS for both models: AWS Fargate and EC2. You can also use it in embedded and client-server topologies. For anyone interested in more details I recommend reading the Hazelcast AWS documentation.
Published at DZone with permission of Rafał Leszko. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments