Soru Belirli bir AWS ECS görevi için IP Adresi nasıl alınır?


Genişletmek istediğim hizmetler HTTP sunucuları olmadığından ve bu nedenle ELB tarafından yönetilemediğinden, kendi servis keşif versiyonunu ECS içinde oluşturmaya çalışıyorum. Ayrıca, ECS, henüz kullanıcı tarafından tanımlanan docker özellikli ağ özelliğini desteklemiyor Hizmet keşif yapmanın başka bir yolu olurdu. Bu konuda tartışıldığı gibi tartışma:

Şu anda Servis Discovery, başka bir tane gerektiren büyük bir acıdır   Hizmet (kendisi genellikle küme tabanlı ve kendini keşfeder ve   daha sonra diğer hizmetleri dinler). Bu dağınık bir çözüm değil   daha da fazla iğrenç olan Lambda "çözümleri"   uygulamak ve korumak.

Bu yüzden diğer seçeneklerin yerine iğrenç Lambda "çözüm" yoluna gidiyorum. Bu hack hizmeti keşfini oluşturmam gereken en önemli şey, EC2 ana bilgisayarlarımda çalışan her bir docker konteynerinin IP adresidir.

SSH'm, ECS kapsayıcı örneklerimden biri olarak hareket eden EC2 sunucusuna docker ps Her çalışan docker konteyner için konteyner kimlikleri almak. Herhangi bir containerId için çalıştırabilirim docker inspect ${containerId} Bu konteyner ile ilgili birçok ayrıntıyı içeren JSON'u döndürür, özellikle NetworkSettings.IPAddress o konteynere bağlı (keşif uygulamam için gereken ana şey).

Bu değeri almak için AWS SDK'yı Lambda içinden kullanmaya çalışıyorum. İşte benim Lambda işlevim şu ana kadar (bunu da çalıştırabilmelisiniz - benim kurulumuma özel bir şey değil):

exports.handler = (event, context, callback) => {
    var AWS = require('aws-sdk'),
        ecs = new AWS.ECS({"apiVersion": '2014-11-13'});

    ecs.listClusters({}, (err, data) => {
        data.clusterArns.map((clusterArn) => {
            ecs.listTasks({
                cluster: clusterArn
            }, (err, data) => {
                ecs.describeTasks({
                    cluster: clusterArn,
                    tasks: data.taskArns
                }, (err, data) => {
                   if (err) console.log(err, err.stack); 
                   else     console.log(JSON.stringify(data, null, 4));
                })
            });
        })
    })
};

Çıktısı describeTasks çağrı oldukça işe yaramaz. Neredeyse kadar detayı yok. docker inspect çağrı üretir, özellikle de görevi çalıştıran docker kabının IP adresini içermez.

İhtiyacım olan verileri bulmaya da çalıştım. describeContainerInstances çağrı, ama beklendiği gibi herhangi bir göreve özel ayrıntıları vermedi.

Koşmayı denemek isterim docker inspect Eğer doğrudan Lambda'dan yapmanın bir yolu varsa, EC2 hostunda. SDK üzerinden konteyner üzerinde komutların çalıştırılmasının mümkün olup olmadığından emin değilim; muhtemelen değil. Bu nedenle, ECS konteyner görüntüsünün özel olarak yapılmış bir sürümü üzerinde çalışan özel bir servis yapmak zorunda kalacağım.

AWS SDK'yı kullanarak bu kapsayıcı IP adreslerini nasıl elde edebilirim? Veya ECS'deki genel servis keşif probleminin nasıl çözüleceğine dair daha iyi bir fikir mi?


5
2018-05-07 18:27


Menşei




Cevaplar:


Bu benim orijinal öncülümü (hizmet deposunu bulmak için görev deposunun kendi iç IP adresini bilmeye ihtiyaç duyuyor) çok kusurlu - IP adresi sadece bir EC2 Container Instance içinde kullanılabilir. Birden fazla kapsayıcı örneğiniz varsa (muhtemelen sahip olmanız gereken), bu görev kapsayıcı IP'leri temel olarak işe yaramaz.

Geldiğim alternatif çözüm, HTTP / HTTPS çalıştıran Uygulama Yük Dengeleyicileri için önerilen kalıbı takip etmektir - kullanmak zorunda olduğum docker örneğindeki bağlantı noktasını işaret ederek, ana bilgisayar bağlantı noktası olarak 0 ile bir bağlantı noktası eşlemesine sahip olmak. Bunu yaparak Docker, daha sonra AWS SDK'sını, özellikle de ECS modülünde bulunan "describeTasks" işlevini kullanarak bulabileceğim rastgele bir ana bilgisayar portu atayacaktır. Detaylar için buraya bakınız: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ECS.html#describeTasks-property

Bu benim kendi hizmet keşif mekanizmamın temel dayanağıdır - bunu tam bir şekilde yapmak için gereken başka birçok ayrıntı vardır. Ana makine kapsayıcılarımın listesini güncel tutmak için AWS SDK'ya ve PostgreSQL veritabanına çağrı yapan Lambda işlevlerini kullandım (dinamik bir DNS kaydı gibi biraz). Numaraların bir kısmı, kapların her biri için IP ve bağlantı noktasını bilmeniz gerektiğidir, ancak descriptionTasks yalnızca bağlantı noktasını döndürür. İşte bir konteyner adını alır ve bu adın kapsayıcıları için kümede bulunan tüm IP adresleri ve bağlantı noktalarını arar, yazdığım kullanışlı bir NodeJS işlevi:

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
            })
        )
    );
};

Bunun 'Q' söz kütüphanesini kullandığını unutmayın - bunu package.json'daki bir bağımlılık olarak bildirmeniz gerekir.

Lambda fonksiyonlarını kullanarak ECS servis keşfi için benim özel çözüm geri kalanı burada bulunabilir: https://github.com/jakefeasel/sqlfiddle3#setting-up-in-amazon-web-services


4
2018-05-30 05:53





Hizmetleriniz HTTP olmasa bile Klasik Elastik Yük Dengeleyici'yi ECS servisi ile ilişkilendirebilirsiniz. ELB'de TCP dinleyiciyi (HTTP veya HTTPs / SSL değil) oluşturduğunuzdan ve kapsayıcınızın açıktaki bağlantı noktasına baktığınızdan emin olun. Klasik ELB ve Uygulama ELB'yi kullanmanın dezavantajı, her ECS servisi için ayrı bir ELB'ye sahip olmanız gerektiğidir (ek maliyet).

http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html


1
2018-05-09 19:35



Fikrim için teşekkürler, farklı bir rotaya yöneldim (kendi cevabımla detaylandırıldı). Müşterilerim ve sunucular için bir ELB olan bir ELB'ye sahip olmaktan dolayı endişeleniyorum - herhangi bir TCP soket bağlantısını, herhangi bir müdahale olmadan halledebilir mi? Ayrıca her bir hizmet için ekstra ödeme yapmayı sevmiyorum - birkaç farklı bağımsız hizmet kullanmayı planlıyorum. Her neyse, bu ilginç bir fikirdi, bunu bildiğim için mutluyum. - Jake Feasel