Đâu là cách tiếp cận phù hợp để thực hiện các nhiệm vụ Amazon ECS của tôi cập nhật hình ảnh Docker của họ, sau khi hình ảnh được cho là đã được cập nhật trong sổ đăng ký tương ứng?
Đâu là cách tiếp cận phù hợp để thực hiện các nhiệm vụ Amazon ECS của tôi cập nhật hình ảnh Docker của họ, sau khi hình ảnh được cho là đã được cập nhật trong sổ đăng ký tương ứng?
Câu trả lời:
Nếu nhiệm vụ của bạn đang chạy trong một dịch vụ, bạn có thể bắt buộc triển khai mới. Điều này buộc định nghĩa nhiệm vụ phải được đánh giá lại và hình ảnh vùng chứa mới được kéo.
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
Mỗi khi bạn bắt đầu một nhiệm vụ (thông qua lệnh gọi StartTask
và RunTask
API hoặc được bắt đầu tự động như một phần của Dịch vụ), ECS Agent sẽ thực hiện một docker pull
trong các thao tác image
bạn chỉ định trong định nghĩa nhiệm vụ của mình. Nếu bạn sử dụng cùng một tên hình ảnh (bao gồm cả thẻ) mỗi khi bạn đẩy vào sổ đăng ký của mình, bạn sẽ có thể chạy hình ảnh mới bằng cách chạy một tác vụ mới. Lưu ý rằng nếu Docker không thể truy cập sổ đăng ký vì bất kỳ lý do gì (ví dụ: sự cố mạng hoặc sự cố xác thực), ECS Agent sẽ cố gắng sử dụng hình ảnh được lưu trong bộ nhớ cache; nếu bạn muốn tránh sử dụng hình ảnh đã lưu trong bộ nhớ cache khi cập nhật hình ảnh của mình, bạn sẽ muốn đẩy một thẻ khác vào sổ đăng ký của mình mỗi lần và cập nhật định nghĩa nhiệm vụ của bạn tương ứng trước khi chạy tác vụ mới.
Cập nhật: Hành vi này hiện có thể được điều chỉnh thông qua ECS_IMAGE_PULL_BEHAVIOR
biến môi trường được đặt trên tác nhân ECS. Xem tài liệu để biết chi tiết. Kể từ thời điểm viết bài, các cài đặt sau được hỗ trợ:
Hành vi được sử dụng để tùy chỉnh quy trình kéo hình ảnh cho các trường hợp vùng chứa của bạn. Phần sau mô tả các hành vi tùy chọn:
Nếu
default
được chỉ định, hình ảnh sẽ được kéo từ xa. Nếu kéo hình ảnh không thành công, thì vùng chứa sẽ sử dụng hình ảnh được lưu trong bộ nhớ cache trên phiên bản.Nếu
always
được chỉ định, hình ảnh luôn được kéo từ xa. Nếu kéo hình ảnh không thành công, thì tác vụ không thành công. Tùy chọn này đảm bảo rằng phiên bản mới nhất của hình ảnh luôn được kéo. Mọi hình ảnh đã lưu trong bộ nhớ cache đều bị bỏ qua và phải tuân theo quy trình dọn dẹp hình ảnh tự động.Nếu
once
được chỉ định, hình ảnh chỉ được kéo từ xa nếu nó chưa được kéo bởi tác vụ trước đó trên cùng một trường hợp vùng chứa hoặc nếu hình ảnh được lưu trong bộ nhớ cache đã bị xóa bởi quy trình dọn dẹp hình ảnh tự động. Nếu không, hình ảnh được lưu trong bộ nhớ cache trên phiên bản sẽ được sử dụng. Điều này đảm bảo rằng không có lần kéo hình ảnh không cần thiết nào được cố gắng.Nếu
prefer-cached
được chỉ định, hình ảnh sẽ được kéo từ xa nếu không có hình ảnh được lưu trong bộ nhớ cache. Nếu không, hình ảnh được lưu trong bộ nhớ cache trên phiên bản sẽ được sử dụng. Tính năng dọn dẹp hình ảnh tự động bị tắt cho vùng chứa để đảm bảo rằng hình ảnh được lưu trong bộ nhớ cache không bị xóa.
/var/log/ecs
.
Đăng ký định nghĩa nhiệm vụ mới và cập nhật dịch vụ để sử dụng định nghĩa nhiệm vụ mới là cách tiếp cận được AWS khuyến nghị. Cách dễ nhất để làm điều này là:
Hướng dẫn này chi tiết hơn và mô tả cách các bước trên phù hợp với quy trình phát triển sản phẩm từ đầu đến cuối.
Tiết lộ đầy đủ: Hướng dẫn này có các bộ chứa từ Bitnami và tôi làm việc cho Bitnami. Tuy nhiên, những suy nghĩ được thể hiện ở đây là của riêng tôi và không phải quan điểm của Bitnami.
Có hai cách để làm điều này.
Đầu tiên, hãy sử dụng AWS CodeDeploy. Bạn có thể cấu hình các phần triển khai Blue / Green trong định nghĩa dịch vụ ECS. Điều này bao gồm một CodeDeployRoleForECS, một TargetGroup khác cho switch và một Listener thử nghiệm (tùy chọn). AWS ECS sẽ tạo ứng dụng CodeDeploy và nhóm triển khai và liên kết các tài nguyên CodeDeploy này với Cụm / Dịch vụ ECS và ELB / Nhóm mục tiêu cho bạn. Sau đó, bạn có thể sử dụng CodeDeploy để bắt đầu triển khai, trong đó bạn cần nhập một AppSpec chỉ định sử dụng tác vụ / vùng chứa nào để cập nhật dịch vụ nào. Đây là nơi bạn chỉ định nhiệm vụ / vùng chứa mới của mình. Sau đó, bạn sẽ thấy các phiên bản mới xuất hiện trong TargetGroup mới và TargetGroup cũ bị ngắt kết nối với ELB và các phiên bản cũ đã đăng ký với TargetGroup cũ sẽ sớm bị chấm dứt.
Điều này nghe có vẻ rất phức tạp. Trên thực tế, vì / nếu bạn đã bật tính năng tự động mở rộng quy mô trên dịch vụ ECS của mình, một cách đơn giản để thực hiện là chỉ cần buộc triển khai mới bằng bảng điều khiển hoặc cli, như một quý ông ở đây đã chỉ ra:
aws ecs update-service --cluster <cluster name> --service <service name> --force-new-deployment
Bằng cách này, bạn vẫn có thể sử dụng kiểu triển khai "cập nhật lần lượt" và ECS sẽ chỉ đơn giản là tạo ra các phiên bản mới và rút các phiên bản cũ mà không có thời gian ngừng hoạt động của dịch vụ nếu mọi thứ đều ổn. Mặt xấu là bạn mất kiểm soát tốt việc triển khai và bạn không thể quay trở lại phiên bản trước nếu có lỗi và điều này sẽ phá vỡ dịch vụ đang diễn ra. Nhưng đây là một cách thực sự đơn giản để đi.
BTW, đừng quên đặt các số thích hợp cho Phần trăm khỏe mạnh tối thiểu và Phần trăm tối đa, như 100 và 200.
Tôi đã tạo một tập lệnh để triển khai các hình ảnh Docker được cập nhật cho một dịch vụ dàn dựng trên ECS, để định nghĩa tác vụ tương ứng đề cập đến các phiên bản hiện tại của hình ảnh Docker. Tôi không biết chắc liệu mình có đang làm theo các phương pháp hay nhất hay không, vì vậy chúng tôi hoan nghênh phản hồi.
Để tập lệnh hoạt động, bạn cần một phiên bản ECS dự phòng hoặc một deploymentConfiguration.minimumHealthyPercent
giá trị để ECS có thể lấy cắp một phiên bản để triển khai định nghĩa nhiệm vụ được cập nhật.
Thuật toán của tôi như thế này:
Mã của tôi được dán bên dưới:
#!/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
Cách làm sau phù hợp với tôi trong trường hợp thẻ hình ảnh docker giống nhau:
Chạy vào cùng một vấn đề. Sau khi dành hàng giờ, đã kết thúc các bước đơn giản này để triển khai tự động hình ảnh cập nhật:
1. Thay đổi định nghĩa nhiệm vụ củaECS: Để hiểu rõ hơn, hãy giả sử bạn đã tạo định nghĩa nhiệm vụ với các chi tiết dưới đây (lưu ý: những con số này sẽ thay đổi tương ứng theo định nghĩa nhiệm vụ của bạn):
launch_type = EC2
desired_count = 1
Sau đó, bạn cần thực hiện các thay đổi sau:
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. Gắn thẻ hình ảnh của bạn là < your-image-name>: mới nhất . Khóa mới nhất sẽ đảm nhận việc kéo theo tác vụ ECS tương ứng.
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. đẩy hình ảnh vào ECR
sudo docker push <your_account_id>.dkr.ecr.us-east-1.amazonaws.com/<your-image-name>:latest
4. áp dụng lực lượng triển khai
sudo aws ecs update-service --cluster <your-cluster-name> --service <your-service-name> --force-new-deployment --region us-east-1
Lưu ý: Tôi đã viết tất cả các lệnh giả sử khu vực là chúng ta-đông-1 . Chỉ cần thay thế nó bằng khu vực tương ứng của bạn trong khi triển khai.
Sử dụng AWS cli Tôi đã thử dịch vụ cập nhật aws ecs như đã đề xuất ở trên. Không nhận docker mới nhất từ ECR. Cuối cùng, tôi chạy lại playbook Ansible của mình đã tạo cụm ECS. Phiên bản của định nghĩa nhiệm vụ bị thay đổi khi chạy ecs_taskdefinition. Sau đó, tất cả là tốt. Hình ảnh docker mới được chọn.
Thực sự không chắc liệu việc thay đổi phiên bản tác vụ có buộc triển khai lại hay không hoặc liệu playbook sử dụng ecs_service có khiến tác vụ tải lại hay không.
Nếu có ai quan tâm, tôi sẽ xin phép xuất bản phiên bản sách vở đã được khử trùng của tôi.
Tôi cũng đang cố gắng tìm một cách tự động để làm việc đó, Đó là đẩy các thay đổi lên ECR và sau đó thẻ mới nhất sẽ được dịch vụ chọn. Ngay, bạn có thể làm điều đó theo cách thủ công bằng cách Dừng tác vụ cho dịch vụ của bạn từ cụm của bạn. Các tác vụ mới sẽ kéo các vùng chứa ECR được cập nhật.
Các lệnh sau đây phù hợp với tôi
docker build -t <repo> .
docker push <repo>
ecs-cli compose stop
ecs-cli compose start