How to Publish Docker Images to ECR

To publish Docker images to ECR, you need to perform the following tasks:
- Ensure you are logged into ECR
- Build and tag your Docker image with the URI of your ECR repository
- Push your Docker image to ECR
Publishing Docker images using the Docker CLI
When building and tagging a Docker image, you need to specify the URI of your ECR repository while tagging the image. The following example demonstrates building the todobackend
image, tagging the image with the URI of your ECR repository (for the actual URI of your repository), and verifying the image name using the docker images
command:
> cd ../todobackend
> docker build -t 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend .
Sending build context to Docker daemon 129.5kB
Step 1/25 : FROM alpine AS build
---> 3fd9065eaf02
Step 2/25 : LABEL application=todobackend
---> Using cache
---> f955808a07fd
...
...
...
Step 25/25 : USER app
---> Running in 4cf3fcab97c9
Removing intermediate container 4cf3fcab97c9
---> 2b2d8d17367c
Successfully built 2b2d8d17367c
Successfully tagged 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:latest
> docker images
REPOSITORY TAG IMAGE ID SIZE
385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend latest 2b2d8d17367c 99.4MB
Once you have built and tagged your image, you can push your image to ECR. If you have already logged into ECR, this is as simple as using the docker push
command and referencing the name of your Docker image:
> docker push 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend
The push refers to repository [385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend]
1cdf73b07ed7: Pushed
0dfffc4aa16e: Pushed
baaced0ec8f8: Pushed
e3b27097ac3f: Pushed
3a29354c4bcc: Pushed
a031167f960b: Pushed
cd7100a72410: Pushed
latest: digest: sha256:322c8b378dd90b3a1a6dc8553baf03b4eb13ebafcc926d9d87c010f08e0339fa size: 1787
If you now navigate to the todobackend
repository in the ECS console, you should see your newly published image appear with the default latest
tag, as shown in the following figure. Notice that when you compare the built size of the image (99 MB in this example) with the size of the image stored in ECR (34 MB in this example), you can see that ECR stores the image in a compressed format, which reduces storage costs.
Publishing Docker images using Docker Compose
Docker Compose includes a service configuration property called image
, which is commonly used to specify the image of a container that you would like to run:
version: '2.4'
services:
web:
image: nginx
Although this is a very common usage pattern for Docker Compose, another configuration and set of behaviors exist if you combine both the build
and image
properties, as demonstrated here, for the docker-compose.yml
file in the todobackend
repository:
version: '2.4'
volumes:
public:
driver: local
services:
test:
build:
context: .
dockerfile: Dockerfile
target: test
release:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:latest
build:
context: .
dockerfile: Dockerfile
environment:
DJANGO_SETTINGS_MODULE: todobackend.settings_release
MYSQL_HOST: db
MYSQL_USER: todo
MYSQL_PASSWORD: password
app:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:${APP_VERSION}
extends:
...
...
In the preceding example, the image
and build
properties are both specified for the release
and app
services. When these two properties are used together, Docker will still build the image from the referenced Dockerfile, but will tag the image with the value to specify for the image
property.
You can apply multiple tags by creating new services that simply extend your release image and define a image property that includes the additional tag.
Take note that for the app
service, you need to reference the environment variable APP_VERSION
. It is intended to tag the image with the current application version that is defined within the Makefile at the root of the todobackend
repository:
.PHONY: test release clean version
export APP_VERSION ?= $(shell git rev-parse --short HEAD)
version:
@ echo '{"Version": "$(APP_VERSION)"}'
To demonstrate the tagging behavior when you combine the image
and build
properties, first delete the Docker image you created earlier:
> docker rmi 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend
Untagged: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:latest
Untagged: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend@sha256:322c8b378dd90b3a1a6dc8553baf03b4eb13ebafcc926d9d87c010f08e0339fa
Deleted: sha256:2b2d8d17367c32993b0aa68f407e89bf4a3496a1da9aeb7c00a8e49f89bf5134
Deleted: sha256:523126379df325e1bcdccdf633aa10bc45e43bdb5ce4412aec282e98dbe076fb
Deleted: sha256:54521ab8917e466fbf9e12a5e15ac5e8715da5332f3655e8cc51f5ad3987a034
Deleted: sha256:03d95618180182e7ae08c16b4687a7d191f3f56d909b868db9e889f0653add46
Deleted: sha256:eb56d3747a17d5b7d738c879412e39ac2739403bbf992267385f86fce2f5ed0d
Deleted: sha256:9908bfa1f773905e0540d70e65d6a0991fa1f89a5729fa83e92c2a8b45f7bd29
Deleted: sha256:d9268f192cb01d0e05a1f78ad6c41bc702b11559d547c0865b4293908d99a311
Deleted: sha256:c6e4f60120cdf713253b24bba97a0c2a80d41a0126eb18f4ea5269034dbdc7e1
Deleted: sha256:0b780adf8501c8a0dbf33f49425385506885f9e8d4295f9bc63c3f895faed6d1
If you now run the docker-compose build release
command, once the command completes, Docker Compose will have built a new image tagged with your ECR repository URI:
> docker-compose build release
WARNING: The APP_VERSION variable is not set. Defaulting to a blank string.
Building release
Step 1/25 : FROM alpine AS build
---> 3fd9065eaf02
Step 2/25 : LABEL application=todobackend
---> Using cache
---> f955808a07fd
...
...
Step 25/25 : USER app
---> Using cache
---> f507b981227f
Successfully built f507b981227f
Successfully tagged 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:latest
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend latest f507b981227f 4 days ago 99.4MB
With your image built and tagged correctly, you can now execute the docker-compose push
command, which can be used to push services defined in the Docker Compose file that include a build
and image
property:
> docker-compose push release
Pushing release (385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:latest)...
The push refers to repository [385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend]
9ae8d6169643: Layer already exists
cdbc5d8be7d1: Pushed
08a1fb32c580: Layer already exists
2e3946df4029: Pushed
3a29354c4bcc: Layer already exists
a031167f960b: Layer already exists
cd7100a72410: Layer already exists
latest: digest: sha256:a1b029d347a2fabd3f58d177dcbbcd88066dc54ccdc15adad46c12ceac450378 size: 1787
In the preceding example, the image associated with the service called release is pushed, given this is the service that you configured with the Docker image URI.
Automating the publish workflow
To automate logging in and out of ECR and publishing your image to ECR, you’ll need to create new tasks in the Makefile of the todobackend repository.
Automating login and logout
The following example demonstrates how to add a couple of new tasks called login
and logout
, which will perform these actions using the Docker client:
.PHONY: test release clean version login logout
export APP_VERSION ?= $(shell git rev-parse --short HEAD)
version:
@ echo '{"Version": "$(APP_VERSION)"}'
login:
$$(aws ecr get-login --no-include-email)
logout:
docker logout https://385605022855.dkr.ecr.us-east-1.amazonaws.com
test:
docker-compose build --pull release
docker-compose build
docker-compose run test
release:
docker-compose up --abort-on-container-exit migrate
docker-compose run app python3 manage.py collectstatic --no-input
docker-compose up --abort-on-container-exit acceptance
@ echo App running at http://$$(docker-compose port app 8000 | sed s/0.0.0.0/localhost/g)
clean:
docker-compose down -v
docker images -q -f dangling=true -f label=application=todobackend | xargs -I ARGS docker rmi -f ARGS
Take note that the login
task uses a double dollar sign ($$), which is required as Make uses single dollar signs to define Make variables. When you specify a double dollar sign, Make will pass a single dollar sign to the shell, which in this case will ensure a bash command substitution is executed.
When logging out with the logout
task, you need to specify the Docker registry; otherwise the Docker client assumes the default public Docker Hub registry.
With these tasks in place, you can now easily log in and out of ECR using the make login
and make logout
commands:
> make logout
docker logout https://385605022855.dkr.ecr.us-east-1.amazonaws.com
Removing login credentials for 385605022855.dkr.ecr.us-east-1.amazonaws.com
> make login
$(aws ecr get-login --no-include-email)
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded
Automating the publishing of Docker images
To automate the publishing workflow, you can add a new task called publish
to the Makefile, which simply calls the docker-compose push
command for the tagged release
and app
services:
.PHONY: test release clean login logout publish
export APP_VERSION ?= $(shell git rev-parse --short HEAD)
version:
@ echo '{"Version": "$(APP_VERSION)"}'
...
...
release:
docker-compose up --abort-on-container-exit migrate
docker-compose run app python3 manage.py collectstatic --no-input
docker-compose up --abort-on-container-exit acceptance
@ echo App running at http://$$(docker-compose port app 8000 | sed s/0.0.0.0/localhost/g)
publish:
docker-compose push release app
clean:
docker-compose down -v
docker images -q -f dangling=true -f label=application=todobackend | xargs -I ARGS docker rmi -f ARGS
With this configuration in place, your Docker image will now be tagged with both the commit hash and latest tags, which you can then publish to ECR by simply running the make publish
command.
It’s time now to commit your changes and run the full Make workflow to test, build, and publish your Docker images, as demonstrated in the following example. See that an image tagged with the commit hash of 97e4abf
is published to ECR:
> git commit -a -m "Add publish tasks"
[master 97e4abf] Add publish tasks
2 files changed, 12 insertions(+), 1 deletion(-)
> make login
$(aws ecr get-login --no-include-email)
Login Succeeded
> make test && make release
docker-compose build --pull release
Building release
...
...
todobackend_db_1 is up-to-date
Creating todobackend_app_1 ... done
App running at http://localhost:32774
$ make publish
docker-compose push release app
Pushing release (385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:latest)...
The push refers to repository [385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend]
53ca7006d9e4: Layer already exists
ca208f4ebc53: Layer already exists
1702a4329d94: Layer already exists
e2aca0d7f367: Layer already exists
c3e0af9081a5: Layer already exists
20ae2e176794: Layer already exists
cd7100a72410: Layer already exists
latest: digest: sha256:d64e1771440208bde0cabe454f213d682a6ad31e38f14f9ad792fabc51008888 size: 1787
Pushing app (385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:97e4abf)...
The push refers to repository [385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend]
53ca7006d9e4: Layer already exists
ca208f4ebc53: Layer already exists
1702a4329d94: Layer already exists
e2aca0d7f367: Layer already exists
c3e0af9081a5: Layer already exists
20ae2e176794: Layer already exists
cd7100a72410: Layer already exists
97e4abf: digest: sha256:d64e1771440208bde0cabe454f213d682a6ad31e38f14f9ad792fabc51008888 size: 1787
> make clean
docker-compose down -v
Stopping todobackend_app_1 ... done
Stopping todobackend_db_1 ... done
...
...
> make logout
docker logout https://385605022855.dkr.ecr.us-east-1.amazonaws.com
Removing login credentials for 385605022855.dkr.ecr.us-east-1.amazonaws.com
If you found this article helpful and want to learn more about Docker and AWS, you can explore Docker on Amazon Web Services by Justin Menga. The book follows a clear, concise and straightforward approach to enable you to effectively use containers with AWS. If you want to build, deploy, and operate applications using the power of containers, Docker on Amazon Web Services is what you must read!