How to create and use Docker base image with AWS ECR and AWS Code Build
by Bruno Stefano and Guilherme Prearo
This is a quick guide that helps pushing and using Docker base image with AWS ECR and Code Build. This is a good practice that helps saving build time (therefore money) and creates a stable environment for applications that use the same requirements.
1. Create ECR
The first step is to create an ECR to store the base image on AWS. This is a very straight forward process that can be achieved following this tutorial https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-create.html .
2. Build and Upload base image
After creating the ECR, you need to build your base image. Suppose you need Ubuntu 20.04 LTS with python and some pip requirements, the first thing is to create a Dockerfile that will generate this base image. If you already have a Dockerfile for your application, exclude all commands that references your application code, environment variables and ports exposure. Leave just the operational system and the required software dependencies for your application. An example is shown below:
FROM public.ecr.aws/lts/ubuntu:18.04ENV USER_NAME=ubuntu \
USER_HOME=/home/ubuntuWORKDIR ${USER_HOME}ADD ./requirements.txt ${USER_HOME}/
RUN set -x \
# Install OS libs
&& apt-get update \
&& apt-get install -y virtualenv python3 curl \
&& apt-get install -y wget\
# Add user
&& useradd -M -u 1000 -s /bin/sh -d ${USER_HOME} ${USER_NAME} \
&& mkdir -p ${USER_HOME} \
&& chown -R 1000:1000 ${USER_HOME}
USER ${USER_NAME}
RUN set -x \
# Create virtualenv
&& virtualenv -p python3 venv \
# Install Python libs
&& . venv/bin/activate \
&& pip install --default-timeout=100 future \
&& pip install --upgrade -r requirements.txt \
&& python -m ipykernel install --user --name=venv
Save your Dockerfile and then, build your base image with Docker build:
docker build -t [IMAGE_NAME] .
Log into AWS and then, push the built image as shown:
aws ecr get-login-password --region [AWS_REGION] | docker login --username AWS --password-stdin [AWS_ACCOUNT_ID].dkr.ecr.[AWS_REGION].amazonaws.comdocker push [AWS_ACCOUNT_ID].dkr.ecr.[AWS_REGION].amazonaws.com/[ECR_REPO_NAME]:[IMAGE_NAME]
3. Write your application’s Dockerfile
The next step is to use the base image pushed to ECR in your application’s Dockerfile. To do this, you can just copy your image URI in the ECR repository page and paste it in the beginning of your Dockerfile as shown below (Figure 1):
FROM [IMAGE_URI]ENV USER_NAME=ubuntu \
USER_HOME=/home/ubuntu \
# Set clock and location
LC_ALL=C.UTF-8 \
LANG=C.UTF-8 \WORKDIR ${USER_HOME}ADD . ${USER_HOME}
USER root
RUN set -x \
# Set permissions
&& chmod +x entrypoint.sh
USER ${USER_NAME}
EXPOSE ${PORT}
CMD ["./entrypoint.sh"]
4. Set up your AWS Code Build
Creating a build project on AWS Code Build is pretty straight forward, first you need to name you build project, then choose your code source as shown on Figures 2 and 3.
The next step consists in choosing the environment in which your code will be built. You can choose between an AWS managed image and a custom image if your application demands that. Here we will use an AWS managed image, since our project doesn’t have any particularities (Figure 4). You can also setup the advanced features to define some environment variables that can be used during the build process, accessed by buildspec commands (Figure 5).
Next, choose to use a buildspec file with default name (Figure 6), we will discuss how to write yours later.
With your docker file done you must write the buildspec file to tell the AWS Code Build how to build your docker image. The build process is divided into phases, pre-build, build and post-build.
- pre-build: In this phase we log into AWS account to be able to pull our custom base image;
- build: We defined the image name and build the docker image with our docker file;
- post-build: Our docker image is pushed to our ECR repository.
Here is a example on how to write your buildspec.ym:
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- export IMAGE_NAME=[IMAGE_NAME]-$ENVIRONMENT-$(date +%Y-%m-%d).$CODEBUILD_BUILD_NUMBER
- docker build --tag $IMAGE_REPO_NAME:$IMAGE_NAME .
- docker tag $IMAGE_REPO_NAME:$IMAGE_NAME $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_NAME
post_build:
commands:
- echo Build completed on `date`
- export IMAGE_PATH=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_NAME
- echo "Image path $IMAGE_PATH"
- echo Pushing the Docker image...
- docker push $IMAGE_PATH
5. AWS Roles permissions
The most difficult part of setting up the build is to assure that your roles will have the right permissions. When creating your AWS Code Build project a role will be automatically created, but you need to add some permissions manually. Go to your IAM, find the Code Build role and add an inline policy, as shown in Figure 7. Choose Json copy one of the policies shown below and paste, do it with the other policy (Figure 8).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRPullPolicy",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": [
"*"
]
},
{
"Sid": "ECRAuthPolicy",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": [
"*"
]
}
]
}{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"codecommit:GitPull"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
6. Conclusion
Setting up AWS Code Build to create the docker image for your project is really simple, you will find that creating a base image can improve your build time and reduce your build billing. The buildspec file can make your build very flexible, you can change your image name based on environment variables, date and time. With all this power it’s easy to take your CI/CD to the next level.