With hard-coded IPs and the right security group config, the gossip config actually becomes pretty easy. You just set the environment variable EVENTSTORE_GOSSIP_SEED=0.0.0.0:1113,0.0.0.0:1113, replacing the 0.0.0.0 with the IPs of the other two nodes.
Also side note – they say the docker image is for development only. I personally don’t see why it should be a problem to use Docker for production. In fact i encourage it since you can do things like leverage ECR’s image scanning and have closer parity between local and prod environments.
Here is the relevant parts of a CFT you might be able to draw inspiration from. Volume creation and instances are not part of it, but it’ll get you as far as the launch template.
ESSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub 'event-store-sg-${Environment}'
GroupDescription: Rules for EventStore ports
SecurityGroupIngress:
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, ExternalCidr]
Description: External https
IpProtocol: tcp
FromPort: 8113
ToPort: 8113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, ExternalCidr]
Description: SSH
IpProtocol: tcp
FromPort: 22
ToPort: 22
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz1]
Description: External http
IpProtocol: tcp
FromPort: 2113
ToPort: 2113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz2]
Description: External http
IpProtocol: tcp
FromPort: 2113
ToPort: 2113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz3]
Description: External http
IpProtocol: tcp
FromPort: 2113
ToPort: 2113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz1]
Description: External tcp
IpProtocol: tcp
FromPort: 1113
ToPort: 1113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz2]
Description: External tcp
IpProtocol: tcp
FromPort: 1113
ToPort: 1113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz3]
Description: External tcp
IpProtocol: tcp
FromPort: 1113
ToPort: 1113
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz1]
Description: Internal tcp
IpProtocol: tcp
FromPort: 1112
ToPort: 1112
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz2]
Description: Internal tcp
IpProtocol: tcp
FromPort: 1112
ToPort: 1112
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz3]
Description: Internal tcp
IpProtocol: tcp
FromPort: 1112
ToPort: 1112
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz1]
Description: Internal http
IpProtocol: tcp
FromPort: 2112
ToPort: 2112
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz2]
Description: Internal http
IpProtocol: tcp
FromPort: 2112
ToPort: 2112
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, SubnetCidrAz3]
Description: Internal http
IpProtocol: tcp
FromPort: 2112
ToPort: 2112
Tags:
- Key: component
Value: !Ref Component
- Key: environment
Value: !Ref Environment
VpcId: !FindInMap [EnvironmentMap, !Ref Environment, VpcId]
ESInstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "event-store-instance-role-${Environment}"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: ec2.amazonaws.com
Policies:
- PolicyName: !Sub "event-store-instance-policy-${Environment}"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "ecr:BatchCheck*"
- "ecr:BatchGet*"
- "ecr:Describe*"
- "ecr:Get*"
Resource: "*"
- Effect: "Allow"
Action:
- "ec2:DescribeVolume*"
Resource: "*"
- Effect: "Allow"
Action:
- "ec2:AttachVolume"
Resource: "*"
Condition:
StringEquals:
"ec2:ResourceTag/component": event-store
ESIamInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub "event-store-instance-profile-${Environment}"
Roles:
- !Ref ESInstanceRole
ESLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
DependsOn:
- ESSecurityGroup
- ESEncryptionKey
Properties:
LaunchTemplateName: !Sub "event-store-launch-template-${Environment}"
LaunchTemplateData:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 22
VolumeType: standard
DeleteOnTermination: true
ImageId: !Ref ESInstanceAMI
InstanceInitiatedShutdownBehavior: terminate
IamInstanceProfile:
Name: !Ref ESIamInstanceProfile
InstanceType: t3.small
KeyName: !Sub "event-store-ssh-key-${Environment}"
SecurityGroupIds:
- !GetAtt ESSecurityGroup.GroupId
TagSpecifications:
- ResourceType: instance
Tags:
- Key: component
Value: !Ref Component
- Key: environment
Value: !Ref Environment
UserData:
Fn::Base64:
Fn::Sub:
- |
#!/bin/bash -x
exec > /init.log 2>&1
sudo yum -y update
sudo yum -y install jq
export AWS_AZ=$(ec2-metadata --availability-zone | cut -d' ' -f 2)
export AWS_REGION=$(echo $AWS_AZ | sed 's/.$//')
export AWS_INSTANCE_ID=$(ec2-metadata --instance-id | cut -d' ' -f 2)
# Attach EventStore volume from this availability zone
export ES_DEVICE=/dev/xvdb
export ES_VOLUME_ID=$(aws ec2 --region $AWS_REGION describe-volumes --filter Name=availability-zone,Values=$AWS_AZ Name=tag-key,Values=event-store-volume-${Environment} | jq -r '.Volumes | .[] | .VolumeId')
aws ec2 --region $AWS_REGION attach-volume --device=$ES_DEVICE --instance-id=$AWS_INSTANCE_ID --volume-id=$ES_VOLUME_ID
aws ec2 --region $AWS_REGION wait volume-in-use --volume-ids $ES_VOLUME_ID
# Format EventStore volume if not already formatted
export ES_NVME_DEVICE=/dev/nvme1n1
[ $(sudo file -s $ES_NVME_DEVICE | cut -d' ' -f 2) == "data" ] && sudo mkfs -t ext4 $ES_DEVICE
# Create user for event store using same uid as in docker container
export ES_UID=105
sudo useradd -u $ES_UID -r eventstore
# Mount EventStore volume and grant permissions
sudo mkdir /data
sudo mount $ES_DEVICE /data
sudo chown eventstore:eventstore /data
sudo mkdir -p /data/data
sudo mkdir -p /data/logs/$AWS_AZ
sudo chown eventstore:eventstore /data/data
sudo chown eventstore:eventstore /data/logs
sudo chown eventstore:eventstore /data/logs/$AWS_AZ
# Install Docker
sudo yum -y install docker
sudo service docker start
sudo $(aws ecr get-login --no-include-email --region $AWS_REGION)
sudo docker pull account.dkr.ecr.us-east-2.amazonaws.com/eventstore:production
sudo docker pull account.dkr.ecr.us-east-2.amazonaws.com/eventstore-proxy:production
- {
ClusterSize: !FindInMap [EnvironmentMap, !Ref Environment, ScalingDesiredCapacity]
}
ESALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub 'event-store-alb-sg-${Environment}'
GroupDescription: Rules for EventStore load balancer ports
SecurityGroupIngress:
- CidrIp: !FindInMap [EnvironmentMap, !Ref Environment, ExternalCidr]
Description: External https
IpProtocol: tcp
FromPort: 8113
ToPort: 8113
Tags:
- Key: component
Value: !Ref Component
- Key: environment
Value: !Ref Environment
VpcId: !FindInMap [EnvironmentMap, !Ref Environment, VpcId]
ESLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "event-store-alb-${Environment}"
Scheme: internal
SecurityGroups:
- !GetAtt ESALBSecurityGroup.GroupId
Subnets: !Split [ "," , !FindInMap [EnvironmentMap, !Ref Environment, Subnets] ]
Tags:
- Key: component
Value: !Ref Component
- Key: environment
Value: !Ref Environment
Type: application
ESTargetGroupExternalHttp:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub event-store-exthttp-${Environment}
Protocol: HTTPS
Port: 8113
TargetType: ip
VpcId: !FindInMap [EnvironmentMap, !Ref Environment, VpcId]
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckProtocol: HTTPS
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 3
Matcher:
HttpCode: 200-399
UnhealthyThresholdCount: 3
Tags:
- Key: environment
Value: !Ref Environment
- Key: component
Value: !Ref Component
DependsOn: [ "ESLoadBalancer" ]
ESListenerExternalHttp:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Certificates:
- CertificateArn: !Sub "arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/a30a9fd5-b445-4895-b4d5-607c8e30135a"
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ESTargetGroupExternalHttp
LoadBalancerArn: !Ref ESLoadBalancer
Port: 8113
Protocol: HTTPS
SslPolicy: "ELBSecurityPolicy-TLS-1-2-Ext-2018-06"