'Get EC2 instance id that hosts an ECS task for a specific service name
With AWS SSM plugin, you can login to ECS container with the following:
aws ssm start-session --target i-<ec2 instance target id>
sudo su
docker ps
docker exec -it <image id> bash
The trick is you need to first find the right ec2 instance id. This can be sort of be done manually via several command line calls. Eg
aws ecs list-container-instances --cluster <cluster name>
aws ecs list-tasks --cluster <cluster name>
But this doesn't give me exactly what I want which is a quick script or oneliner to be able to specify an ECS service name and immediately login to an EC2 instance that is hosting a task for that service.
There obviously may be multiple instances hosting multiple tasks from a service - the first one is ok.
In summary, how can I get the EC2 instance id that hosts a task for a specific service name. Ideally, this instance id can get piped into the aws ssm command.
Solution 1:[1]
There is a container metadata file that is made available to each container. The file location is automatically placed in an environment variable, ECS_CONTAINER_METADATA_FILE.
According to the docs, you must enable container metadata, because it is not available by default. This can be done by setting ECS_ENABLE_CONTAINER_METADATA=true in your ECS EC2 instance's /etc/ecs/ecs.config file. (You must restart the ECS agent after updating the file).
You can see the contents of the file in your container by running cat $ECS_CONTAINER_METADATA_FILE. For example,
{
"Cluster": "default",
"ContainerInstanceARN": "arn:aws:ecs:us-west-2:012345678910:container-instance/1f73d099-b914-411c-a9ff-81633b7741dd",
"TaskARN": "arn:aws:ecs:us-west-2:012345678910:task/2b88376d-aba3-4950-9ddf-bcb0f388a40c",
"ContainerID": "98e44444008169587b826b4cd76c6732e5899747e753af1e19a35db64f9e9c32",
"ContainerName": "metadata",
"DockerContainerName": "/ecs-metadata-7-metadata-f0edfbd6d09fdef20800",
"ImageID": "sha256:c24f66af34b4d76558f7743109e2476b6325fcf6cc167c6e1e07cd121a22b341",
"ImageName": "httpd:2.4",
"PortMappings": [
{
"ContainerPort": 80,
"HostPort": 80,
"BindIp": "",
"Protocol": "tcp"
}
],
"Networks": [
{
"NetworkMode": "bridge",
"IPv4Addresses": [
"172.17.0.2"
]
}
],
"MetadataFileStatus": "READY"
}
With this information, we can make an API call to get the EC2 instance id the container is running on. For the following example, I am assuming that jq and the aws-cli are installed in your container. I'm also assuming that you have added an environment variable, ECS_CLUSTER, to your Task Definition, which contains the name of your ECS cluster.
#!/bin/bash -e
CONTAINER_ARN=$(cat ${ECS_CONTAINER_METADATA_FILE} | jq -r '.ContainerInstanceARN')
CONTAINER_DESCRIPTION=$(aws ecs describe-container-instances --container-instances ${CONTAINER_ARN} --cluster ${ECS_CLUSTER} --region ${YOUR_REGION})
EC2_INSTANCE_ID=$(echo ${CONTAINER_DESCRIPTION} | jq -r '.containerInstances[0].ec2InstanceId')
echo ${EC2_INSTANCE_ID}
I am running a similar script in my container. By sure you configure the IAM policy associated with your task's IAM Role so that it has permission to perform the ecs:DescribeContainerInstances action.
Solution 2:[2]
Finally figured out how to do it easily in Ruby with a few systems calls to AWS CLI to output a map of ec2InstanceId to service group
#!/usr/bin/env ruby
require 'json'
cluster = ARGV[0]
container_instances = JSON.parse(`aws ecs list-container-instances --cluster #{cluster} |jq`)["containerInstanceArns"]
container_instances_metadata = JSON.parse(`aws ecs describe-container-instances --cluster #{cluster} --container-instances #{container_instances.join(' ')}|jq`)["containerInstances"]
target_map = container_instances_metadata.inject({}){|map, cim| map[cim["containerInstanceArn"]] = cim["ec2InstanceId"]; map}
tasks = JSON.parse(`aws ecs list-tasks --cluster #{cluster} |jq`)["taskArns"]
tasks_metadata = JSON.parse(`aws ecs describe-tasks --cluster #{cluster} --tasks #{tasks.join(' ')} |jq`)["tasks"]
final_map = tasks_metadata.map do |task|
ec2InstanceId = target_map[task["containerInstanceArn"]]
[ec2InstanceId, task["group"], task['overrides']]
end
puts final_map.map{|i| i.join(' ')}
Solution 3:[3]
It can be achieved in a simpler way as well, You'r Welcome:?
CLUSTER=$1
ServiceName=$2
TASKARN=$(aws ecs list-tasks --cluster $CLUSTER --service-name $ServiceName --output text | awk 'NR==1 {print $2}')
CONTAINER_INSTANCE=$(aws ecs describe-tasks --cluster $CLUSTER --tasks $TASKARN | jq -r '.tasks[0].containerInstanceArn')
InstanceId=$(aws ecs describe-container-instances --cluster $CLUSTER --container-instances $CONTAINER_INSTANCE | jq -r '.containerInstances[0].ec2InstanceId')
InstanceIp=$(aws ec2 describe-instances --instance-id $InstanceId | jq -r '.Reservations[0].Instances[0].PrivateIpAddress')
echo $InstanceIp
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Peter Kirby |
| Solution 2 | BoomShadow |
| Solution 3 |
