Apa pendekatan yang tepat untuk membuat tugas Amazon ECS saya memperbarui gambar Docker mereka, setelah gambar tersebut diperbarui di registri yang sesuai?
Apa pendekatan yang tepat untuk membuat tugas Amazon ECS saya memperbarui gambar Docker mereka, setelah gambar tersebut diperbarui di registri yang sesuai?
Jawaban:
Jika tugas Anda berjalan di bawah layanan, Anda dapat memaksa penerapan baru. Ini memaksa definisi tugas untuk dievaluasi ulang dan gambar container baru ditarik.
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
Setiap kali Anda memulai tugas (baik melalui panggilan StartTask
dan RunTask
API atau yang dimulai secara otomatis sebagai bagian dari Layanan), Agen ECS akan melakukan docker pull
hal yang image
Anda tentukan dalam definisi tugas Anda. Jika Anda menggunakan nama gambar yang sama (termasuk tag) setiap kali Anda mendorong ke registri, Anda seharusnya dapat menjalankan gambar baru dengan menjalankan tugas baru. Perhatikan bahwa jika Docker tidak dapat mencapai registri karena alasan apa pun (misalnya, masalah jaringan atau masalah otentikasi), Agen ECS akan mencoba menggunakan gambar yang di-cache; jika Anda ingin menghindari gambar yang di-cache agar tidak digunakan saat Anda memperbarui gambar, Anda harus memasukkan tag yang berbeda ke registri Anda setiap kali dan memperbarui definisi tugas Anda secara bersamaan sebelum menjalankan tugas baru.
Pembaruan: Perilaku ini sekarang dapat disetel melalui ECS_IMAGE_PULL_BEHAVIOR
variabel lingkungan yang disetel pada agen ECS. Lihat dokumentasi untuk detailnya. Pada saat penulisan, pengaturan berikut ini didukung:
Perilaku yang digunakan untuk menyesuaikan proses gambar tarik untuk instance container Anda. Berikut ini penjelasan tentang perilaku opsional:
Jika
default
ditentukan, gambar ditarik dari jarak jauh. Jika penarikan gambar gagal, maka penampung menggunakan gambar yang di-cache pada instance.Jika
always
ditentukan, gambar selalu ditarik dari jarak jauh. Jika penarikan gambar gagal, maka tugas gagal. Opsi ini memastikan bahwa versi gambar terbaru selalu ditarik. Semua gambar yang disimpan dalam cache akan diabaikan dan tunduk pada proses pembersihan gambar otomatis.Jika
once
ditentukan, gambar akan ditarik dari jarak jauh hanya jika belum ditarik oleh tugas sebelumnya pada instance penampung yang sama atau jika gambar yang di-cache dihapus oleh proses pembersihan gambar otomatis. Jika tidak, gambar yang di-cache pada instance akan digunakan. Ini memastikan bahwa tidak ada penarikan gambar yang tidak perlu.Jika
prefer-cached
ditentukan, gambar ditarik dari jarak jauh jika tidak ada gambar yang disimpan dalam cache. Jika tidak, gambar yang di-cache pada instance akan digunakan. Pembersihan gambar otomatis dinonaktifkan untuk penampung guna memastikan bahwa gambar yang disimpan dalam cache tidak dihapus.
/var/log/ecs
.
Mendaftarkan definisi tugas baru dan memperbarui layanan untuk menggunakan definisi tugas baru adalah pendekatan yang direkomendasikan oleh AWS. Cara termudah untuk melakukannya adalah dengan:
Tutorial ini memiliki lebih banyak detail dan menjelaskan bagaimana langkah-langkah di atas cocok dengan proses pengembangan produk ujung ke ujung.
Pengungkapan penuh: Tutorial ini menampilkan wadah dari Bitnami dan saya bekerja untuk Bitnami. Namun pemikiran yang diungkapkan di sini adalah milik saya sendiri dan bukan pendapat Bitnami.
Ada dua cara untuk melakukannya.
Pertama, gunakan AWS CodeDeploy. Anda dapat mengonfigurasi bagian penerapan Biru / Hijau dalam definisi layanan ECS. Ini termasuk CodeDeployRoleForECS, TargetGroup lain untuk switch, dan Test Listener (opsional). AWS ECS akan membuat aplikasi CodeDeploy dan grup penerapan dan menautkan sumber daya CodeDeploy ini dengan ECS Cluster / Layanan dan ELB / TargetGroup untuk Anda. Kemudian Anda dapat menggunakan CodeDeploy untuk memulai penerapan, di mana Anda perlu memasukkan AppSpec yang menentukan penggunaan tugas / penampung apa untuk memperbarui layanan apa. Di sinilah Anda menentukan tugas / wadah baru Anda. Kemudian, Anda akan melihat instance baru berputar di TargetGroup baru dan TargetGroup lama terputus ke ELB, dan segera instance lama yang terdaftar ke TargetGroup lama akan dihentikan.
Ini terdengar sangat rumit. Sebenarnya, karena / jika Anda telah mengaktifkan penskalaan otomatis pada layanan ECS Anda, cara sederhana untuk melakukannya adalah dengan memaksa penerapan baru menggunakan konsol atau cli, seperti yang ditunjukkan oleh seorang pria di sini:
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
Dengan cara ini, Anda masih dapat menggunakan jenis penerapan "pembaruan berkelanjutan", dan ECS akan menjalankan instans baru dan menguras instans lama tanpa downtime layanan Anda jika semuanya baik-baik saja. Sisi buruknya adalah Anda kehilangan kendali yang baik pada penerapan dan Anda tidak dapat memutar kembali ke versi sebelumnya jika ada kesalahan dan ini akan merusak layanan yang sedang berjalan. Tapi ini adalah cara yang sangat sederhana untuk pergi.
BTW, jangan lupa untuk menetapkan angka yang tepat untuk persen sehat Minimum dan persen Maksimum, seperti 100 dan 200.
saya menciptakan skrip untuk menerapkan image Docker yang diperbarui ke layanan staging di ECS, sehingga definisi tugas yang sesuai mengacu pada versi image Docker saat ini. Saya tidak tahu pasti apakah saya mengikuti praktik terbaik, jadi masukan akan kami terima.
Agar skrip berfungsi, Anda memerlukan instans ECS cadangan atau file deploymentConfiguration.minimumHealthyPercent
nilai sehingga ECS dapat mencuri instans untuk menerapkan definisi tugas yang diperbarui.
Algoritme saya seperti ini:
Kode saya ditempel di bawah ini:
#!/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
Berikut ini berfungsi untuk saya jika tag gambar buruh pelabuhan sama:
Mengalami masalah yang sama. Setelah menghabiskan berjam-jam, telah menyimpulkan langkah-langkah yang disederhanakan ini untuk penerapan otomatis dari gambar yang diperbarui:
1. Perubahan definisi tugas ECS: Untuk pemahaman yang lebih baik, anggaplah Anda telah membuat definisi tugas dengan detail di bawah ini (catatan: angka-angka ini akan berubah sesuai definisi tugas Anda):
launch_type = EC2
desired_count = 1
Maka Anda perlu melakukan perubahan berikut:
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. Beri tag gambar Anda sebagai < your-image-name>: terbaru . Kunci terbaru menangani penarikan oleh tugas ECS masing-masing.
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. Dorong gambar ke ECR
sudo docker push <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest
4. menerapkan penerapan paksa
sudo aws ecs update-service --cluster <your-cluster-name> --service <your-service-name> --force-new-deployment --region us-east-1
Catatan: Saya telah menulis semua perintah dengan asumsi wilayah menjadi us-east-1 . Cukup ganti dengan wilayah Anda masing-masing saat menerapkan.
Menggunakan AWS cli, saya mencoba layanan pembaruan aws ecs seperti yang disarankan di atas. Tidak mengambil buruh pelabuhan terbaru dari ECR. Pada akhirnya, saya menjalankan kembali buku pedoman Yang Mungkin saya yang membuat kluster ECS. Versi definisi tugas diganti saat ecs_taskdefinition dijalankan. Maka semuanya baik-baik saja. Gambar buruh pelabuhan baru diambil.
Benar-benar tidak yakin apakah perubahan versi tugas memaksa penerapan ulang, atau jika pedoman yang menggunakan ecs_service menyebabkan tugas dimuat ulang.
Jika ada yang tertarik, saya akan mendapatkan izin untuk menerbitkan versi buku pedoman saya yang sudah dibersihkan.
Saya juga mencoba menemukan cara otomatis untuk melakukannya, yaitu mendorong perubahan ke ECR dan kemudian tag terbaru harus diambil oleh layanan. Benar, Anda dapat melakukannya secara manual dengan Menghentikan tugas untuk layanan Anda dari cluster Anda. Tugas baru akan menarik kontainer ECR yang diperbarui.
Perintah berikut berhasil untuk saya
docker build -t <repo> .
docker push <repo>
ecs-cli compose stop
ecs-cli compose start