Güncellenen Docker görüntülerini Amazon ECS görevlerine nasıl dağıtırım?


110

İlgili kayıt defterinde söz konusu görüntüler güncellendikten sonra, Amazon ECS görevlerimin Docker görüntülerini güncellemesini sağlamak için doğru yaklaşım nedir ?


Otomatik / zamanlanmış bir Lambda işlevi çalıştırmanızı tavsiye ederim. Bu şekilde, örneğin dışında. Bunu denedin mi? Bir seferde adımlar atmak için SWF'yi de kullanabilirsiniz
iSkore

@İSkore'da otomatikleştirmeme gerek yok. Sonunda bunun için bir komut dosyası yazmak istiyorum, ancak ne zaman çalıştıracağımı kendim seçiyorum.
aknuds1

Ahh anladım. Bundan emin değildim. Biraz daha bilgi verebilir misiniz?
iSkore

@iSkore Bunu zaten yaptığımdan daha iyi nasıl tarif edeceğimi bilmiyorum. Prosedür: 1. Docker görüntüsünün yeni sürümünü kayıt defterine aktarın. 2. Yeni görüntü sürümünü ECS'ye dağıtın. Soru, ikincisinin nasıl uygulanacağıdır.
aknuds1

bu da EKS için kolay veya açık değil ... F, nasıl en yaygın görev olan bir küme kullanmak, yeni bir görüntü dağıtmak, bu nedenle dokümantasyonda bu kadar belirsiz?

Yanıtlar:


89

Göreviniz bir hizmet altında çalışıyorsa, yeni bir dağıtımı zorlayabilirsiniz. Bu, görev tanımının yeniden değerlendirilmesini ve yeni konteyner görüntüsünün çekilmesini zorlar.

aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment

1
Bunun işe yaraması için ECS örneklerinizde aynı boyutta ek bir görevi dağıtmak için yeterli kaynak olduğundan emin olmanız gerektiğini düşünüyorum. AWS'nin, eskisini sonlandırmadan önce, yeni bir görev örneğinin önceden başlatılmasını bekleyerek temelde bir hotswap gerçekleştirmeye çalıştığını varsayıyorum. Yapmazsanız, 0 çalışan örnekle "dağıtım" girişleri eklemeye devam eder.
Alex Fedulov

3
@AlexFedulov, evet, haklı olduğunu düşünüyorum. Yeni bir dağıtım oluştururken kesinti süresine maruz kalmamak için, 1) Yeni sürümü eski sürümle birlikte dağıtmaya yetecek kadar örnek hazırlayabilirsiniz. Bu, otomatik ölçeklendirme ile elde edilebilir. 2) Fargate dağıtım türünü kullanın. ECS'nin yenisini dağıtmadan önce eski hizmetinizi kaldırmasına izin vermek için hizmetin "minimum sağlıklı yüzdesi" parametresini 0 olarak ayarlayarak fazladan kaynak ayırmaktan kaçınabilirsiniz. Yine de bu biraz kesintiye neden olacaktır.
Dima

3
Bilinmeyen seçenekler: --force-new-deployment
user4674453

1
Bilinmeyen seçenekler: --force-new-deployment: awscli'yi yükseltme
Kyle Parisi

1
bu komutu denedim, konteyneri yeni imajla güncellemiyor, aynı eski imaja sahip başka bir konteyneri döndürüyor. Bu yüzden, hizmette istenen sayı = 1 olduğunu belirtmeme rağmen çalışan iki konteynerim oluyor
matematik

61

Bir görevi her başlattığınızda (ya StartTaskve RunTaskAPI çağrıları aracılığıyla ya da bir Hizmetin parçası olarak otomatik olarak başlatılan), ECS Aracısı görev tanımınızda docker pullbelirlediğiniz bir işlemi gerçekleştirir image. Kayıt defterinize her itişinizde aynı görüntü adını (etiket dahil) kullanırsanız, yeni bir görev çalıştırarak yeni görüntünün çalıştırılmasını sağlayabilmelisiniz. Docker herhangi bir nedenle kayıt defterine erişemezse (örneğin, ağ sorunları veya kimlik doğrulama sorunları), ECS Aracısının önbelleğe alınmış bir görüntüyü kullanmaya çalışacağını unutmayın; Görüntünüzü güncellediğinizde önbelleğe alınmış görüntülerin kullanılmasını önlemek istiyorsanız, yeni görevi çalıştırmadan önce kayıt defterinize her seferinde farklı bir etiket göndermek ve görev tanımınızı buna göre güncellemek isteyeceksiniz.

Güncelleme: Bu davranış artık ECS_IMAGE_PULL_BEHAVIORECS aracısında ayarlanan ortam değişkeni aracılığıyla ayarlanabilir. Ayrıntılar için belgelere bakın. Yazma anından itibaren aşağıdaki ayarlar desteklenmektedir:

Kapsayıcı örnekleriniz için çekme görüntüsü sürecini özelleştirmek için kullanılan davranış. Aşağıda isteğe bağlı davranışlar açıklanmaktadır:

  • Eğer defaultbelirtilirse, görüntü uzaktan çekilir. Görüntü çekme başarısız olursa, kapsayıcı örnekte önbelleğe alınan görüntüyü kullanır.

  • Eğer alwaysbelirtilirse, görüntü daima uzaktan çekilir. Görüntü çekme başarısız olursa, görev başarısız olur. Bu seçenek, görüntünün en son sürümünün her zaman çekilmesini sağlar. Önbelleğe alınan görüntüler yok sayılır ve otomatik görüntü temizleme işlemine tabi tutulur.

  • Eğer oncebelirtilirse, görüntü aynı kap örneğinde bir önceki görev tarafından çekilen edilmemiştir veya önbelleğe alınmış görüntü otomatik görüntü temizleme işlemi tarafından kaldırılması durumunda yalnızca uzaktan çekilir. Aksi takdirde, örnekteki önbelleğe alınmış görüntü kullanılır. Bu, gereksiz görüntü çekmeye teşebbüs edilmemesini sağlar.

  • Eğer prefer-cachedbelirtilirse hiçbir önbelleğe alınmış görüntü varsa, görüntü uzaktan çekilir. Aksi takdirde, örnekteki önbelleğe alınmış görüntü kullanılır. Önbelleğe alınan görüntünün kaldırılmamasını sağlamak için kapsayıcı için otomatik görüntü temizleme devre dışı bırakılır.


4
Emin misiniz? Dockerhub'a yeni bir görüntü gönderdikten sonra (aynı etiket adını kullanarak) eski docker görüntülerinin çalıştırıldığı örnekleri gördüm. Sanırım her yeni görüntü oluşturulduğunda etiket adını çarpmalıyım. Ancak, bu benim deneyimime göre oldukça nadirdi, bu yüzden belki de anlık ağ sorunlarıydı. (ECS üzerinde çalıştığınızın farkındayım, bu yüzden bunu yanıtlayacak en iyi kişi sizsiniz, ancak bu tam olarak benim deneyimlediğim şey değil. Bu kaba gelirse özür dilerim, niyetim değil!)
Ibrahim

1
Evet, şu anki davranış, her seferinde bir çekmeye çalışacak olmasıdır. Çekme başarısız olursa (ağ sorunları, izin eksikliği vb.), Önbelleğe alınmış bir görüntüyü kullanmaya çalışacaktır. Daha fazla ayrıntıyı genellikle içinde bulunan aracı günlük dosyalarında bulabilirsiniz /var/log/ecs.
Samuel Karp

26

Yeni bir görev tanımının kaydedilmesi ve hizmeti yeni görev tanımını kullanacak şekilde güncellemek, AWS tarafından önerilen yaklaşımdır. Bunu yapmanın en kolay yolu şudur:

  1. Görev Tanımlarına gidin
  2. Doğru görevi seçin
  3. Yeni revizyon oluşturmayı seçin
  4. : Latest etiketi gibi bir şeyle kapsayıcı görüntüsünün en son sürümünü zaten çekiyorsanız, Oluştur'u tıklamanız yeterlidir. Aksi takdirde, kapsayıcı görüntüsünün sürüm numarasını güncelleyin ve ardından Oluştur'u tıklayın.
  5. İşlemleri Genişlet
  6. Güncelleme Hizmeti'ni seçin (iki kez)
  7. Ardından hizmetin yeniden başlatılmasını bekleyin

Bu öğreticide daha fazla ayrıntı vardır ve yukarıdaki adımların uçtan uca bir ürün geliştirme sürecine nasıl uyduğunu açıklar.

Tam açıklama: Bu eğitimde Bitnami'den konteynerler yer alıyor ve ben Bitnami için çalışıyorum. Ancak burada ifade edilen düşünceler bana aittir ve Bitnami'nin görüşü değildir.


3
Bu işe yarar, ancak hizmet min / maks değerlerinizi değiştirmeniz gerekebilir. Yalnızca bir EC2 bulut sunucunuz varsa, minimum sağlıklılık yüzdesini sıfıra ayarlamanız gerekir; aksi takdirde, güncellenen kapsayıcıyı dağıtmak için görevi asla sonlandırmaz (hizmetinizi geçici olarak çevrimdışı yapar).
Malvineous

3
@Malvineous İyi nokta! Gelen yazının ECS ayarları bölümünde , ben buna pek de açıklar. İşte o bölümden önerilen yapılandırma: Görev sayısı - 1, Minimum sağlıklı yüzdesi - 0, Maksimum yüzde - 200.
Neal

@Neal, burada belirtildiği gibi yaklaşımınızı denedim ... hala sevinç yok
Hafiz

@Hafiz Bunu anlamak için yardıma ihtiyacınız varsa, ne kadar ilerlediğinizi ve hangi hatayı vurduğunuzu açıklamalısınız.
Neal

Bu yalnızca hizmetler için işe yarar, hizmetler olmayan görevler için geçerli değildir.
zaitsman

9

Bunu yapmanın iki yolu var.

Öncelikle AWS CodeDeploy'u kullanın. ECS hizmet tanımında Mavi / Yeşil dağıtım bölümlerini yapılandırabilirsiniz. Bu, bir CodeDeployRoleForECS, anahtar için başka bir TargetGroup ve bir test Dinleyici (isteğe bağlı) içerir. AWS ECS, CodeDeploy uygulama ve dağıtım grubu oluşturacak ve bu CodeDeploy kaynaklarını sizin için ECS Kümenize / Hizmetinize ve ELB / Hedef Gruplarınıza bağlayacaktır. Ardından, hangi hizmeti güncellemek için hangi görevi / kapsayıcının kullanılacağını belirten bir AppSpec girmeniz gereken bir dağıtımı başlatmak için CodeDeploy'u kullanabilirsiniz. Yeni görevinizi / kapsayıcınızı belirttiğiniz yer burasıdır. Ardından, yeni hedef grupların yeni Hedef Grubunda döndüğünü ve eski Hedef Grubun ELB ile bağlantısının kesildiğini göreceksiniz ve yakında eski Hedef Grubuna kayıtlı eski örnekler sonlandırılacaktır.

Bu çok karmaşık görünüyor. Aslında, ECS hizmetinizde otomatik ölçeklendirmeyi etkinleştirdiyseniz, bunu yapmanın basit bir yolu, bir beyefendinin belirttiği gibi, konsol veya cli kullanarak yeni bir dağıtım yapmaya zorlamaktır:

aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment

Bu şekilde, "sürekli güncelleme" dağıtım türünü kullanmaya devam edebilirsiniz ve ECS, her şey yolundaysa hizmetinizde kesinti olmadan yeni bulut sunucuları çalıştırır ve eskileri boşaltır. Kötü tarafı, dağıtımdaki hassas kontrolü kaybedersiniz ve bir hata varsa önceki sürüme geri dönemezsiniz ve bu, devam eden hizmeti bozar. Ancak bu, gitmenin gerçekten basit bir yoludur.

BTW, Minimum sağlıklı yüzde ve Maksimum yüzde için 100 ve 200 gibi doğru sayıları ayarlamayı unutmayın.


IP'yi değiştirmek zorunda kalmadan bunu yapmanın bir yolu var mı? Benimkinde bunu çalıştırdığımda işe yaradı ama çalıştırdığım Özel IP'yi değiştirdi
Migdotcom

@Migdotcom Proxy NLB'ye ihtiyaç duyduğumda benzer bir sorun yaşadım. Kısacası, bir EC2 bulut sunucusu IP'sini aynı tutmanın tek yolu, esnek IP adresleri kullanmak veya farklı bir yaklaşım kullanmaktır. Kullanım durumunuzu bilmiyorum ancak Global Accelerator'ı ECS bağlantılı ALB'ye bağlamak bana statik IP adresleri sağladı, bu benim kullanım durumumu çözdü. Dinamik dahili IP'leri bilmek istiyorsanız, ALB'yi bir lambda ile sorgulamanız gerekecektir. Bu çok çaba sarf etti. Aşağıdaki bağlantı: aws.amazon.com/blogs/networking-and-content-delivery/…
Marcus,

aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment benim için çalıştı!
gvasquez

3

Güncellenmiş Docker görüntülerini ECS üzerindeki bir hazırlama hizmetine dağıtmak için bir komut dosyası oluşturdum , böylece karşılık gelen görev tanımı Docker görüntülerinin mevcut sürümlerini ifade eder. En iyi uygulamaları takip edip etmediğimi kesin olarak bilmiyorum, bu nedenle geri bildirimler memnuniyetle karşılanacaktır.

Komut dosyasının çalışması için, ECS'nin deploymentConfiguration.minimumHealthyPercentgüncellenmiş görev tanımını dağıtmak üzere bir örneği çalabilmesi için yedek bir ECS örneğine veya bir değere ihtiyacınız vardır.

Algoritmam şöyle:

  1. Git revizyonu ile görev tanımındaki kapsayıcılara karşılık gelen Docker görüntülerini etiketleyin.
  2. Docker görüntü etiketlerini ilgili kayıtlara gönderin.
  3. Görev tanımı ailesindeki eski görev tanımlarının kaydını silin.
  4. Yeni görev tanımını kaydedin, şimdi mevcut Git revizyonlarıyla etiketlenmiş Docker görüntülerine atıfta bulunun.
  5. Yeni görev tanımını kullanmak için hizmeti güncelleyin.

Kodum aşağıya yapıştırıldı:

dağıtım-ec'ler

#!/usr/bin/env python3
import subprocess
import sys
import os.path
import json
import re
import argparse
import tempfile

_root_dir = os.path.abspath(os.path.normpath(os.path.dirname(__file__)))
sys.path.insert(0, _root_dir)
from _common import *


def _run_ecs_command(args):
    run_command(['aws', 'ecs', ] + args)


def _get_ecs_output(args):
    return json.loads(run_command(['aws', 'ecs', ] + args, return_stdout=True))


def _tag_image(tag, qualified_image_name, purge):
    log_info('Tagging image \'{}\' as \'{}\'...'.format(
        qualified_image_name, tag))
    log_info('Pulling image from registry in order to tag...')
    run_command(
        ['docker', 'pull', qualified_image_name], capture_stdout=False)
    run_command(['docker', 'tag', '-f', qualified_image_name, '{}:{}'.format(
        qualified_image_name, tag), ])
    log_info('Pushing image tag to registry...')
    run_command(['docker', 'push', '{}:{}'.format(
        qualified_image_name, tag), ], capture_stdout=False)
    if purge:
        log_info('Deleting pulled image...')
        run_command(
            ['docker', 'rmi', '{}:latest'.format(qualified_image_name), ])
        run_command(
            ['docker', 'rmi', '{}:{}'.format(qualified_image_name, tag), ])


def _register_task_definition(task_definition_fpath, purge):
    with open(task_definition_fpath, 'rt') as f:
        task_definition = json.loads(f.read())

    task_family = task_definition['family']

    tag = run_command([
        'git', 'rev-parse', '--short', 'HEAD', ], return_stdout=True).strip()
    for container_def in task_definition['containerDefinitions']:
        image_name = container_def['image']
        _tag_image(tag, image_name, purge)
        container_def['image'] = '{}:{}'.format(image_name, tag)

    log_info('Finding existing task definitions of family \'{}\'...'.format(
        task_family
    ))
    existing_task_definitions = _get_ecs_output(['list-task-definitions', ])[
        'taskDefinitionArns']
    for existing_task_definition in [
        td for td in existing_task_definitions if re.match(
            r'arn:aws:ecs+:[^:]+:[^:]+:task-definition/{}:\d+'.format(
                task_family),
            td)]:
        log_info('Deregistering task definition \'{}\'...'.format(
            existing_task_definition))
        _run_ecs_command([
            'deregister-task-definition', '--task-definition',
            existing_task_definition, ])

    with tempfile.NamedTemporaryFile(mode='wt', suffix='.json') as f:
        task_def_str = json.dumps(task_definition)
        f.write(task_def_str)
        f.flush()
        log_info('Registering task definition...')
        result = _get_ecs_output([
            'register-task-definition',
            '--cli-input-json', 'file://{}'.format(f.name),
        ])

    return '{}:{}'.format(task_family, result['taskDefinition']['revision'])


def _update_service(service_fpath, task_def_name):
    with open(service_fpath, 'rt') as f:
        service_config = json.loads(f.read())
    services = _get_ecs_output(['list-services', ])[
        'serviceArns']
    for service in [s for s in services if re.match(
        r'arn:aws:ecs:[^:]+:[^:]+:service/{}'.format(
            service_config['serviceName']),
        s
    )]:
        log_info('Updating service with new task definition...')
        _run_ecs_command([
            'update-service', '--service', service,
            '--task-definition', task_def_name,
        ])


parser = argparse.ArgumentParser(
    description="""Deploy latest Docker image to staging server.
The task definition file is used as the task definition, whereas
the service file is used to configure the service.
""")
parser.add_argument(
    'task_definition_file', help='Your task definition JSON file')
parser.add_argument('service_file', help='Your service JSON file')
parser.add_argument(
    '--purge_image', action='store_true', default=False,
    help='Purge Docker image after tagging?')
args = parser.parse_args()

task_definition_file = os.path.abspath(args.task_definition_file)
service_file = os.path.abspath(args.service_file)

os.chdir(_root_dir)

task_def_name = _register_task_definition(
    task_definition_file, args.purge_image)
_update_service(service_file, task_def_name)

_common.py

import sys
import subprocess


__all__ = ['log_info', 'handle_error', 'run_command', ]


def log_info(msg):
    sys.stdout.write('* {}\n'.format(msg))
    sys.stdout.flush()


def handle_error(msg):
    sys.stderr.write('* {}\n'.format(msg))
    sys.exit(1)


def run_command(
        command, ignore_error=False, return_stdout=False, capture_stdout=True):
    if not isinstance(command, (list, tuple)):
        command = [command, ]
    command_str = ' '.join(command)
    log_info('Running command {}'.format(command_str))
    try:
        if capture_stdout:
            stdout = subprocess.check_output(command)
        else:
            subprocess.check_call(command)
            stdout = None
    except subprocess.CalledProcessError as err:
        if not ignore_error:
            handle_error('Command failed: {}'.format(err))
    else:
        return stdout.decode() if return_stdout else None

@Andris Teşekkürler, düzeltildi.
aknuds1

5
Bu çok fazla. Terraform veya sadece tek bir ecs-cli hattı ile konuşlandırılması mümkün olmalıdır.
holms

@holms ECS görev görüntüsünü güncellemek için Terraform kullanıyorum. Bu, yukarıdaki python kodu kadar aşırıdır. Gerekli adımlar bir o kadar karmaşıktır.
Jari Turkia

3

AWS CodePipeline.

ECR'yi bir kaynak ve ECS'yi dağıtılacak bir hedef olarak ayarlayabilirsiniz.


2
bunun için herhangi bir dokümantasyona bağlantı verebilir misiniz?
BenDog

2

Aynı sorunla karşılaştım. Saatler geçirdikten sonra, güncellenmiş görüntünün otomatik olarak dağıtılması için şu basitleştirilmiş adımları tamamladık:

1. ECS görev tanımı değişiklikleri: Daha iyi bir anlayış için, aşağıdaki ayrıntılarla bir görev tanımı oluşturduğunuzu varsayalım (not: bu sayılar, görev tanımınıza göre buna göre değişecektir):

launch_type = EC2

desired_count = 1

O zaman aşağıdaki değişiklikleri yapmanız gerekir:

deployment_minimum_healthy_percent = 0  //this does the trick, if not set to zero the force deployment wont happen as ECS won't allow to stop the current running task

deployment_maximum_percent = 200  //for allowing rolling update

2. Resminizi < resminizin-adı>: en yeni olarak etiketleyin . En son anahtar, ilgili ECS görevi tarafından çekilmeyle ilgilenir.

sudo docker build -t imageX:master .   //build your image with some tag
sudo -s eval $(aws ecr get-login --no-include-email --region us-east-1)  //login to ECR
sudo docker tag imageX:master <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest    //tag your image with latest tag

3. Görüntüyü ECR'ye itin

sudo docker push  <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest

4. zorla dağıtım uygulayın

sudo aws ecs update-service --cluster <your-cluster-name> --service <your-service-name> --force-new-deployment --region us-east-1

Not: Bölgenin us-east-1 olduğunu varsayarak tüm komutları yazdım . Uygulama sırasında sadece kendi bölgenizle değiştirin.


Parametrelerin terraform parametreler olduğunu fark ettim; CloudFormation için aynı sonuca nasıl ulaşılacağına dair herhangi bir fikir: AutoScalingGroup MinSize: 0 ve MaxSize: 1'e sahibim; başka ne ayarlanması gerekiyor?
Wayne

1

Docker görüntü etiketinin aynı olması durumunda aşağıdakiler benim için çalıştı:

  1. Kümeye ve hizmete gidin.
  2. Servisi seçin ve güncelle'ye tıklayın.
  3. Görev sayısını 0 olarak ayarlayın ve güncelleyin.
  4. Dağıtım tamamlandıktan sonra, görev sayısını 1 olarak yeniden ölçeklendirin.

0

AWS cli kullanarak yukarıda önerildiği gibi aws ecs güncelleme hizmetini denedim. ECR'den en son docker alınmadı. Sonunda, ECS kümesini oluşturan Ansible başucu kitabımı yeniden çalıştırıyorum. Ecs_taskdefinition çalıştığında görev tanımının sürümü çarpılıyor. O zaman her şey yolunda. Yeni docker görüntüsü alınır.

Görev sürümü değişikliğinin yeniden dağıtımı zorlayıp zorlamayacağından veya ecs_service kullanan başucu kitabının görevin yeniden yüklenmesine neden olup olmadığından gerçekten emin değilim.

İlgilenen varsa, başucu kitabımın sterilize edilmiş bir sürümünü yayınlama izni alacağım.


Görev tanımı revizyonunun yalnızca gerçek görev tanımı yapılandırmasını güncellediğinizde gerekli olduğuna inanıyorum. bu durumda en son etiketi olan bir resim kullanıyorsanız, yapılandırmayı değiştirmenize gerek yoktur? Elbette bir etiket olarak commit id'sine sahip olmak güzeldir ve geri alabilmeniz için ayrı bir görev tanımı revizyonuna sahip olmak da güzeldir, ancak o zaman CI'nız konteyner için kullandığınız tüm kimlik bilgilerini görecek ve bu benim şeyleri uygulamak istediğim yol değil.
holms

0

Ben de bunu yapmanın otomatik bir yolunu bulmaya çalışıyorum, yani değişiklikleri ECR'ye itmek ve ardından en son etiket servis tarafından alınmalıdır. Kümenizden hizmetiniz için görevi Durdurarak bunu manuel olarak yapabilirsiniz. Yeni görevler güncellenmiş ECR kapsayıcılarını çekecektir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.