İ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 ?
İ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 ?
Yanıtlar:
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
Bir görevi her başlattığınızda (ya StartTask
ve RunTask
API ç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 pull
belirlediğ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_BEHAVIOR
ECS 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
default
belirtilirse, 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
always
belirtilirse, 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
once
belirtilirse, 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-cached
belirtilirse 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.
/var/log/ecs
.
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:
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.
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.
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.minimumHealthyPercent
gü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:
Kodum aşağıya yapıştırıldı:
#!/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)
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
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.
Docker görüntü etiketinin aynı olması durumunda aşağıdakiler benim için çalıştı:
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.
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.
Aşağıdaki komutlar benim için çalıştı
docker build -t <repo> .
docker push <repo>
ecs-cli compose stop
ecs-cli compose start