Acontece que minha premissa original (a necessidade de saber o endereço IP interno do recipiente da tarefa para a descoberta de serviço) é muito falha - esse endereço IP só pode ser usado em uma única Instância de Contêiner do EC2. Se você tiver várias instâncias de contêiner (o que provavelmente deve ter), esses IPs do contêiner de tarefas são basicamente inúteis.
A solução alternativa que criei é seguir o padrão sugerido para balanceadores de carga de aplicativos que executam HTTP / HTTPS - ter um mapeamento de porta com 0 como a porta do host, apontando para a porta na instância do docker que preciso usar . Ao fazer isso, o Docker atribuirá uma porta de host aleatória, que eu posso encontrar usando o SDK da AWS - em particular, a função "describeTasks" disponível no módulo do ECS. Veja aqui para mais detalhes: link
Esta é a base fundamental para o meu mecanismo de descoberta de serviço de rolagem - há muitos outros detalhes necessários para fazer isso de maneira completa. Eu usei as funções do Lambda chamando o AWS SDK, bem como um banco de dados PostgreSQL, para manter atualizada a minha lista de recipientes de host (algo parecido com um registro de DNS dinâmico). Parte do truque é que você precisa saber o IP e a porta para cada um dos contêineres, mas describeTasks apenas retorna a porta. Aqui está uma função útil do NodeJS que escrevi, que pega um nome de contêiner e procura todos os endereços IP e portas encontrados dentro do cluster para contêineres com esse nome:
var Q = require('q');
/**
* @param {String} - cluster - name of the cluster to query, e.g. "sqlfiddle3"
* @param {String} - containerType - name of the container to search for within the cluster
* @returns {Promise} - promise resolved with a list of ip/port combinations found for this container name, like so:
[
{
"connection_meta": "{\"type\":\"ecs\",\"taskArn\":\"arn:aws:ecs:u..\"}",
"port": 32769,
"ip": "10.0.1.49"
}
]
*
*/
exports.getAllHostsForContainerType = (cluster, containerType) => {
var AWS = require('aws-sdk'),
ecs = new AWS.ECS({"apiVersion": '2014-11-13'}),
ec2 = new AWS.EC2({"apiVersion": '2016-11-15'});
return ecs.listTasks({ cluster }).promise()
.then((taskList) => ecs.describeTasks({ cluster, tasks: taskList.taskArns }).promise())
.then((taskDetails) => {
var containersForName = taskDetails.tasks
.filter((taskDetail) =>
taskDetail.containers.filter(
(container) => container.name === containerType
).length > 0
)
.map((taskDetail) =>
taskDetail.containers.map((container) => {
container.containerInstanceArn = taskDetail.containerInstanceArn;
return container;
})
)
.reduce((final, containers) =>
final.concat(containers)
, []);
return containersForName.length ? (ecs.describeContainerInstances({ cluster,
containerInstances: containersForName.map(
(containerDetails) => containerDetails.containerInstanceArn
)
}).promise()
.then((containerInstanceList) => {
containersForName.forEach((containerDetails) => {
containerDetails.containerInstanceDetails = containerInstanceList.containerInstances.filter((instance) =>
instance.containerInstanceArn === containerDetails.containerInstanceArn
)[0];
});
return ec2.describeInstances({
InstanceIds: containerInstanceList.containerInstances.map((instance) =>
instance.ec2InstanceId
)
}).promise();
})
.then((instanceDetails) => {
var instanceList = instanceDetails.Reservations.reduce(
(final, res) => final.concat(res.Instances), []
);
containersForName.forEach((containerDetails) => {
if (containerDetails.containerInstanceDetails) {
containerDetails.containerInstanceDetails.ec2Instance = instanceList.filter(
(instance) => instance.InstanceId === containerDetails.containerInstanceDetails.ec2InstanceId
)[0];
}
});
return containersForName;
})) : [];
})
.then(
(containersForName) => containersForName.map(
(container) => ({
connection_meta: JSON.stringify({
type: "ecs",
taskArn: container.taskArn
}),
// assumes that this container has exactly one network binding
port: container.networkBindings[0].hostPort,
ip: container.containerInstanceDetails.ec2Instance.PrivateIpAddress
})
)
);
};
Observe que isso usa a biblioteca de promessas 'Q' - você precisará declarar isso como uma dependência em seu pacote.json.
O restante da minha solução personalizada para a entrega da descoberta de serviço do ECS usando as funções do Lambda pode ser encontrada aqui: link