Skip to content

Instantly share code, notes, and snippets.

@mljohns89
Last active May 25, 2022 13:39
Show Gist options
  • Save mljohns89/e1f8881620292329b408465a982dc7b8 to your computer and use it in GitHub Desktop.
Save mljohns89/e1f8881620292329b408465a982dc7b8 to your computer and use it in GitHub Desktop.
Example Config for Making Eureka work with AWS ECS Fargate
@Configuration
@Slf4j
public class AwsEcsFargateConfig {
private static final String AWS_API_VERSION = "v2";
private static final String AWS_METADATA_URL = "http://169.254.170.2/" + AWS_API_VERSION + "/metadata";
@Value("${server.port}")
private int port;
@Value("${spring.application.name}")
private String appName;
private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
@Bean
public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) throws JsonProcessingException {
EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
config.setNonSecurePort(port);
config.setAppname(appName);
config.setVirtualHostName(appName);
config.setSecureVirtualHostName(appName);
try {
String ecsFargateMetadata = readEcsMetadata();
EcsTaskMetadata ecsTaskMetadata = mapper.readValue(ecsFargateMetadata, EcsTaskMetadata.class);
String ipAddress = findContainerPrivateIP(ecsTaskMetadata);
log.info("OVERRIDE IP ADDRESS TO: {}", ipAddress);
config.setIpAddress(ipAddress);
config.setHostname(ipAddress);
config.setInstanceId(ipAddress+":"+config.getSecureVirtualHostName()+":"+config.getNonSecurePort());
log.info("ECS FARGATE METADATA: {}", ecsFargateMetadata);
} catch (Exception e) {
log.error("Something went wrong when reading ECS metadata: {}", e.getMessage());
}
log.info("EurekaInstanceConfigBean: {}", mapper.writeValueAsString(config));
log.info("EurekaInstanceConfigBean.getVirtualHostName(): {}", mapper.writeValueAsString(config.getVirtualHostName()));
return config;
}
private String readEcsMetadata() throws Exception {
URL obj = new URL(AWS_METADATA_URL);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
StringBuilder response = new StringBuilder();
try{
con.setRequestMethod("GET");
try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()))) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
}
} finally {
con.disconnect();
}
return response.toString();
}
private String findContainerPrivateIP(EcsTaskMetadata metadata) {
if (null != metadata){
for (Container container: metadata.getContainers()){
boolean found = container.getName().toLowerCase().contains(appName);
if (found){
Network network = container.getNetworks()[0];
return network.getIPv4Addresses()[0];
}
}
}
return "";
}
}
@Data
public class Container {
@JsonProperty("DockerId")
private String dockerID;
@JsonProperty("Name")
private String name;
@JsonProperty("DockerName")
private String dockerName;
@JsonProperty("Image")
private String image;
@JsonProperty("ImageID")
private String imageID;
@JsonProperty("Labels")
private Labels labels;
@JsonProperty("DesiredStatus")
private String desiredStatus;
@JsonProperty("KnownStatus")
private String knownStatus;
@JsonProperty("Limits")
private Limits limits;
@JsonProperty("CreatedAt")
private String createdAt;
@JsonProperty("StartedAt")
private String startedAt;
@JsonProperty("Type")
private String type;
@JsonProperty("Networks")
private Network[] networks;
@JsonProperty("Health")
private Health health;
@JsonProperty("Volumes")
private Volumes[] volumes;
}
@Data
public class EcsTaskMetadata {
@JsonProperty("Cluster")
private String cluster;
@JsonProperty("TaskARN")
private String taskARN;
@JsonProperty("Family")
private String family;
@JsonProperty("Revision")
private String revision;
@JsonProperty("DesiredStatus")
private String desiredStatus;
@JsonProperty("KnownStatus")
private String knownStatus;
@JsonProperty("Containers")
private Container[] containers;
@JsonProperty("Limits")
private Limits limits;
@JsonProperty("PullStartedAt")
private String pullStartedAt;
@JsonProperty("PullStoppedAt")
private String pullStoppedAt;
@JsonProperty("AvailabilityZone")
private String availabilityZone;
}
@Data
public class Health {
private String status;
private String statusSince;
private String output;
}
@Data
public class Labels {
@JsonProperty("com.amazonaws.ecs.cluster")
private String comAmazonawsEcsCluster;
@JsonProperty("com.amazonaws.ecs.container-name")
private String comAmazonawsEcsContainerName;
@JsonProperty("com.amazonaws.ecs.task-arn")
private String comAmazonawsEcsTaskArn;
@JsonProperty("com.amazonaws.ecs.task-definition-family")
private String comAmazonawsEcsTaskDefinitionFamily;
@JsonProperty("com.amazonaws.ecs.task-definition-version")
private String comAmazonawsEcsTaskDefinitionVersion;
}
@Data
public class Limits {
@JsonProperty("CPU")
private double cpu;
@JsonProperty("Memory")
private long memory;
}
@Data
public class Network {
@JsonProperty("NetworkMode")
private String networkMode;
@JsonProperty("IPv4Addresses")
private String[] iPv4Addresses;
}
@Data
public class Volumes {
@JsonProperty("DockerName")
private String dockerName;
@JsonProperty("Source")
private String source;
@JsonProperty("Destination")
private String destination;
}
@mljohns89
Copy link
Author

mljohns89 commented May 3, 2022

Example of how to use Eureka Service Discovery in ECS Fargate Instances with a Spring Boot/Spring Cloud Application. The tricky part is you essentially need to grab the private IP Address from the ECS Task Metadata and then override the destination IP Address with that private IP Address. You'll only know what that IP address is though at runtime since it is Fargate (EC2 instances don't have this issue because their IP address are static and can be configured in advance)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment