In the cloud, instances are ephemeral; this is especially true if you use auto scaling. Instances will be added when demand increases or to replace a faulty instance. This can make it tricky to figure out what instances are serving what services. Netflix Eureka is a service discovery service; services will register with Eureka to announce their availability and clients will query Eureka to find which instance is hosting a service. In this article, we will go through the steps to get Eureka up and running.

Introduction

There are multiple steps that we will go through in setting up Eureka.

  1. Eureka Properties - We’ll introduce some common Eureka properties that will need to be set for clients and services.
  2. SSH Security Group - While SSH access is not necessary for production use of Eureka, we will need it for setting up the image at least.
  3. Create Image - Creating an AMI with Eureka installed on it.
  4. DNS - Eureka can either have its location hard-coded or it can be found using DNS. Using DNS is much more flexible and what we’ll be covering in this article.
  5. Eureka Security Group - We’ll create a security group for the Eureka servers.
  6. IAM Roles - Rather than hard code credentials on the image, we’ll use IAM roles for Eureka to do its interactions with AWS.
  7. Auto Scaling Group - Put Eureka in an auto scaling group to have it running all the time.
  8. Status Page - How to get the status page of the Eureka cluster.
  9. Test - There is a demo application with Eureka, we’ll use it to test the setup.
  10. Cleanup - If you decide to shutdown Eureka, we’ll see how it turn it off.
  11. Start Up Again - If you decided to shutdown Eureka and want to turn it back on.

Eureka Properties

Here are some properties we’ll set for our Eureka clients and services, whether they are set in a file or passed in from the command line.

Eureka Configuration Subset

1
2
3
4
5
6
eureka.datacenter=cloud
eureka.region=us-east-1
eureka.shouldUseDns=true
eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2
eureka.datacenter
Used to tell if we're running on AWS or not. When set to cloud it gets some AWS metadata to pass along to the Eureka server. Look at com.netflix.eureka.EurekaBootStrap, specifically initEurekaEnvironment to see it being set.
eureka.shouldUseDns
When true, rather than hard coding where the Eureka servers are, it will be looked up in DNS. This makes discovering the discovery service much easier.
eureka.region
Tell which region the instance is being run in. Used with eureka.eurekaServer.domainName.
eureka.eurekaServer.domainName
Used with eureka.region to look up the Eureka servers from DNS. It will look for a TXT record with the format txt. + eureka.region + . + eureka.eurekaServer.domainName, so in this case txt.us-east-1.eureka.example.com.
Note In this article we'll see how to set up Eureka using VPC and EC2 Classic, however there is an assumption that only one or the other will be used at a time. If you wanted to set up Eureka servers for both VPC and EC2 Classic on the same region at the same time, you'll have to use a different eureka.eurekaServer.domainName for each.

SSH Security Group

We need SSH access for creating the AMI with Eureka on it; it is optional for the instances that will actually be running Eureka. If you already have such a security group, you can skip to the next step of Create Image. If you do not have such a group, we’ll create one now.

Standard

Example API Request

1
2
3
4
5
https://ec2.us-east-1.amazonaws.com/
?Action=CreateSecurityGroup
&GroupName=ssh
&GroupDescription=ssh
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
aws --region us-east-1 ec2 \
create-security-group \
--group-name "ssh" \
--description "ssh"

Output

1
2
3
4
{
    "return": "true",
    "GroupId": "sg-1d19e276"
}

VPC

Example API Request

1
2
3
4
5
6
https://ec2.us-east-1.amazonaws.com/
?Action=CreateSecurityGroup
&GroupName=ssh
&GroupDescription=ssh
&VpcId=vpc-e322f886
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
aws --region us-east-1 ec2 \
create-security-group \
--group-name "ssh" \
--description "ssh" \
--vpc-id "vpc-e322f886"

Output

1
2
3
4
{
    "return": "true",
    "GroupId": "sg-7583d810"
}

For this example we’ll allow access to the IP range 1.2.3.4/32. Replace this with your appropriate IP range.

Note You must use an IPv4 IP range. If you do not know your IP address you can get it from What Is My IP, remember to put /32 after that to allow just that IP address.

Standard

Example API Request

1
2
3
4
5
6
7
8
https://ec2.us-east-1.amazonaws.com/
?Action=AuthorizeSecurityGroupIngress
&GroupId=sg-1d19e276
&IpPermissions.1.IpProtocol=tcp
&IpPermissions.1.FromPort=22
&IpPermissions.1.ToPort=22
&IpPermissions.1.IpRanges.1.CidrIp=1.2.3.4/32
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
aws --region us-east-1 ec2 \
authorize-security-group-ingress \
--group-id "sg-1d19e276" \
--ip-permissions '[
    {
        "IpProtocol": "tcp",
        "FromPort": 22,
        "ToPort": 22,
        "IpRanges": [
            {
                "CidrIp": "1.2.3.4/32"
            }
        ]
    }
]'

Output

1
2
3
{
    "return": "true"
}

VPC

Example API Request

1
2
3
4
5
6
7
8
https://ec2.us-east-1.amazonaws.com/
?Action=AuthorizeSecurityGroupIngress
&GroupId=sg-7583d810
&IpPermissions.1.IpProtocol=tcp
&IpPermissions.1.FromPort=22
&IpPermissions.1.ToPort=22
&IpPermissions.1.IpRanges.1.CidrIp=1.2.3.4/32
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
aws --region us-east-1 ec2 \
authorize-security-group-ingress \
--group-id "sg-7583d810" \
--ip-permissions '[
    {
        "IpProtocol": "tcp",
        "FromPort": 22,
        "ToPort": 22,
        "IpRanges": [
            {
                "CidrIp": "1.2.3.4/32"
            }
        ]
    }
]'

Output

1
2
3
{
    "return": "true"
}

Create Image

Start and Connect to a New Instance

Start a new instance. For this example we’ll use Amazon Linux.

Standard

Example API Request

1
2
3
4
5
6
7
8
9
https://ec2.us-east-1.amazonaws.com/
?Action=RunInstances
&ImageId=ami-6869aa05
&MinCount=1
&MaxCount=1
&KeyName=example
&InstanceType=c3.large
&SecurityGroupId.1=sg-1d19e276
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
aws --region us-east-1 ec2 \
run-instances \
--image-id "ami-6869aa05" \
--key-name "example" \
--instance-type "c3.large" \
--security-group-ids '["sg-1d19e276"]'

Output (Excerpt)

1
2
3
4
5
6
7
{
    "Instances": [
        {
            "InstanceId": "i-81b0196a"
        }
    ]
}

VPC

Example API Request

1
2
3
4
5
6
7
8
9
10
11
12
https://ec2.us-east-1.amazonaws.com/
?Action=RunInstances
&ImageId=ami-6869aa05
&MinCount=1
&MaxCount=1
&KeyName=example
&InstanceType=c3.large
&NetworkInterface.1.DeviceIndex=0
&NetworkInterface.1.SubnetId=subnet-98dbdfb0
&NetworkInterface.1.SecurityGroupId.1=sg-7583d810
&NetworkInterface.1.AssociatePublicIpAddress=true
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
aws --region us-east-1 ec2 \
run-instances \
--image-id "ami-6869aa05" \
--key-name "example" \
--instance-type "c3.large" \
--network-interfaces '[
    {
        "DeviceIndex": 0,
        "SubnetId": "subnet-98dbdfb0",
        "Groups": ["sg-7583d810"],
        "AssociatePublicIpAddress": true
    }
]'

Output (Excerpt)

1
2
3
4
5
6
7
{
    "Instances": [
        {
            "InstanceId": "i-81b0196a"
        }
    ]
}

Describe the instance to get the public DNS name.

Standard

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=DescribeInstances
&InstanceId.1=i-81b0196a
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
describe-instances \
--instance-ids '["i-81b0196a"]'

Output (Excerpt)

1
2
3
4
5
6
7
8
9
10
11
{
    "Reservations": [
        {
            "Instances": [
                {
                    "PublicDnsName": "ec2-54-167-207-47.compute-1.amazonaws.com"
                }
            ]
        }
    ]
}

VPC

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=DescribeInstances
&InstanceId.1=i-81b0196a
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
describe-instances \
--instance-ids '["i-81b0196a"]'

Output (Excerpt)

1
2
3
4
5
6
7
8
9
10
11
{
    "Reservations": [
        {
            "Instances": [
                {
                    "PublicDnsName": "ec2-54-167-207-47.compute-1.amazonaws.com"
                }
            ]
        }
    ]
}

SSH to the instance

Console - user@hostname ~ $

1
ssh ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com

Install Software

Install JDK, git, Tomcat, and Apache

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
yes | sudo yum install java-1.8.0-openjdk-devel git tomcat8 httpd

Make sure the to use Java 8 JRE:

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
sudo /usr/sbin/alternatives --set java /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

Make sure the to use Java 8 JDK:

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
sudo /usr/sbin/alternatives --set javac /usr/lib/jvm/java-1.8.0-openjdk.x86_64/bin/javac

Setup Configuration Files

Set JAVA_OPTS for Tomcat. In here we’ll set system properties for the specifics of the instance, like what region it is in; we’ll also tell Tomcat where to find the configuration files. We’ll do this by appending to the end of /etc/tomcat8/tomcat8.conf.

/etc/tomcat8/tomcat8.conf - (Excerpt)

1
JAVA_OPTS="-Deureka.datacenter=cloud -Deureka.region=us-east-1 -Darchaius.deployment.region=us-east-1 -Dlog4j.configuration=file:///etc/eureka/log4j.properties -Darchaius.configurationSource.additionalUrls=file:///etc/eureka/eureka-client.properties,file:///etc/eureka/eureka-server.properties"

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
2
3
cat <<'EOF' | sudo tee -a /etc/tomcat8/tomcat8.conf
JAVA_OPTS="-Deureka.datacenter=cloud -Deureka.region=us-east-1 -Darchaius.deployment.region=us-east-1 -Dlog4j.configuration=file:///etc/eureka/log4j.properties -Darchaius.configurationSource.additionalUrls=file:///etc/eureka/eureka-client.properties,file:///etc/eureka/eureka-server.properties"
EOF

Next we’ll create a directory for the configuration files we just told Tomcat about. It will be /etc/eureka

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
sudo mkdir -p /etc/eureka

We’ll create the client properties file, /etc/eureka/eureka-client.properties, and populate it with the properties that would stay the same regardless of region.

/etc/eureka/eureka-client.properties

1
2
3
4
5
6
7
8
9
eureka.environment=prod
eureka.shouldUseDns=true
eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2
eureka.port=80
eureka.name=eureka
eureka.vipAddress=eureka.example.com
eureka.preferSameZone=false

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
2
3
4
5
6
7
8
9
10
11
cat <<'EOF' | sudo tee /etc/eureka/eureka-client.properties
eureka.environment=prod
eureka.shouldUseDns=true
eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2
eureka.port=80
eureka.name=eureka
eureka.vipAddress=eureka.example.com
eureka.preferSameZone=false
EOF

Now we’ll create the server properties file, /etc/eureka/eureka-server.properties. This just has the AWS credential information for Eureka, but since we’ll be using an IAM role, the values of these properties will be blank.

/etc/eureka/eureka-server.properties

1
2
eureka.awsAccessId=
eureka.awsSecretKey=

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
2
3
4
cat <<'EOF' | sudo tee /etc/eureka/eureka-server.properties
eureka.awsAccessId=
eureka.awsSecretKey=
EOF

Finally we’ll set up the log4j configuration file, /etc/eureka/log4j.properties. In here we can set the format of the logging, what logging level to use, and where to save the logs.

/etc/eureka/log4j.properties

1
2
3
4
5
6
log4j.rootCategory=INFO,FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=/var/log/tomcat8/eureka.log
log4j.appender.FILE.layout=com.netflix.logging.log4jAdapter.NFPatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d %-5p %C:%L [%t] [%M] %m%n
log4j.logger.asyncAppenders=INFO,FILE

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
2
3
4
5
6
7
8
cat <<'EOF' | sudo tee /etc/eureka/log4j.properties
log4j.rootCategory=INFO,FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=/var/log/tomcat8/eureka.log
log4j.appender.FILE.layout=com.netflix.logging.log4jAdapter.NFPatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d %-5p %C:%L [%t] [%M] %m%n
log4j.logger.asyncAppenders=INFO,FILE
EOF

We will need mod_proxy and mod_proxy_ajp loaded, these are default on Amazon Linux. This is so we can have Apache proxy for Tomcat, and do nice things like gzip the responses from Tomcat.

/etc/httpd/conf.d/tomcat_proxy.conf

1
2
3
4
<IfModule proxy_ajp_module>
    ProxyPass / ajp://127.0.0.1:8009/
    SetOutputFilter DEFLATE
</IfModule>

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
2
3
4
5
6
cat <<'EOF' | sudo tee /etc/httpd/conf.d/tomcat_proxy.conf
<IfModule proxy_ajp_module>
    ProxyPass / ajp://127.0.0.1:8009/
    SetOutputFilter DEFLATE
</IfModule>
EOF

Build Eureka

Now we’ll work on building Eureka. First thing we need to do is clone it from GitHub.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
git clone https://github.com/Netflix/eureka.git

We’ll change directory to go into the directory we just cloned.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~ $

1
cd eureka

And now we’ll build the war file to deploy in Tomcat.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~/eureka $

1
./gradlew clean build

Install Eureka

Once it is done building we can find it in ./eureka-server/build/libs/eureka-server-eureka_version.war, in this case eureka_version is 1.4.12-SNAPSHOT. We’ll copy this war file to where Tomcat can deploy it and we’ll rename it to eureka.war.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~/eureka $

1
sudo cp ./eureka-server/build/libs/eureka-server-1.4.12-SNAPSHOT.war /var/lib/tomcat8/webapps/eureka.war

Enable Services

Have Tomcat turn on automatically when the instance is started.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~/eureka $

1
sudo chkconfig tomcat8 on

And have Apache turn on automatically when the instance is started.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~/eureka $

1
sudo chkconfig httpd on

We’ve done everything we need to do on the instance for creating the new image, so shutdown.

Console - ec2-user@ec2-54-167-207-47.compute-1.amazonaws.com ~/eureka $

1
sudo shutdown -h now

Create Image

Now that we’ve got our instance set up the way we want, let’s create our image.

Common

Example API Request

1
2
3
4
5
6
https://ec2.us-east-1.amazonaws.com/
?Action=CreateImage
&InstanceId=i-81b0196a
&Name=eureka-amazon-linux-2016-08-28-22-49-42
&Description=Eureka (Amazon Linux) - 2016-08-28T22:49:42Z
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
aws --region us-east-1 ec2 \
create-image \
--instance-id "i-81b0196a" \
--name "eureka-amazon-linux-2016-08-28-22-49-42" \
--description "Eureka (Amazon Linux) - 2016-08-28T22:49:42Z"

Output

1
2
3
{
    "ImageId": "ami-94c16cfc"
}

Terminate Instances

We that now have our Eureka image, we no longer have need for the instance we used to create it. Terminate the instance.

Common

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws/
?Action=TerminateInstances
&InstanceId.1=i-81b0196a
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
terminate-instances \
--instance-ids '["i-81b0196a"]'

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "TerminatingInstances": [
        {
            "InstanceId": "i-81b0196a",
            "CurrentState": {
                "Code": 48,
                "Name": "terminated"
            },
            "PreviousState": {
                "Code": 80,
                "Name": "stopped"
            }
        }
    ]
}

DNS

Allocate 3 Elastic IP Addresses

Standard

Example API Request

1
2
3
https://ec2.us-east-1.amazonaws.com/
?Action=AllocateAddress
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
aws --region us-east-1 ec2 \
allocate-address

Output

1
2
3
4
{
    "PublicIp": "54.174.98.162",
    "Domain": "standard"
}

Console - user@hostname ~ $

1
2
aws --region us-east-1 ec2 \
allocate-address

Output

1
2
3
4
{
    "PublicIp": "54.174.112.44",
    "Domain": "standard"
}

Console - user@hostname ~ $

1
2
aws --region us-east-1 ec2 \
allocate-address

Output

1
2
3
4
{
    "PublicIp": "54.174.112.158",
    "Domain": "standard"
}

VPC

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=AllocateAddress
&Domain=vpc
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
allocate-address \
--domain "vpc"

Output

1
2
3
4
5
{
    "PublicIp": "54.174.98.162",
    "Domain": "vpc",
    "AllocationId": "eipalloc-9ef359fb"
}

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
allocate-address \
--domain "vpc"

Output

1
2
3
4
5
{
    "PublicIp": "54.174.112.44",
    "Domain": "vpc",
    "AllocationId": "eipalloc-9cf359f9"
}

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
allocate-address \
--domain "vpc"

Output

1
2
3
4
5
{
    "PublicIp": "54.174.112.158",
    "Domain": "vpc",
    "AllocationId": "eipalloc-9ff359fa"
}

Elastic IPs:

  • 54.174.98.162
  • 54.174.112.44
  • 54.174.112.158

Tell which availability zones to use:

  • Name: txt.us-east-1.eureka.example.com.
  • Type: TXT
  • Value:
"us-east-1a.eureka.example.com" "us-east-1d.eureka.example.com" "us-east-1e.eureka.example.com"

Tell what the elastic IP for us-east-1a is:

  • Name: txt.us-east-1a.eureka.example.com.
  • Type: TXT
  • Value:
"ec2-54-174-98-162.compute-1.amazonaws.com"

Tell what the elastic IP for us-east-1d is:

  • Name: txt.us-east-1d.eureka.example.com.
  • Type: TXT
  • Value:
"ec2-54-174-112-44.compute-1.amazonaws.com"

Tell what the elastic IP for us-east-1e is:

  • Name: txt.us-east-1e.eureka.example.com.
  • Type: TXT
  • Value:
"ec2-54-174-112-158.compute-1.amazonaws.com"

If you’re using Route53 for your DNS service, the following script takes care of all this.

eureka-setup.py

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
127
#!/usr/bin/env python

#

import argparse
import boto.ec2
import boto.route53
import dns.resolver
import dns.reversename
import logging

logging.basicConfig(format = "%(levelname)s:%(asctime)s:%(pathname)s:%(funcName)s:%(message)s", datefmt = "%Y-%m-%dT%H:%M:%S%Z", level = logging.INFO)

def setup_eureka_region(r53_zone, eureka_server_domain_name, region, availability_zones):
    logging.info("Setup eureka region: eureka_server_domain_name = " + eureka_server_domain_name + "; region = " + region + "; availability_zones = " + str(availability_zones))

    eureka_region_name = "txt." + region + "." + eureka_server_domain_name
    logging.info(eureka_region_name)

    eureka_region_value = ""
    first = True
    for availability_zone in availability_zones:
        if first:
            first = False
        else:
            eureka_region_value = eureka_region_value + " "
        eureka_region_value = eureka_region_value + "\"" + availability_zone + "." + eureka_server_domain_name + "\""
    logging.info(eureka_region_value)

    eureka_region = r53_zone.find_records(name = eureka_region_name, type = "TXT")

    add_eureka_region = True
    if eureka_region is not None:
        remove_eureka_region = True
        if len(eureka_region.resource_records) == 1:
            if eureka_region.resource_records[0] == eureka_region_value:
                remove_eureka_region = False
                add_eureka_region = False
                logging.info("Eureka region already set")
    else:
        remove_eureka_region = False

    if remove_eureka_region:
        logging.info("Removing eureka region")
        r53_zone.delete_record(eureka_region)

    if add_eureka_region:
        logging.info("Adding eureka region")
        r53_zone.add_record(resource_type = "TXT", name = eureka_region_name, value = eureka_region_value, ttl = 60)

#

def setup_eureka_availability_zone(r53_zone, ec2_conn, eureka_server_domain_name, region, availability_zone, vpc = False):
    logging.info("Setup eureka availability zone: eureka_server_domain_name = " + eureka_server_domain_name + "; availability_zone = " + availability_zone + "; vpc = " + str(vpc))

    eureka_az_name = "txt." + availability_zone + "." + eureka_server_domain_name
    logging.info(eureka_az_name)

    eureka_az = r53_zone.find_records(name = eureka_az_name, type = "TXT")

    if vpc:
        addr = ec2_conn.allocate_address(domain = "vpc")
    else:
        addr = ec2_conn.allocate_address()

    ip_addr = addr.public_ip

    logging.info("ip address = " + ip_addr)

    hostname = str(dns.resolver.query(dns.reversename.from_address(ip_addr), "PTR")[0]).rstrip(".")
    eureka_az_value = "\"" + hostname + "\""

    if eureka_az is not None:
        logging.info("Delete eureka az record " + availability_zone)
        r53_zone.delete_record(eureka_az)
    logging.info("Add eureka az record " + availability_zone)
    r53_zone.add_record(resource_type = "TXT", name = eureka_az_name, value = eureka_az_value, ttl = 60)

parser = argparse.ArgumentParser(description="Setup eureka for dns configuration.")
parser.add_argument("--domain", type=str, nargs=1, help="domain name/route53 zone name")
parser.add_argument("--region", type=str, nargs=1, help="region to setup in")
parser.add_argument("--zones", type=str, nargs="+", help="availability zones in the region to use (e.g.: a b c)")
parser.add_argument("--eureka-server-domain-name", type=str, nargs="+", help="the value of eureka.eurekaServer.domainName")
parser.add_argument("--vpc", action="store_true", default=False, help="setup vpc elastic IPs")

args = parser.parse_args()

if args.eureka_server_domain_name is None:
    sys.stderr.write("--eureka-server-domain-name not set\n")
    sys.exit(1)

if args.domain is None:
    sys.stderr.write("--domain not set\n")
    sys.exit(1)

if args.region is None:
    sys.stderr.write("--region not set\n")
    sys.exit(1)

if args.zones is None:
    sys.stderr.write("--zones not set\n")
    sys.exit(1)

eureka_server_domain_name = args.eureka_server_domain_name[0]
domain = args.domain[0]
region = args.region[0]
availability_zones = []
for zone in args.zones:
    availability_zones.append(region + zone)
vpc = args.vpc

logging.info("domain = " + domain)
logging.info("eureka_server_domain_name = " + eureka_server_domain_name)
logging.info("region = " + region)
logging.info("zones = " + str(availability_zones))
logging.info("vpc = " + str(vpc))

r53_conn = boto.route53.connect_to_region(region)
r53_zone = r53_conn.get_zone(domain)
ec2_conn = boto.ec2.connect_to_region(region)

setup_eureka_region(r53_zone = r53_zone, eureka_server_domain_name = eureka_server_domain_name, region = region, availability_zones = availability_zones)

#

for availability_zone in availability_zones:
    setup_eureka_availability_zone(r53_zone = r53_zone, ec2_conn = ec2_conn, eureka_server_domain_name = eureka_server_domain_name, region = region, availability_zone = availability_zone, vpc = vpc)

The script uses boto and dnspython so install them if needed:

Console - user@hostname ~ $

1
2
pip install boto
pip install dnspython

And execute the script:

Standard

Console - user@hostname ~ $

1
2
3
4
5
./eureka-setup.py \
--eureka-server-domain-name "eureka.example.com" \
--domain "example.com" \
--region "us-east-1" \
--zones a d e

VPC

Console - user@hostname ~ $

1
2
3
4
5
6
./eureka-setup.py \
--eureka-server-domain-name "eureka.example.com" \
--domain "example.com" \
--region "us-east-1" \
--zones a d e \
--vpc

Eureka Security Group

Create security group for Eureka clients

Standard

Example API Request

1
2
3
4
5
https://ec2.us-east-1.amazonaws.com/
?Action=CreateSecurityGroup
&GroupName=eureka
&GroupDescription=Eureka
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
aws --region us-east-1 ec2 \
create-security-group \
--group-name "eureka" \
--description "Eureka"

Output

1
2
3
4
{
    "return": "true",
    "GroupId": "sg-373e7f5c"
}

VPC

Example API Request

1
2
3
4
5
6
https://ec2.us-east-1.amazonaws.com/
?Action=CreateSecurityGroup
&GroupName=eureka
&GroupDescription=Eureka
&VpcId=vpc-e322f886
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
aws --region us-east-1 ec2 \
create-security-group \
--group-name "eureka" \
--description "Eureka" \
--vpc-id "vpc-e322f886"

Output

1
2
3
4
{
    "return": "true",
    "GroupId": "sg-878cd7e2"
}

Create security group for Eureka servers

Standard

Example API Request

1
2
3
4
5
https://ec2.us-east-1.amazonaws.com/
?Action=CreateSecurityGroup
&GroupName=eureka-server
&GroupDescription=Eureka Server
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
aws --region us-east-1 ec2 \
create-security-group \
--group-name "eureka-server" \
--description "Eureka Server"

Output

1
2
3
4
{
    "return": "true",
    "GroupId": "sg-9e9cfef4"
}

VPC

Example API Request

1
2
3
4
5
6
https://ec2.us-east-1.amazonaws.com/
?Action=CreateSecurityGroup
&GroupName=eureka-server
&GroupDescription=Eureka Server
&VpcId=vpc-e322f886
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
aws --region us-east-1 ec2 \
create-security-group \
--group-name "eureka-server" \
--description "Eureka Server" \
--vpc-id "vpc-e322f886"

Output

1
2
3
4
{
    "return": "true",
    "GroupId": "sg-9d8cd7f8"
}

Authorize access to Eureka servers from the eureka-server and eureka security groups, as well as IP range 1.2.3.4/32, on TCP port 80.

Standard

Example API Request

1
2
3
4
5
6
7
8
9
10
https://ec2.us-east-1.amazonaws.com/
?Action=AuthorizeSecurityGroupIngress
&GroupId=sg-9e9cfef4
&IpPermissions.1.IpProtocol=tcp
&IpPermissions.1.FromPort=80
&IpPermissions.1.ToPort=80
&IpPermissions.1.Groups.1.GroupId=sg-9e9cfef4
&IpPermissions.1.Groups.2.GroupId=sg-373e7f5c
&IpPermissions.1.IpRanges.1.CidrIp=1.2.3.4/32
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
aws --region us-east-1 ec2 \
authorize-security-group-ingress \
--group-id "sg-9e9cfef4" \
--ip-permissions '[
    {
        "IpProtocol": "tcp",
        "FromPort": 80,
        "ToPort": 80,
        "UserIdGroupPairs": [
            {
                "GroupId": "sg-9e9cfef4"
            },
            {
                "GroupId": "sg-373e7f5c"
            }
        ],
        "IpRanges": [
            {
                "CidrIp": "1.2.3.4/32"
            }
        ]
    }
]'

Output

1
2
3
{
    "return": "true"
}

VPC

Example API Request

1
2
3
4
5
6
7
8
9
10
https://ec2.us-east-1.amazonaws.com/
?Action=AuthorizeSecurityGroupIngress
&GroupId=sg-9d8cd7f8
&IpPermissions.1.IpProtocol=tcp
&IpPermissions.1.FromPort=80
&IpPermissions.1.ToPort=80
&IpPermissions.1.Groups.1.GroupId=sg-9d8cd7f8
&IpPermissions.1.Groups.2.GroupId=sg-878cd7e2
&IpPermissions.1.IpRanges.1.CidrIp=1.2.3.4/32
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
aws --region us-east-1 ec2 \
authorize-security-group-ingress \
--group-id "sg-9d8cd7f8" \
--ip-permissions '[
    {
        "IpProtocol": "tcp",
        "FromPort": 80,
        "ToPort": 80,
        "UserIdGroupPairs": [
            {
                "GroupId": "sg-9d8cd7f8"
            },
            {
                "GroupId": "sg-878cd7e2"
            }
        ],
        "IpRanges": [
            {
                "CidrIp": "1.2.3.4/32"
            }
        ]
    }
]'

Output

1
2
3
{
    "return": "true"
}

IAM Roles

Here we will create the IAM role for Eureka to use. There are four steps to it when using the AWS CLI.

  1. CreateRole
  2. PutRolePolicy
  3. CreateInstanceProfile
  4. AddRoleToInstanceProfile

Step 1: Create the role. We’ll name the role eureka. And since this is for EC2, we’ll use the standard AssumeRolePolicyDocument for EC2 instances.

AssumeRolePolicyDocument

1
2
3
4
5
6
7
8
9
10
11
12
13
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Common

Example API Request

1
2
3
4
5
https://iam.amazonaws.com/
?Action=CreateRole
&RoleName=eureka
&AssumeRolePolicyDocument={"Version": "2008-10-17","Statement": [{"Sid": "","Effect": "Allow","Principal": {"Service": "ec2.amazonaws.com"},"Action": "sts:AssumeRole"}]}
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
aws iam \
create-role \
--role-name "eureka" \
--assume-role-policy-document '{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}'

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2008-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "ec2.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "AROAJ4C532EVC7OFGLCKE",
        "CreateDate": "2014-09-05T09:45:11.435Z",
        "RoleName": "eureka",
        "Path": "/",
        "Arn": "arn:aws:iam::123456789012:role/eureka"
    }
}

Step 2: Put a policy on the role. This policy is the minimum that Eureka needs:

PolicyDocument

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "Statement": [
        {
            "Sid": "Stmt1358974336152",
            "Action": [
                "ec2:DescribeAddresses",
                "ec2:AssociateAddress",
                "ec2:DisassociateAddress"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "Stmt1358974395291",
            "Action": [
                "autoscaling:DescribeAutoScalingGroups"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

Common

Example API Request

1
2
3
4
5
6
https://iam.amazonaws.com/
?Action=PutRolePolicy
&RoleName=eureka
&PolicyName=eureka
&PolicyDocument={"Statement": [{"Sid": "Stmt1358974336152","Action": ["ec2:DescribeAddresses","ec2:AssociateAddress","ec2:DisassociateAddress"],"Effect": "Allow","Resource": "*"},{"Sid": "Stmt1358974395291","Action": ["autoscaling:DescribeAutoScalingGroups"],"Effect": "Allow","Resource": "*"}]}
&*AUTHPARAMS*

Console - user@hostname ~ $

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
aws iam \
put-role-policy \
--role-name "eureka" \
--policy-name "eureka" \
--policy-document '{
    "Statement": [
        {
            "Sid": "Stmt1358974336152",
            "Action": [
                "ec2:DescribeAddresses",
                "ec2:AssociateAddress",
                "ec2:DisassociateAddress"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "Stmt1358974395291",
            "Action": [
                "autoscaling:DescribeAutoScalingGroups"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}'

Step 3: Create an instance profile.

Common

Example API Request

1
2
3
4
https://iam.amazonaws.com/
?Action=CreateInstanceProfile
&InstanceProfileName=eureka
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws iam \
create-instance-profile \
--instance-profile-name "eureka"

Output

1
2
3
4
5
6
7
8
9
10
{
    "InstanceProfile": {
        "InstanceProfileId": "AIPAJ27CXMB2PQUUDGPGI",
        "Roles": [],
        "CreateDate": "2014-09-05T09:47:01.558Z",
        "InstanceProfileName": "eureka",
        "Path": "/",
        "Arn": "arn:aws:iam::123456789012:instance-profile/eureka"
    }
}

Step 4: Add the role to the instance profile.

Common

Example API Request

1
2
3
4
5
https://iam.amazonaws.com/
?Action=AddRoleToInstanceProfile
&RoleName=eureka
&InstanceProfileName=eureka
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
aws iam \
add-role-to-instance-profile \
--role-name "eureka" \
--instance-profile-name "eureka"

The IAM role is now setup and ready to go.

Auto Scaling Group

Get Subnets

This step is only necessary for setting up auto scaling groups using a VPC. We need the subnet IDs of each of the availability zones we want to use. This command will list all the subnets in the region:

VPC

Example API Request

1
2
3
https://ec2.us-east-1.amazonaws.com/
?Action=DescribeSubnets
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
aws --region us-east-1 ec2 \
describe-subnets

Output

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
{
    "Subnets": [
        {
            "VpcId": "vpc-e322f886",
            "CidrBlock": "172.30.4.0/24",
            "MapPublicIpOnLaunch": true,
            "DefaultForAz": false,
            "State": "available",
            "AvailabilityZone": "us-east-1e",
            "SubnetId": "subnet-98dbdfb0",
            "AvailableIpAddressCount": 251
        },
        {
            "VpcId": "vpc-e322f886",
            "CidrBlock": "172.30.3.0/24",
            "MapPublicIpOnLaunch": true,
            "DefaultForAz": false,
            "State": "available",
            "AvailabilityZone": "us-east-1d",
            "SubnetId": "subnet-15d7334c",
            "AvailableIpAddressCount": 251
        },
        {
            "VpcId": "vpc-e322f886",
            "CidrBlock": "172.30.0.0/24",
            "MapPublicIpOnLaunch": true,
            "DefaultForAz": false,
            "State": "available",
            "AvailabilityZone": "us-east-1a",
            "SubnetId": "subnet-89ea10fe",
            "AvailableIpAddressCount": 251
        }
    ]
}

If we’d rather lookup just one availability zone, we can do it like so:

VPC

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=DescribeSubnets
&Filter.1.Name=availabilityZone
&Filter.1.Value.1=us-east-1a

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
aws --region us-east-1 ec2 \
describe-subnets \
--filters '[
    {
        "Name": "availabilityZone",
        "Values": ["us-east-1a"]
    }
]'

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "Subnets": [
        {
            "VpcId": "vpc-e322f886",
            "CidrBlock": "172.30.0.0/24",
            "MapPublicIpOnLaunch": true,
            "DefaultForAz": false,
            "State": "available",
            "AvailabilityZone": "us-east-1a",
            "SubnetId": "subnet-89ea10fe",
            "AvailableIpAddressCount": 251
        }
    ]
}

Create Launch Configuration

Before we create the actual auto scaling group, we need to create a launch configuration. This covers most of the information we’d need when starting up an instance manually. We’ll tell it what AMI to use, what instance type, what security groups, what key pair, and what IAM role to use. Although we’re not doing it in this example, it is also where we’d set the bid price if we were doing spot instances.

Standard

Example API Request

1
2
3
4
5
6
7
8
9
10
https://autoscaling.us-east-1.amazonaws.com/
?Action=CreateLaunchConfiguration
&LaunchConfigurationName=eureka-amazon-linux-2016-08-28-22-49-42-m3.medium
&ImageId=ami-94c16cfc
&KeyName=example
&SecurityGroups.member.1=sg-9e9cfef4
&SecurityGroups.member.2=sg-1d19e276
&InstanceType=m3.medium
&IamInstanceProfile=eureka
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
aws --region us-east-1 autoscaling \
create-launch-configuration \
--launch-configuration-name "eureka-amazon-linux-2016-08-28-22-49-42-m3.medium" \
--image-id "ami-94c16cfc" \
--key-name "example" \
--security-groups '["sg-9e9cfef4", "sg-1d19e276"]' \
--instance-type "m3.medium" \
--iam-instance-profile "eureka"

VPC

Example API Request

1
2
3
4
5
6
7
8
9
10
11
https://autoscaling.us-east-1.amazonaws.com/
?Action=CreateLaunchConfiguration
&LaunchConfigurationName=eureka-amazon-linux-2016-08-28-22-49-42-t2.micro
&ImageId=ami-94c16cfc
&KeyName=example
&SecurityGroups.member.1=sg-9d8cd7f8
&SecurityGroups.member.2=sg-7583d810
&InstanceType=t2.micro
&IamInstanceProfile=eureka
&AssociatePublicIpAddress=true
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
aws --region us-east-1 autoscaling \
create-launch-configuration \
--launch-configuration-name "eureka-amazon-linux-2016-08-28-22-49-42-t2.micro" \
--image-id "ami-94c16cfc" \
--key-name "example" \
--security-groups '["sg-9d8cd7f8", "sg-7583d810"]' \
--instance-type "t2.micro" \
--iam-instance-profile "eureka" \
--associate-public-ip-address

Create Auto Scaling Group

Now that we have a launch configuration, we’ll create the auto scaling group. In addition to which launch configuration to use, we’ll also say how many instances to start, which availability zones can be used, and set tags if we want to.

Standard

Example API Request

1
2
3
4
5
6
7
8
9
10
11
12
https://autoscaling.us-east-1.amazonaws.com/
?Action=CreateAutoScalingGroup
&AutoScalingGroupName=eureka-servers-standard
&LaunchConfigurationName=eureka-amazon-linux-2016-08-28-22-49-42-m3.medium
&MinSize=3
&MaxSize=3
&DesiredCapacity=3
&AvailabilityZones.member.1=us-east-1a
&AvailabilityZones.member.2=us-east-1d
&AvailabilityZones.member.3=us-east-1e
&Tags.member.1=key=Name,value=eureka-servers,propagate=true
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
aws --region us-east-1 autoscaling \
create-auto-scaling-group \
--auto-scaling-group-name "eureka-servers-standard" \
--launch-configuration-name "eureka-amazon-linux-2016-08-28-22-49-42-m3.medium" \
--min-size 3 \
--max-size 3 \
--desired-capacity 3 \
--availability-zones '["us-east-1a", "us-east-1d", "us-east-1e"]' \
--tags '[
    {
        "Key": "Name",
        "Value": "eureka-server",
        "PropagateAtLaunch": true
    }
]'

VPC

Example API Request

1
2
3
4
5
6
7
8
9
10
11
12
13
https://autoscaling.us-east-1.amazonaws.com/
?Action=CreateAutoScalingGroup
&AutoScalingGroupName=eureka-servers-vpc
&LaunchConfigurationName=eureka-amazon-linux-2016-08-28-22-49-42-t2.micro
&MinSize=3
&MaxSize=3
&DesiredCapacity=3
&AvailabilityZones.member.1=us-east-1a
&AvailabilityZones.member.2=us-east-1d
&AvailabilityZones.member.3=us-east-1e
&VPCZoneIdentifier=subnet-89ea10fe,subnet-15d7334c,subnet-98dbdfb0
&Tags.member.1=key=Name,value=eureka-servers,propagate=true
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
aws --region us-east-1 autoscaling \
create-auto-scaling-group \
--auto-scaling-group-name "eureka-servers-vpc" \
--launch-configuration-name "eureka-amazon-linux-2016-08-28-22-49-42-t2.micro" \
--min-size 3 \
--max-size 3 \
--desired-capacity 3 \
--availability-zones '["us-east-1a", "us-east-1d", "us-east-1e"]' \
--tags '[
    {
        "Key": "Name",
        "Value": "eureka-server",
        "PropagateAtLaunch": true
    }
]' \
--vpc-zone-identifier "subnet-89ea10fe,subnet-15d7334c,subnet-98dbdfb0"

Describe Auto Scaling Groups

Once the auto scaling group starts our instances, we can describe the auto scaling group to find what the instance IDs are.

Standard

Example API Request

1
2
3
4
https://autoscaling.us-east-1.amazonaws.com/
?Action=DescribeAutoScalingGroups
&AutoScalingGroupNames.member.1=eureka-servers-standard
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 autoscaling \
describe-auto-scaling-groups \
--auto-scaling-group-names '["eureka-servers-standard"]'

Output

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
{
    "AutoScalingGroups": [
        {
            "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:58b40ff5-c67a-4f83-a1f5-3e1cc51707bf:autoScalingGroupName/eureka-servers-standard",
            "HealthCheckGracePeriod": 0,
            "SuspendedProcesses": [],
            "DesiredCapacity": 3,
            "Tags": [
                {
                    "ResourceType": "auto-scaling-group",
                    "ResourceId": "eureka-servers-standard",
                    "PropagateAtLaunch": true,
                    "Value": "eureka-server",
                    "Key": "Name"
                }
            ],
            "EnabledMetrics": [],
            "LoadBalancerNames": [],
            "AutoScalingGroupName": "eureka-servers-standard",
            "DefaultCooldown": 300,
            "MinSize": 3,
            "Instances": [
                {
                    "InstanceId": "i-d869b933",
                    "AvailabilityZone": "us-east-1a",
                    "HealthStatus": "Healthy",
                    "LifecycleState": "InService",
                    "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-m3.medium"
                },
                {
                    "InstanceId": "i-0b1ffae5",
                    "AvailabilityZone": "us-east-1e",
                    "HealthStatus": "Healthy",
                    "LifecycleState": "InService",
                    "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-m3.medium"
                },
                {
                    "InstanceId": "i-1717af3a",
                    "AvailabilityZone": "us-east-1d",
                    "HealthStatus": "Healthy",
                    "LifecycleState": "InService",
                    "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-m3.medium"
                }
            ],
            "MaxSize": 3,
            "VPCZoneIdentifier": null,
            "TerminationPolicies": [
                "Default"
            ],
            "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-m3.medium",
            "CreatedTime": "2014-09-11T10:20:58.786Z",
            "AvailabilityZones": [
                "us-east-1a",
                "us-east-1e",
                "us-east-1d"
            ],
            "HealthCheckType": "EC2"
        }
    ]
}

VPC

Example API Request

1
2
3
4
https://autoscaling.us-east-1.amazonaws.com/
?Action=DescribeAutoScalingGroups
&AutoScalingGroupNames.member.1=eureka-servers-vpc
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 autoscaling \
describe-auto-scaling-groups \
--auto-scaling-group-names '["eureka-servers-vpc"]'

Output

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
{
    "AutoScalingGroups": [
        {
            "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:e8a0c421-2084-44e3-87a3-cccbbc90bd60:autoScalingGroupName/eureka-servers-vpc",
            "HealthCheckGracePeriod": 0,
            "SuspendedProcesses": [],
            "DesiredCapacity": 3,
            "Tags": [
                {
                    "ResourceType": "auto-scaling-group",
                    "ResourceId": "eureka-servers-vpc",
                    "PropagateAtLaunch": true,
                    "Value": "eureka-server",
                    "Key": "Name"
                }
            ],
            "EnabledMetrics": [],
            "LoadBalancerNames": [],
            "AutoScalingGroupName": "eureka-servers-vpc",
            "DefaultCooldown": 300,
            "MinSize": 3,
            "Instances": [
                {
                    "InstanceId": "i-d869b933",
                    "AvailabilityZone": "us-east-1a",
                    "HealthStatus": "Healthy",
                    "LifecycleState": "InService",
                    "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-t2.micro"
                },
                {
                    "InstanceId": "i-0b1ffae5",
                    "AvailabilityZone": "us-east-1e",
                    "HealthStatus": "Healthy",
                    "LifecycleState": "InService",
                    "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-t2.micro"
                },
                {
                    "InstanceId": "i-1717af3a",
                    "AvailabilityZone": "us-east-1d",
                    "HealthStatus": "Healthy",
                    "LifecycleState": "InService",
                    "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-t2.micro"
                }
            ],
            "MaxSize": 3,
            "VPCZoneIdentifier": "subnet-89ea10fesubnet-15d7334c,subnet-98dbdfb0",
            "TerminationPolicies": [
                "Default"
            ],
            "LaunchConfigurationName": "eureka-amazon-linux-2016-08-28-22-49-42-t2.micro",
            "CreatedTime": "2014-09-06T06:32:52.975Z",
            "AvailabilityZones": [
                "us-east-1a",
                "us-east-1e",
                "us-east-1d"
            ],
            "HealthCheckType": "EC2"
        }
    ]
}

Status Page

In this example, we could go the status page of Eureka cluster by opening any of these URLs in our web browser. Make sure to replace the hostnames with your appropriate elastic IPs.

  • http://ec2-54-174-98-162.compute-1.amazonaws.com/eureka/
  • http://ec2-54-174-112-44.compute-1.amazonaws.com/eureka/
  • http://ec2-54-174-112-158.compute-1.amazonaws.com/eureka/

When we do visit it, it should look something like this:

Test

There is a sample client and service we can use to demonstrate Eureka. On a server that can connect to the Eureka servers, checkout Eureka again.

Console - user@hostname ~ $

1
git clone https://github.com/Netflix/eureka.git

Console - user@hostname ~ $

1
cd eureka

Console - user@hostname ~/eureka $

1
./gradlew clean build

Edit sample-eureka-client-properties

~/eureka/eureka-server/conf/sampleclient/sample-eureka-client.properties - (Original)

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
###Eureka Client configuration for Sample Eureka Client

#Properties based configuration for Eureka client. The properties specified here is mostly what the users
#need to change. All of these can be specified as a java system property with -D option (eg)-Deureka.region=us-east-1
#For additional tuning options refer <url to go here>


#Region where Eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string
#indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1
eureka.region=default

#Name of the application to be identified by other services

eureka.name=sampleEurekaClient

#Virtual host name by which the clients identifies this service
#eureka.vipAddress=eureka.mydomain.net

#The port where the service will be running and servicing requests
#eureka.port=80

#For eureka clients running in eureka server, it needs to connect to servers in other zones
eureka.preferSameZone=true

#Change this if you want to use a DNS based lookup for determining other eureka servers. For example
#of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties
eureka.shouldUseDns=false

eureka.us-east-1.availabilityZones=default

eureka.serviceUrl.default=http://localhost/eureka/v2/

Comment the following lines:

Comment These Lines

1
2
3
4
eureka.region=default
eureka.shouldUseDns=false
eureka.us-east-1.availabilityZones=default
eureka.serviceUrl.default=http://localhost/eureka/v2/

Add the following lines:

Add These Lines

1
2
3
4
eureka.shouldUseDns=true
eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2

We’ll end up with:

~/eureka/eureka-server/conf/sampleclient/sample-eureka-client.properties

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
###Eureka Client configuration for Sample Eureka Client

#Properties based configuration for eureka client. The properties specified here is mostly what the users
#need to change. All of these can be specified as a java system property with -D option (eg)-Deureka.region=us-east-1
#For additional tuning options refer <url to go here>


#Region where eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string
#indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1
#eureka.region=default

#Name of the application to be identified by other services

eureka.name=sampleEurekaClient

#Virtual host name by which the clients identifies this service
#eureka.vipAddress=eureka.mydomain.net

#The port where the service will be running and servicing requests
#eureka.port=80

#For eureka clients running in eureka server, it needs to connect to servers in other zones
eureka.preferSameZone=true

#Change this if you want to use a DNS based lookup for determining other eureka servers. For example
#of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties
#eureka.shouldUseDns=false
eureka.shouldUseDns=true

#eureka.us-east-1.availabilityZones=default

#eureka.serviceUrl.default=http://localhost/eureka/v2/

eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2

Edit sample-eureka-service.properties

~/eureka/eureka-server/conf/sampleservice/sample-eureka-service.properties - (Original)

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
###Eureka Client configuration for Sample Eureka Service

#Properties based configuration for eureka client. The properties specified here is mostly what the users
#need to change. All of these can be specified as a java system property with -D option (eg)-Deureka.region=us-east-1
#For additional tuning options refer <url to go here>


#Region where eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string
#indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1
eureka.region=default

#Name of the application to be identified by other services

eureka.name=sampleservice

#Virtual host name by which the clients identifies this service
eureka.vipAddress=sampleservice.mydomain.net

#The port where the service will be running and serving requests
eureka.port=8001

#For eureka clients running in eureka server, it needs to connect to servers in other zones
eureka.preferSameZone=false

#Change this if you want to use a DNS based lookup for determining other eureka servers. For example
#of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties
eureka.shouldUseDns=false

eureka.us-east-1.availabilityZones=default

eureka.serviceUrl.default=http://localhost/eureka/v2/

Comment the following lines:

Comment These Lines

1
2
3
4
eureka.region=default
eureka.shouldUseDns=false
eureka.us-east-1.availabilityZones=default
eureka.serviceUrl.default=http://localhost/eureka/v2/

Add the following lines:

Add These Lines

1
2
3
4
eureka.shouldUseDns=true
eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2

~/eureka/eureka-server/conf/sampleservice/sample-eureka-service.properties

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
###Eureka Client configuration for Sample Eureka Service

#Properties based configuration for eureka client. The properties specified here is mostly what the users
#need to change. All of these can be specified as a java system property with -D option (eg)-Deureka.region=us-east-1
#For additional tuning options refer <url to go here>


#Region where eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string
#indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1
#eureka.region=default

#Name of the application to be identified by other services

eureka.name=sampleservice

#Virtual host name by which the clients identifies this service
eureka.vipAddress=sampleservice.mydomain.net

#The port where the service will be running and serving requests
eureka.port=8001

#For eureka clients running in eureka server, it needs to connect to servers in other zones
eureka.preferSameZone=false

#Change this if you want to use a DNS based lookup for determining other eureka servers. For example
#of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties
#eureka.shouldUseDns=false
eureka.shouldUseDns=true

#eureka.us-east-1.availabilityZones=default

#eureka.serviceUrl.default=http://localhost/eureka/v2/

eureka.eurekaServer.domainName=eureka.example.com
eureka.eurekaServer.port=80
eureka.eurekaServer.context=eureka/v2

Edit runclient.sh

~/eureka/eureka-server/runclient.sh - (Original)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
#Copy all libraries
TEST_CLASSPATH=
for i in testlibs/WEB-INF/lib/*
do
 if [ "$TEST_CLASSPATH" = "" ] ; then
   TEST_CLASSPATH=$i
 fi
 TEST_CLASSPATH=$TEST_CLASSPATH:$i
done
TEST_CLASSPATH=$TEST_CLASSPATH:build/classes/main:conf/sampleclient

echo CLASSPATH:$TEST_CLASSPATH
java -Deureka.region=default -Deureka.environment=test -Deureka.client.props=sample-eureka-client -cp $TEST_CLASSPATH com.netflix.eureka.SampleEurekaClient

Replace -Deureka.region=default with -Deureka.region=us-east-1

We would normally set eureka.datacenter=cloud, but the example is hard coded to non-AWS datacenter.

~/eureka/eureka-server/runclient.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
#Copy all libraries
TEST_CLASSPATH=
for i in testlibs/WEB-INF/lib/*
do
 if [ "$TEST_CLASSPATH" = "" ] ; then
   TEST_CLASSPATH=$i
 fi
 TEST_CLASSPATH=$TEST_CLASSPATH:$i
done
TEST_CLASSPATH=$TEST_CLASSPATH:build/classes/main:conf/sampleclient

echo CLASSPATH:$TEST_CLASSPATH
java -Deureka.region=us-east-1 -Deureka.environment=test -Deureka.client.props=sample-eureka-client -cp $TEST_CLASSPATH com.netflix.eureka.SampleEurekaClient

Edit runservice.sh

~/eureka/eureka-server/runservice.sh - (Original)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
#Copy all libraries
TEST_CLASSPATH=
for i in testlibs/WEB-INF/lib/*
do
 if [ "$TEST_CLASSPATH" = "" ] ; then
   TEST_CLASSPATH=$i
 fi
 TEST_CLASSPATH=$TEST_CLASSPATH:$i
done
TEST_CLASSPATH=$TEST_CLASSPATH:build/classes/main:conf/sampleservice

echo CLASSPATH:$TEST_CLASSPATH
java -Deureka.region=default -Deureka.environment=test -Deureka.client.props=sample-eureka-service -cp $TEST_CLASSPATH com.netflix.eureka.SampleEurekaService

Replace -Deureka.region=default with -Deureka.region=us-east-1

We would normally set eureka.datacenter=cloud, but the example is hard coded to non-AWS datacenter.

~/eureka/eureka-server/runservice.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
#Copy all libraries
TEST_CLASSPATH=
for i in testlibs/WEB-INF/lib/*
do
 if [ "$TEST_CLASSPATH" = "" ] ; then
   TEST_CLASSPATH=$i
 fi
 TEST_CLASSPATH=$TEST_CLASSPATH:$i
done
TEST_CLASSPATH=$TEST_CLASSPATH:build/classes/main:conf/sampleservice

echo CLASSPATH:$TEST_CLASSPATH
java -Deureka.region=us-east-1 -Deureka.environment=test -Deureka.client.props=sample-eureka-service -cp $TEST_CLASSPATH com.netflix.eureka.SampleEurekaService

Get testlibs

testlibs isn’t built, get it manually:

Console - user@hostname ~/eureka $

1
cd eureka-server/build/libs/

Console - user@hostname ~/eureka/eureka-server/build/libs $

1
2
3
mkdir eureka-server-1.4.12-SNAPSHOT
unzip -d eureka-server-1.4.12-SNAPSHOT eureka-server-1.4.12-SNAPSHOT.war
cd ../../

Console - user@hostname ~/eureka/eureka-server $

1
2
mkdir -p testlibs/WEB-INF/lib
cp -R build/libs/eureka-server-1.4.12-SNAPSHOT/WEB-INF/lib/* testlibs/WEB-INF/lib

Run the Example

Run the service:

Console - user@hostname ~/eureka/eureka-server $

1
./runservice.sh &

Wait for Service started and ready to process requests.. then run the client:

Console - user@hostname ~/eureka/eureka-server $

1
./runclient.sh

Cleanup

Turn off instances

Standard

Example API Request

1
2
3
4
5
6
7
https://autoscaling.us-east-1.amazonaws.com/
?Action=UpdateAutoScalingGroup
&AutoScalingGroupName=eureka-servers-standard
&MinSize=0
&MaxSize=0
&DesiredCapacity=0
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
aws --region us-east-1 autoscaling \
update-auto-scaling-group \
--auto-scaling-group-name "eureka-servers-standard" \
--min-size 0 \
--max-size 0 \
--desired-capacity 0

VPC

Example API Request

1
2
3
4
5
6
7
https://autoscaling.us-east-1.amazonaws.com/
?Action=UpdateAutoScalingGroup
&AutoScalingGroupName=eureka-servers-vpc
&MinSize=0
&MaxSize=0
&DesiredCapacity=0
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
aws --region us-east-1 autoscaling \
update-auto-scaling-group \
--auto-scaling-group-name "eureka-servers-vpc" \
--min-size 0 \
--max-size 0 \
--desired-capacity 0

Release Elastic IPs:

Standard

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=ReleaseAddress
&PublicIp=54.174.98.162
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
release-address \
--public-ip "54.174.98.162"

Output

1
2
3
{
    "return": "true"
}

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=ReleaseAddress
&PublicIp=54.174.112.44
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
release-address \
--public-ip "54.174.112.44"

Output

1
2
3
{
    "return": "true"
}

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=ReleaseAddress
&PublicIp=54.174.112.158
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
release-address \
--public-ip "54.174.112.158"

Output

1
2
3
{
    "return": "true"
}

VPC

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=ReleaseAddress
&AllocationId=eipalloc-9ef359fb
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
release-address \
--allocation-id "eipalloc-9ef359fb"

Output

1
2
3
{
    "return": "true"
}

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=ReleaseAddress
&AllocationId=eipalloc-9cf359f9
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
release-address \
--allocation-id "eipalloc-9cf359f9"

Output

1
2
3
{
    "return": "true"
}

Example API Request

1
2
3
4
https://ec2.us-east-1.amazonaws.com/
?Action=ReleaseAddress
&AllocationId=eipalloc-9ff359fa
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
aws --region us-east-1 ec2 \
release-address \
--allocation-id "eipalloc-9ff359fa"

Output

1
2
3
{
    "return": "true"
}

Start Up Again

Allocate the Elastic IPs and setup DNS as seen in the DNS section

Turn on the instances

Standard

Example API Request

1
2
3
4
5
6
7
https://autoscaling.us-east-1.amazonaws.com/
?Action=UpdateAutoScalingGroup
&AutoScalingGroupName=eureka-servers-standard
&MinSize=3
&MaxSize=3
&DesiredCapacity=3
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
aws --region us-east-1 autoscaling \
update-auto-scaling-group \
--auto-scaling-group-name "eureka-servers-standard" \
--min-size 3 \
--max-size 3 \
--desired-capacity 3

VPC

Example API Request

1
2
3
4
5
6
7
https://autoscaling.us-east-1.amazonaws.com/
?Action=UpdateAutoScalingGroup
&AutoScalingGroupName=eureka-servers-vpc
&MinSize=3
&MaxSize=3
&DesiredCapacity=3
&*AUTHPARAMS*

Console - user@hostname ~ $

1
2
3
4
5
6
aws --region us-east-1 autoscaling \
update-auto-scaling-group \
--auto-scaling-group-name "eureka-servers-vpc" \
--min-size 3 \
--max-size 3 \
--desired-capacity 3