This is a tutorial for using AWS CLI to programmatically create an AWS ECR repository and then commit a Docker image to that repository. This is the first step in a series of tutorials for creating an automated testing framework in AWS Fargate which can be one step of a DevOps pipeline.
As an example I am going to use the Docker php:7.2-apache image and to make it a little more complex, add a few more PHP extensions using a new Dockerfile so that my new image will be Drupal 8 compliant.
Let's get started.
Pull Base Image and Define Dockerfile
The base php:7.2-apache image needs to be pulled from Docker Hub. Issue the following command.
Yes the docker image is there. Now I need to create a directory for the Dockerfile and a sample index.php file which will prove our image is working.
By default the Docker php image uses /var/www/html as Apache's docroot. However, I'm going to alter this to something more standard for Drupal web apps - a directory called docroot will be created and will contain a simple index.php containing a call to phpinfo();
The Dockerfile should now be created and populated. There a number of activities that need to be performed on it.
$ docker pull php:7.2-apache 7.2-apache: Pulling from library/php 000eee12ec04: Pull complete 8ae4f9fcfeea: Pull complete 60f22fbbd07a: Pull complete ccc7a63ad75f: Pull complete a2427b8dd6e7: Pull complete 91cac3b30184: Pull complete d6e40015fc10: Pull complete 240e21c03bb4: Pull complete 504858e1e4aa: Pull complete 9a0523b2d73f: Pull complete e3acb84829f4: Pull complete 28a372733f87: Pull complete 62ee66cdb80a: Pull complete c3f368ddc7aa: Pull complete Digest: sha256:602bef9acc8d54527ae5b7ee0f16e5cdb6ab81364310f5123b93b95e357f608d Status: Downloaded newer image for php:7.2-apache docker.io/library/php:7.2-apache $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE php 7.2-apache fec8a1aaffb8 31 hours ago 410MB lambci/lambda go1.x 8d962cde7e27 3 months ago 707MB sls-docker latest 8d962cde7e27 3 months ago 707MB
$ mkdir automated && cd automated
$ mkdir docroot && echo "<?php phpinfo();" > docroot/index.php
- Reference the base image with a FROM instruction
- Install the libraries that support memcached and graphics manipulation
- Enable memcached PHP extension
- Configure the graphics PHP extension
- Install the graphics PHP extension
- Copy the files in the current directory and below into the Docker image at the /var/www/html ensuring the permissions are correct for the apache user and group
- Set the environment variable AH_SITE_ENVIRONMENT to devops - this is an Acquia Cloud environment variable and only of any significance for those using Acquia as their host - or need a setting in my case to denote that devops is not running on Acquia
- Change the default location for the docroot - that means setting an environment variable and using the sed editor to change the configuration in the sites files
FROM php:7.2-apache RUN apt-get update && apt-get install -y libmemcached-dev zlib1g-dev \ libfreetype6-dev \ libjpeg62-turbo-dev \ libpng-dev \ libwebp-dev \ && pecl install memcached \ && docker-php-ext-enable memcached \ && docker-php-ext-configure gd --with-gd --with-webp-dir --with-jpeg-dir \ --with-png-dir --with-zlib-dir --with-freetype-dir \ && docker-php-ext-install gd COPY --chown=www-data:www-data ./ /var/www/html/. ENV AH_SITE_ENVIRONMENT devops ENV APACHE_DOCUMENT_ROOT /var/www/html/docroot RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
Build the Image, Run the Container

Once the Dockerfile is complete, it is built with
and to confirm it's built correctly:
Great it's there. Now to spin up the container.
Note I am mapping port 8080 to the container's port 80 which probably won't be pertinent to you. Long story I will try to make short. I write my blogs on my Macbook on a local network in the 192.168 range assigned by my ISP's hub. But I develop and test all the practical work in my blogs in a VM in the IP 10. range. Routing between 192.168 and 10. is difficult without port forwarding - so I port forward 127.0.0.1 8080 to my VM's 10.0.2.15 8080, so when I run my Docker Apache container I must remember incoming is on port 8080 and map that against the container's port 80. Hope that makes sense!
Now check it's running:
Yes, and we can see from the screenshot above that it's picking up the memcached and the gd graphics extensions. Success.
$ docker build -t d8codebase .
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE d8codebase latest c2193f9778a6 33 minutes ago 439MB <none> <none> 2a71964685e0 2 hours ago 432MB php 7.2-apache fec8a1aaffb8 35 hours ago 410MB lambci/lambda go1.x 8d962cde7e27 3 months ago 707MB sls-docker latest 8d962cde7e27 3 months ago 707MB
$ docker run -d -p 8080:80 d8codebase
Now check it's running:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9a6dc2fb88ff d8codebase "docker-php-entrypoi…" 44 minutes ago Up 44 minutes 0.0.0.0:8080->80/tcp agitated_feistel
Programmatically Create the AWS ECR Repository

This is quite easy and I've put it into a shell script that requires one parameter - the repo name to be created. The work is done by the aws ecr create-repository call. The rest of the code is simply checking for error conditions. The screenshot above was taken on the AWS console after the shell script below was successfully run with a parameter of d8codebase.
#!/bin/bash #Programmatically create an ECR repo # Check whether we have the args if [[ -z $@ ]]; then echo "usage: [repo_name]" exit 1 fi # Create the repo REPO_NAME=`aws ecr create-repository --repository-name "$1" | jq .repository.repositoryName -r` # Check it was created ok. We already get a quality stderr msg from aws cli so just quit on error. # Similarly, if already exists it will tell us if [[ "${REPO_NAME}" != $1 ]]; then exit 2 fi echo "Successfully created ${REPO_NAME}"
Login to Docker and Push Image to ECR

Now the script to automatically login to Docker and push the image to ECR. Note that AWS ECR does require you to use the docker login command, and therefore provides a AWS CLI ECR command get-login which returns a token than can be piped into the docker login command. The token retrieved from ECR has a large payload, and it is top and tailed with other metadata beyond just the password. Therefore that needs to be removed, and if you look closely at the script below, you can see that I pipe the entire returned payload through sed with a regex, and then the result is piped onwards to docker login. Hopefully the rest is quite self explanatory - and you can see the screenshot above that the push has worked and the image can now be included in ECS / Fargate task definitions.
#!/bin/bash #Programmatically push to an ECR repo # Check whether we have the args if [[ -z $@ ]]; then echo "usage: [repo_name]" exit 1 fi REPO_NAME=$1 ECR_ACCOUNT="XXXXXXXXXX.dkr.ecr.eu-west-2.amazonaws.com" ECR_REPO="${ECR_ACCOUNT}/$1" # AWS Authentication - since we need to use docker push # The sed regex will top and tail the additional meta data sent by the aws login request. aws ecr get-login --region eu-west-2 | sed -e 's/^.*-p \(.*\)\s\-\e.*$/\1/' | docker login --password-stdin -u AWS ${ECR_ACCOUNT} # Get the image id of the Docker build. If there is a "latest" use that else get the first without latest TAG=`docker images | grep -w "${REPO_NAME}" | grep latest | awk '{ print $3; }'` if [[ -z ${TAG} ]]; then TAG=`docker images | grep -w "${REPO_NAME}" | awk '{ print $3; }'` fi docker tag "$TAG" "${ECR_REPO}" docker push "${ECR_REPO}"