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.
Eureka Properties - We’ll introduce some common Eureka properties that will need to be set for clients and services.
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.
Create Image - Creating an AMI with Eureka installed on it.
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.
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.
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.
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.
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.
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.
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.
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.
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.
#!/usr/bin/env python#importargparseimportboto.ec2importboto.route53importdns.resolverimportdns.reversenameimportlogginglogging.basicConfig(format="%(levelname)s:%(asctime)s:%(pathname)s:%(funcName)s:%(message)s",datefmt="%Y-%m-%dT%H:%M:%S%Z",level=logging.INFO)defsetup_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_namelogging.info(eureka_region_name)eureka_region_value=""first=Trueforavailability_zoneinavailability_zones:iffirst:first=Falseelse: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=Trueifeureka_regionisnotNone:remove_eureka_region=Trueiflen(eureka_region.resource_records)==1:ifeureka_region.resource_records[0]==eureka_region_value:remove_eureka_region=Falseadd_eureka_region=Falselogging.info("Eureka region already set")else:remove_eureka_region=Falseifremove_eureka_region:logging.info("Removing eureka region")r53_zone.delete_record(eureka_region)ifadd_eureka_region:logging.info("Adding eureka region")r53_zone.add_record(resource_type="TXT",name=eureka_region_name,value=eureka_region_value,ttl=60)#defsetup_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_namelogging.info(eureka_az_name)eureka_az=r53_zone.find_records(name=eureka_az_name,type="TXT")ifvpc:addr=ec2_conn.allocate_address(domain="vpc")else:addr=ec2_conn.allocate_address()ip_addr=addr.public_iplogging.info("ip address = "+ip_addr)hostname=str(dns.resolver.query(dns.reversename.from_address(ip_addr),"PTR")[0]).rstrip(".")eureka_az_value="\""+hostname+"\""ifeureka_azisnotNone: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()ifargs.eureka_server_domain_nameisNone:sys.stderr.write("--eureka-server-domain-name not set\n")sys.exit(1)ifargs.domainisNone:sys.stderr.write("--domain not set\n")sys.exit(1)ifargs.regionisNone:sys.stderr.write("--region not set\n")sys.exit(1)ifargs.zonesisNone: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=[]forzoneinargs.zones:availability_zones.append(region+zone)vpc=args.vpclogging.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)#foravailability_zoneinavailability_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
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:
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.
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.
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.
###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=falseeureka.us-east-1.availabilityZones=defaulteureka.serviceUrl.default=http://localhost/eureka/v2/
###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.comeureka.eurekaServer.port=80eureka.eurekaServer.context=eureka/v2
###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=falseeureka.us-east-1.availabilityZones=defaulteureka.serviceUrl.default=http://localhost/eureka/v2/
###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.comeureka.eurekaServer.port=80eureka.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 librariesTEST_CLASSPATH=for i in testlibs/WEB-INF/lib/*do
if["$TEST_CLASSPATH"=""] ; then
TEST_CLASSPATH=$ifi
TEST_CLASSPATH=$TEST_CLASSPATH:$idone
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 librariesTEST_CLASSPATH=for i in testlibs/WEB-INF/lib/*do
if["$TEST_CLASSPATH"=""] ; then
TEST_CLASSPATH=$ifi
TEST_CLASSPATH=$TEST_CLASSPATH:$idone
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 librariesTEST_CLASSPATH=for i in testlibs/WEB-INF/lib/*do
if["$TEST_CLASSPATH"=""] ; then
TEST_CLASSPATH=$ifi
TEST_CLASSPATH=$TEST_CLASSPATH:$idone
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 librariesTEST_CLASSPATH=for i in testlibs/WEB-INF/lib/*do
if["$TEST_CLASSPATH"=""] ; then
TEST_CLASSPATH=$ifi
TEST_CLASSPATH=$TEST_CLASSPATH:$idone
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