แนวทางที่ถูกต้องในการทำให้งานAmazon ECSของฉันอัปเดตอิมเมจ Docker ของพวกเขาคืออะไรเมื่อกล่าวว่ารูปภาพได้รับการอัปเดตในรีจิสทรีที่เกี่ยวข้องแล้ว
แนวทางที่ถูกต้องในการทำให้งานAmazon ECSของฉันอัปเดตอิมเมจ Docker ของพวกเขาคืออะไรเมื่อกล่าวว่ารูปภาพได้รับการอัปเดตในรีจิสทรีที่เกี่ยวข้องแล้ว
คำตอบ:
หากงานของคุณกำลังทำงานภายใต้บริการคุณสามารถบังคับให้ปรับใช้ใหม่ได้ ซึ่งจะบังคับให้นิยามงานได้รับการประเมินใหม่และดึงอิมเมจคอนเทนเนอร์ใหม่
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
ทุกครั้งที่คุณเริ่มงาน (ไม่ว่าจะผ่านการเรียกStartTask
และRunTask
API หรือเริ่มโดยอัตโนมัติโดยเป็นส่วนหนึ่งของบริการ) ตัวแทน ECS จะดำเนินdocker pull
การimage
คุณระบุในนิยามงานของคุณ หากคุณใช้ชื่อรูปภาพเดียวกัน (รวมถึงแท็ก) ทุกครั้งที่คุณพุชไปยังรีจิสตรีของคุณคุณจะสามารถเรียกใช้รูปภาพใหม่ได้โดยการรันงานใหม่ โปรดทราบว่าหาก Docker ไม่สามารถเข้าถึงรีจิสทรีไม่ว่าด้วยเหตุผลใด ๆ (เช่นปัญหาเครือข่ายหรือปัญหาการตรวจสอบสิทธิ์) ECS Agent จะพยายามใช้ภาพแคช หากคุณต้องการหลีกเลี่ยงไม่ให้ใช้รูปภาพแคชเมื่อคุณอัปเดตรูปภาพของคุณคุณจะต้องพุชแท็กอื่นไปยังรีจิสตรีของคุณทุกครั้งและอัปเดตข้อกำหนดของงานให้สอดคล้องกันก่อนที่จะเรียกใช้งานใหม่
อัปเดต: ขณะนี้สามารถปรับพฤติกรรมนี้ผ่านECS_IMAGE_PULL_BEHAVIOR
ตัวแปรสภาพแวดล้อมที่ตั้งค่าบนเอเจนต์ ECS ดูรายละเอียดในเอกสารประกอบ ในขณะที่เขียนรองรับการตั้งค่าต่อไปนี้:
ลักษณะการทำงานที่ใช้ในการปรับแต่งกระบวนการดึงอิมเมจสำหรับอินสแตนซ์คอนเทนเนอร์ของคุณ ต่อไปนี้จะอธิบายถึงพฤติกรรมทางเลือก:
หาก
default
ระบุไว้ภาพจะถูกดึงจากระยะไกล หากการดึงรูปภาพล้มเหลวคอนเทนเนอร์จะใช้รูปภาพแคชบนอินสแตนซ์หาก
always
ระบุไว้รูปภาพจะถูกดึงจากระยะไกลเสมอ หากการดึงภาพล้มเหลวแสดงว่างานนั้นล้มเหลว ตัวเลือกนี้ช่วยให้แน่ใจว่ามีการดึงรูปภาพเวอร์ชันล่าสุดเสมอ รูปภาพที่แคชไว้จะถูกละเว้นและอยู่ภายใต้กระบวนการล้างข้อมูลรูปภาพอัตโนมัติหาก
once
ระบุไว้รูปภาพจะถูกดึงจากระยะไกลก็ต่อเมื่อไม่ได้ดึงโดยงานก่อนหน้าบนอินสแตนซ์คอนเทนเนอร์เดียวกันหรือหากอิมเมจที่แคชถูกลบออกโดยกระบวนการล้างข้อมูลอิมเมจอัตโนมัติ มิฉะนั้นจะใช้รูปภาพแคชบนอินสแตนซ์ เพื่อให้แน่ใจว่าไม่มีการพยายามดึงภาพที่ไม่จำเป็นหาก
prefer-cached
ระบุไว้รูปภาพจะถูกดึงจากระยะไกลหากไม่มีรูปภาพแคช มิฉะนั้นจะใช้รูปภาพแคชบนอินสแตนซ์ การล้างข้อมูลอิมเมจอัตโนมัติถูกปิดใช้งานสำหรับคอนเทนเนอร์เพื่อให้แน่ใจว่ารูปภาพที่แคชจะไม่ถูกลบออก
/var/log/ecs
.
การลงทะเบียนข้อกำหนดงานใหม่และการอัปเดตบริการเพื่อใช้นิยามงานใหม่เป็นแนวทางที่ AWS แนะนำ วิธีที่ง่ายที่สุดคือ:
บทแนะนำนี้มีรายละเอียดเพิ่มเติมและอธิบายว่าขั้นตอนข้างต้นเหมาะสมกับกระบวนการพัฒนาผลิตภัณฑ์แบบ end-to-end อย่างไร
การเปิดเผยข้อมูลทั้งหมด: บทช่วยสอนนี้มีคอนเทนเนอร์จาก Bitnami และฉันทำงานให้กับ Bitnami อย่างไรก็ตามความคิดที่แสดงในที่นี้เป็นของฉันเองไม่ใช่ความคิดเห็นของ Bitnami
มีสองวิธีในการทำเช่นนี้
ขั้นแรกให้ใช้ AWS CodeDeploy คุณสามารถกำหนดค่าส่วนการปรับใช้สีน้ำเงิน / เขียวในข้อกำหนดบริการ ECS ซึ่งรวมถึง CodeDeployRoleForECS, TargetGroup อื่นสำหรับสวิตช์และ Listener ทดสอบ (ไม่บังคับ) AWS ECS จะสร้างแอปพลิเคชัน CodeDeploy และกลุ่มการปรับใช้และเชื่อมโยงทรัพยากร CodeDeploy เหล่านี้กับคลัสเตอร์ / บริการ ECS ของคุณและ ELB / TargetGroups ให้กับคุณ จากนั้นคุณสามารถใช้ CodeDeploy เพื่อเริ่มการปรับใช้ซึ่งคุณต้องป้อน AppSpec ที่ระบุว่าใช้งาน / คอนเทนเนอร์ใดเพื่ออัปเดตบริการใด ที่นี่คุณระบุงาน / คอนเทนเนอร์ใหม่ของคุณ จากนั้นคุณจะเห็นอินสแตนซ์ใหม่หมุนขึ้นใน TargetGroup ใหม่และ TargetGroup เก่าถูกตัดการเชื่อมต่อกับ ELB และในไม่ช้าอินสแตนซ์เก่าที่ลงทะเบียนกับ TargetGroup เก่าจะถูกยกเลิก
ฟังดูซับซ้อนมาก อันที่จริงเนื่องจาก / ถ้าคุณเปิดใช้งานการปรับขนาดอัตโนมัติในบริการ ECS ของคุณวิธีง่ายๆในการทำก็คือบังคับให้ใช้งานใหม่โดยใช้คอนโซลหรือ cli เช่นสุภาพบุรุษที่นี่ชี้ให้เห็น:
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
ด้วยวิธีนี้คุณยังคงสามารถใช้ประเภทการปรับใช้ "การอัปเดตแบบต่อเนื่อง" ได้และ ECS จะหมุนอินสแตนซ์ใหม่และระบายอินสแตนซ์เก่าโดยไม่ต้องหยุดให้บริการหากทุกอย่างเรียบร้อย ด้านที่ไม่ดีคือคุณสูญเสียการควบคุมที่ดีในการปรับใช้และคุณไม่สามารถย้อนกลับไปใช้เวอร์ชันก่อนหน้าได้หากมีข้อผิดพลาดและจะทำให้บริการที่กำลังดำเนินอยู่เสียหาย แต่นี่เป็นวิธีง่ายๆที่จะไป
BTW อย่าลืมกำหนดตัวเลขที่เหมาะสมสำหรับเปอร์เซ็นต์สุขภาพขั้นต่ำและเปอร์เซ็นต์สูงสุดเช่น 100 และ 200
ฉันสร้างสคริปต์สำหรับปรับใช้อิมเมจ Docker ที่อัปเดตกับบริการการจัดเตรียมบน ECS เพื่อให้ข้อกำหนดงานที่เกี่ยวข้องอ้างอิงถึงเวอร์ชันปัจจุบันของอิมเมจ Docker ฉันไม่แน่ใจว่าฉันปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดหรือไม่ดังนั้นเรายินดีรับข้อเสนอแนะ
เพื่อให้สคริปต์ทำงานได้คุณต้องมีอินสแตนซ์ ECS สำรองหรือไฟล์ deploymentConfiguration.minimumHealthyPercent
ค่าเพื่อให้ ECS สามารถขโมยอินสแตนซ์เพื่อปรับใช้นิยามงานที่อัปเดต
อัลกอริทึมของฉันเป็นดังนี้:
รหัสของฉันวางด้านล่าง:
#!/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
AWS CodePipeline
คุณสามารถตั้งค่า ECR เป็นแหล่งที่มาและ ECS เป็นเป้าหมายเพื่อปรับใช้
พบปัญหาเดียวกัน หลังจากใช้เวลาหลายชั่วโมงได้สรุปขั้นตอนที่เรียบง่ายเหล่านี้สำหรับการปรับใช้อิมเมจที่อัปเดตโดยอัตโนมัติ:
1. การเปลี่ยนแปลงนิยามงาน ECS: เพื่อความเข้าใจที่ดีขึ้นสมมติว่าคุณได้สร้างนิยามงานโดยมีรายละเอียดด้านล่าง (หมายเหตุ: ตัวเลขเหล่านี้จะเปลี่ยนไปตามนิยามงานของคุณ):
launch_type = EC2
desired_count = 1
จากนั้นคุณต้องทำการเปลี่ยนแปลงต่อไปนี้:
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.Tag ภาพของคุณเป็น < ของคุณภาพชื่อ>: ล่าสุด คีย์ล่าสุดดูแลการดึงโดยงาน ECS ที่เกี่ยวข้อง
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. ดันภาพไปที่ ECR
sudo docker push <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest
4. ใช้การบังคับใช้
sudo aws ecs update-service --cluster <your-cluster-name> --service <your-service-name> --force-new-deployment --region us-east-1
หมายเหตุ: ผมได้เขียนคำสั่งทั้งหมดสมมติภูมิภาคที่จะให้เราตะวันออก-1 เพียงแค่แทนที่ด้วยภูมิภาคของคุณในขณะที่ใช้งาน
สิ่งต่อไปนี้ใช้ได้ผลสำหรับฉันในกรณีที่แท็กรูปภาพนักเทียบท่าเหมือนกัน:
การใช้ AWS cli ฉันลองใช้บริการอัปเดต ecs ของ AWS ตามที่แนะนำข้างต้น ไม่ได้รับนักเทียบท่ารุ่นล่าสุดจาก ECR ในท้ายที่สุดฉันเปิดใช้งาน Ansible playbook ของฉันที่สร้างคลัสเตอร์ ECS อีกครั้ง เวอร์ชันของนิยามงานถูกชนเมื่อ ecs_taskdefinition รัน แล้วทั้งหมดเป็นสิ่งที่ดี ภาพนักเทียบท่าใหม่ถูกหยิบขึ้นมา
ไม่แน่ใจจริงๆว่าการเปลี่ยนแปลงเวอร์ชันของงานบังคับให้ใช้งานซ้ำหรือไม่หรือว่าเพลย์บุ๊กที่ใช้ ecs_service ทำให้งานโหลดซ้ำ
หากใครสนใจฉันจะได้รับอนุญาตให้เผยแพร่ Playbook เวอร์ชันที่ผ่านการฆ่าเชื้อแล้ว
ฉันกำลังพยายามหาวิธีอัตโนมัติในการทำนั่นคือการผลักดันการเปลี่ยนแปลงไปยัง ECR จากนั้นควรเลือกแท็กล่าสุดโดยบริการ คุณสามารถทำได้ด้วยตนเองโดยการหยุดงานสำหรับบริการของคุณจากคลัสเตอร์ของคุณ งานใหม่จะดึงคอนเทนเนอร์ ECR ที่ปรับปรุงแล้ว
เนื่องจากยังไม่มีความคืบหน้าใด ๆ ที่ฝั่ง AWS ฉันจะให้คุณหลามสคริปต์ง่ายๆที่ว่าทำตามขั้นตอนที่อธิบายไว้ในคำตอบที่สูงจัดอันดับของDimaและซามูเอลคาร์พ
ขั้นแรกให้พุชรูปภาพของคุณไปยัง AWS Registry ECR จากนั้นเรียกใช้สคริปต์:
import boto3, time
client = boto3.client('ecs')
cluster_name = "Example_Cluster"
service_name = "Example-service"
reason_to_stop = "obsolete deployment"
# Create new deployment; ECS Service forces to pull from docker registry, creates new task in service
response = client.update_service(cluster=cluster_name, service=service_name, forceNewDeployment=True)
# Wait for ecs agent to start new task
time.sleep(10)
# Get all Service Tasks
service_tasks = client.list_tasks(cluster=cluster_name, serviceName=service_name)
# Get meta data for all Service Tasks
task_meta_data = client.describe_tasks(cluster=cluster_name, tasks=service_tasks["taskArns"])
# Extract creation date
service_tasks = [(task_data['taskArn'], task_data['createdAt']) for task_data in task_meta_data["tasks"]]
# Sort according to creation date
service_tasks = sorted(service_tasks, key= lambda task: task[1])
# Get obsolete task arn
obsolete_task_arn = service_tasks[0][0]
print("stop ", obsolete_task_arn)
# Stop obsolete task
stop_response = client.stop_task(cluster=cluster_name, task=obsolete_task_arn, reason=reason_to_stop)
รหัสนี้ทำ:
คำสั่งต่อไปนี้ใช้ได้ผลสำหรับฉัน
docker build -t <repo> .
docker push <repo>
ecs-cli compose stop
ecs-cli compose start