DEV Community

Dip Bhakta
Dip Bhakta

Posted on • Originally published at Medium on

Replicating existing AWS ECR images to another region is easy too, Muhi!!

Here is an imaginary conversation between Muhi and his manager.

Manager: Muhi, most of our clients are in South Korea. They pull the aws ecr images from Mumbai region and face trouble due to latence.

Muhi: Okayy! So, what can we do for them?

Manager: We have to replicate all the ecr images from Mumbai to Seol region.

Muhi: That is an easy task. I just have to enable cross region replication in ecr. Right sir?

Manager: Yeah, right. But how about the existing images? They are still using them.

Muhi: Oh ho! I have not thought about this.

Manager: Don’t worry Muhi. That is an easy task too!

So, first of all, how is Muhi planning to replicate the future images to be pushed into the Mumbai region? Since, due to some reasons, pushing to Mumbai region is necessary. For that, he has to enable cross region replication (CRR) in AWS ECR. Once he browses the AWS ECR console, he can go to the replication settings on the left bar (Private registry -> Settings -> Replication). In the Replication Configuration window, click on ‘Add Rule’.

Then, you choose the destination for replication. It can be either cross-region replication or cross-account replication. Remember that, for cross-account replication, the destination account must allow the source account the neccessary permissions to the ecr. Muhi is using cross-region replication here in the same account. So, he selects that and proceeds. Then, he chooses the destination region and completes enabling cross-region replication.

Now, every image pushed in the source region registry will be replicated to the destination region registry.

But what about the existing images in the source repository? For, that Muhi will be creating a lambda function. Muhi usually uses Python 3.9 runtime in lambdas. So, he creates a lambda function with a role which has the following permissions:

ecr::ListImage, ecr::DescribeRepositories, ecr::BatchGetImage, ecr::PutImage, ecr::BatchDeleteImage

Then Muhi starts coding.

import boto3

def get_ecr_client(region_name):
    client = boto3.client('ecr', region_name=region_name)
    return client

def list_repositories(ecr, registry_id):
    return ecr.describe_repositories(registryId=registry_id, maxResults=1000)

def list_images(ecr, registry_id, repository_name):
    return ecr.list_images(
        registryId=registry_id,
        repositoryName=repository_name,
        maxResults=1000,
        filter={'tagStatus': 'TAGGED'}
    )

def get_image_data(ecr, registry_id, repository_name, image_tag):
    return ecr.batch_get_image(
        registryId=registry_id,
        repositoryName=repository_name,
        imageIds=[{'imageTag': image_tag}]
    )

def reupload_image(ecr, registry_id, repository_name, image_manifest, original_tag):
    temp_tag = original_tag + "-temp"

    # Put the image with the temporary tag back into the repository
    ecr.put_image(registryId=registry_id, repositoryName=repository_name, imageManifest=image_manifest, imageTag=temp_tag)

    # Delete the original image with its original tag
    ecr.batch_delete_image(registryId=registry_id, repositoryName=repository_name, imageIds=[{'imageTag': original_tag}])

    # Put the image back into the repository with the original tag
    ecr.put_image(registryId=registry_id, repositoryName=repository_name, imageManifest=image_manifest, imageTag=original_tag)

    # Delete the image with the temporary tag
    ecr.batch_delete_image(registryId=registry_id, repositoryName=repository_name, imageIds=[{'imageTag': temp_tag}])

def delete_temp_images(ecr, registry_id):
    repositories_list = list_repositories(ecr, registry_id)

    for repository in repositories_list['repositories']:
        repository_name = repository['repositoryName']
        images_list = list_images(ecr, registry_id, repository_name)

        for image in images_list['imageIds']:
            image_tag = image['imageTag']
            if image_tag.endswith('-temp'):
                print("Deleting: {0}:{1}".format(repository_name, image_tag), end=" ... ")
                ecr.batch_delete_image(registryId=registry_id, repositoryName=repository_name, imageIds=[{'imageTag': image_tag}])
                print("OK")

def lambda_handler(event, context):
    source_region_name = 'YOUR_SOURCE_REGION_HERE'
    destination_region_name = 'YOUR_DESTINATION_REGION_HERE'
    registry_id = 'YOUR_AWS_ACCOUNT_ID_HERE'

    source_ecr = get_ecr_client(source_region_name)
    source_repositories_list = list_repositories(ecr, registry_id)

    for repository in source_repositories_list['repositories']:
        repository_name = repository['repositoryName']
        images_list = list_images(source_ecr, registry_id, repository_name)

        for image in images_list['imageIds']:
            image_tag = image['imageTag']
            image_data = get_image_data(source_ecr, registry_id, repository_name, image_tag)

            print("Reuploading:", repository_name, ":", image_tag, end=" ")
            for image_info in image_data['images']:
                reupload_image(source_ecr, registry_id, repository_name, image_info['imageManifest'], image_tag)
                print("OK")

    destination_ecr = get_ecr_client(destination_region_name)
    # Additional code to delete images with '-temp' tag
    delete_temp_images(destination_ecr, registry_id)
Enter fullscreen mode Exit fullscreen mode

Muhi has showed us a pretty interesting trick. Since Cross-region replication is enabled, every newly pushed images will be replicated to the destination region. So, after listing all the repositories and images, Muhi pushes all the image manifests with appending “-temp” to the image tag. So, newly pushed tag+”-temp” images are replicated in the destination region. Now, Muhi deletes the original tags and put the the “original tag” images back. So, now the “orginal tag” images are also replicated in the destination region. Then he deletes the tag+”-temp” image tags.

The only task remaining is to delete all the image tags which ends with “-temp” in the destination region. Quite easy!! right?

Top comments (0)