In our case, we wanted to have one repository for all our services, because otherwise we would have to create and maintain ECR infrastructure for each single service.
What we did was basically creating one shared repository for all services (Cloudformation in this case):
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
registryName:
Type: String
Default: services
Resources:
ecr:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Ref registryName
ImageTagMutability: MUTABLE
… and then when building the services, we would use SERVICENAME_VERSION
as the convention for the actual image/version:
#!/bin/bash
set -e
export AWS_ACCOUNT="123456789000"
export AWS_DEFAULT_REGION="eu-central-1"
export SERVICE_NAME="demo-service"
export SERVICE_VERSION="${SERVICE_VERSION:-latest}"
export IMAGE_NAME="$AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/services:${SERVICE_NAME}_${SERVICE_VERSION}"
aws ecr get-login-password | docker login --username AWS --password-stdin "$AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com"
docker build -t $IMAGE_NAME .
docker push $IMAGE_NAME
(Simplified, but works.)
UPDATE:
In a real-world example, when you want to pull images into an ECS cluster that is placed in a VPC, you will need to set up VPC endpoints on the ECR. The Cloudformation code for this looks something like this:
privateLinkEcrApi:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.api"
PrivateDnsEnabled: true
VpcId: !ImportValue vpc
SecurityGroupIds:
- !ImportValue albSecurityGroup
SubnetIds:
- !ImportValue publicSubnetA
- !ImportValue publicSubnetB
VpcEndpointType: Interface
privateLinkEcrDkr:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.dkr"
PrivateDnsEnabled: true
VpcId: !ImportValue vpc
SecurityGroupIds:
- !ImportValue albSecurityGroup
SubnetIds:
- !ImportValue publicSubnetA
- !ImportValue publicSubnetB
VpcEndpointType: Interface
privateLinkEcrLogs:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.logs"
PrivateDnsEnabled: true
VpcId: !ImportValue vpc
SecurityGroupIds:
- !ImportValue albSecurityGroup
SubnetIds:
- !ImportValue publicSubnetA
- !ImportValue publicSubnetB
VpcEndpointType: Interface
privateLinkEcrS3:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
VpcId: !ImportValue vpc
SecurityGroupIds:
- !ImportValue albSecurityGroup
SubnetIds:
- !ImportValue publicSubnetA
- !ImportValue publicSubnetB
VpcEndpointType: Interface
privateLinkEcrS3Gw:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
VpcId: !ImportValue vpc
RouteTableIds:
- !ImportValue publicRouteTable
- !ImportValue privateRouteTableA
- !ImportValue privateRouteTableB
VpcEndpointType: Gateway
(NB: You will have to adapt this code as the actual VPC, subnets etc. are set up in a different template, and the actual configuration depends very much on your own environment. But this should get you on the right track.)