Integrating Your Codeship CI/CD Pipeline with AWS ECR

Integrations

Reading Time: 7 minutes

Amazon recently announced its hosted EC2 Container Registry. Now that support for Amazon ECR has been released, we’re here to show you how you can integrate ECR into your CI/CD pipeline at Codeship.

ECR behaves similarly to a standard Docker registry where you can push and pull images and reference the registry when building your images. That being said, there are some differences between integrating Docker Hub or Quay and integrating with ECR. Let’s explore integrating with ECR, with a focus on Codeship tooling.

How It Works

Build on the reliable AWS infrastructure, Amazon provides a highly available container registry that integrates directly with the core AWS toolset. This sits behind standard AWS authentication and routing and allows you to easily co-locate your container images with your container cluster.

Quay and Docker Hub, which provide a single endpoint for interaction, use credentials to determine access and visibility. ECR provides a unique URL for each registry, which is tied to a specific user account. This means that different repositories may belong to different registries and have different URLs depending on what user account they were created under. The AWS CLI lets you specify this registry ID when creating repositories.

ECR is compatible with the Docker registry V2 API, which means you can run standard docker pull and docker push commands against your registry. However, this is protected by a layer of security.

In order to do a docker login, you must use the AWS CLI to get a temporary login token, which you can configure to expire up to 36 hours. This means that standard practices of keeping a dockercfg with persistent registry credentials locally, and as part of your encrypted CI databag, will not work with for ECR. We’ll talk in more detail about how we solve this for the Codeship CI/CD pipeline later.

Getting Started

In order to begin using ECR and then integrate it with our CI/CD pipeline, we’ll need to first set up the AWS CLI, create a repository, and determine our registry URL.

To set up the AWS cli, follow the instructions in the AWS CLI docs and then run aws configure to provide the tool with your AWS access credentials.

From here on, all ECR-related commands are available via the aws ecr section and can be listed by running aws ecr help:

$ aws ecr help

NAME
       ecr -

DESCRIPTION
       The  Amazon EC2 Container Registry makes it easier to manage public and
       private Docker images for AWS or on-premises environments. Amazon ECR
       supports resource-level permissions to control who can create, update,
       use or delete images. Users and groups can be created individually in
       AWS Identity and Access Management (IAM) or federated with enterprise
       directories such as Microsoft Active Directory. Images are stored on
       highly durable AWS infrastructure and include built-in caching so that
       starting hundreds of containers is as fast as a single container.

AVAILABLE COMMANDS
       o batch-check-layer-availability
       o batch-delete-image
       o batch-get-image
       o complete-layer-upload
       o create-repository
       o delete-repository
       o delete-repository-policy
       o describe-repositories
       o get-authorization-token
       o get-download-url-for-layer
       o get-login
       o get-repository-policy
       o help
       o initiate-layer-upload
       o list-images
       o put-image
       o set-repository-policy
       o upload-layer-part

Let’s start by creating a repository within our registry for a sample application, myuser/myapp. This will allow us to push and pull images using the Docker cli and API.

$ aws ecr create-repository --repository-name myuser/myapp
{
    "repository": {
        "registryId": "108487657846", 
        "repositoryName": "myuser/myapp", 
        "repositoryArn": "arn:aws:ecr:us-east-1:108487657846:repository/myuser/myapp"
    }
}

We can now use the registryId field to determine the registry URL via the template https://${REGISTRY_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com. This will be needed later when referencing images. You can also get the registry URL from a generated ECR dockercfg.

Sign up for a free Codeship Account

Generating Credentials

Since ECR adheres to standard AWS authentication, you must use a secondary, temporary token rather than an AWS keypair in order to push or pull images. This can be accomplished by either generating a Docker login via the AWS cli or simply generating a Docker auth token which can be used to log in.

$ aws ecr get-login
docker login -u AWS -p CiBwm0YaIyYjCVV+BAIBEICCAoo55cBYr9IBaPchhO8Ba0iPy8xRFuvIOSaw2yHVV/lE1/v5e9FZGck03lrA7q/rAFHRjvCrOdSS+/cvV2kpFv1drVEiMR9EEDRKdgLEw4ung3YrKDHqVZjXhxWaRiC2mFIKaDFNjyNYxY6Kmg5JCJTCwHRjOoWADJ0SJRDJdcqN8oKkyUvCEgW8idIWsFw5pjCLtQNtI2VX3XrnE8s5GLddQIsJOG3d1ak3a8LFzXUVb+V3eOysAuLtrCcZlPGyODZHI1nfcgcqjh16zeNitqRI2+H8G+kGAL2Xlbzwp8gVNkH+AX/vkbi0/1QFy/8KgyC7jvnn3+gedXqjNSW4sDS0yjCyp6pL+S4MVTkyq8fkrB/tdgRtJm5n1G6uqeekXuoXXPe5UFce9Rq8/14xKABgEBAgB4cJtGGiEiXkbSZuZ9RurqnnpF7qF1z3uVBXHvUavP9eMAAALXMIIC0wYsXHix4VRzWyzUbB8PANn/Qojf1oMQkQ2u15CZJ8Tol0LHgDi5/qGZ+wHTn+sz/dilpwlmrTuo+6avfZdfQy9r47+EPohNB0OquH03gt3fSjR5efU0ldE62VL/GrgHpgOH9qfSsCDvnKDuwfD5lFEIc3npcLh3djbcchTzCSqHdjAjgQgMQh54JSojL3TydS8WclKg6/W7wQIaozk+zfOoETPq90nO1UtT9QbBxbBBqL1JOs9Wu1owX1Ec9wS5oIuwXYNpHqDTA0EQTV4jZsZ335JMAijcM5GHN7MJ2ukOXOffonmHKoVdNJ7RpLBdz8moVKewhOF4jSh8GMbWu19W3uJsGHyS1oMfZz17whuWdetF77cf5cSUF7HXJEW77zDGRUVo0PWqg2CNEdCaonasScWKLQcT1AjhrQ+ctiXZcjoZGRRxFIZ8qR6t3lwn+sZIGszRkdhEI3lKW7EfZl4PfJVieip8m4sccbcetUzjLeSJeJKoZIhvcNAQcGoIICxDCCAsACAQAwggK5BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGyzVF8QSt9pZg9PIg== -e none https://108487657846.dkr.ecr.us-east-1.amazonaws.com

The generated command contains the URL for the ECR registry. This command can be directly executed via a subshell:

$ $(aws ecr get-login)
WARNING: login credentials saved in /root/.docker/config.json
Login Succeeded

Alternatively, you can manually generate a token using the AWS cli:

$ aws ecr get-authorization-token --query authorizationData[].authorizationToken --output text | base64 -d | cut -d: -f2
CiBwm0YaIyYjCVV+BAIBEICCAoo55cBYr9IBaPchhO8Ba0iPy8xRFuvIOSaw2yHVV/lE1/v5e9FZGck03lrA7q/rAFHRjvCrOdSS+/cvV2kpFv1drVEiMR9EEDRKdgLEw4ung3YrKDHqVZjXhxWaRiC2mFIKaDFNjyNYxY6Kmg5JCJTCwHRjOoWADJ0SJRDJdcqN8oKkyUvCEgW8idIWsFw5pjCLtQNtI2VX3XrnE8s5GLddQIsJOG3d1ak3a8LFzXUVb+V3eOysAuLtrCcZlPGyODZHI1nfcgcqjh16zeNitqRI2+H8G+kGAL2Xlbzwp8gVNkH+AX/vkbi0/1QFy/8KgyC7jvnn3+gedXqjNSW4sDS0yjCyp6pL+S4MVTkyq8fkrB/tdgRtJm5n1G6uqeekXuoXXPe5UFce9Rq8/14xKABgEBAgB4cJtGGiEiXkbSZuZ9RurqnnpF7qF1z3uVBXHvUavP9eMAAALXMIIC0wYsXHix4VRzWyzUbB8PANn/Qojf1oMQkQ2u15CZJ8Tol0LHgDi5/qGZ+wHTn+sz/dilpwlmrTuo+6avfZdfQy9r47+EPohNB0OquH03gt3fSjR5efU0ldE62VL/GrgHpgOH9qfSsCDvnKDuwfD5lFEIc3npcLh3djbcchTzCSqHdjAjgQgMQh54JSojL3TydS8WclKg6/W7wQIaozk+zfOoETPq90nO1UtT9QbBxbBBqL1JOs9Wu1owX1Ec9wS5oIuwXYNpHqDTA0EQTV4jZsZ335JMAijcM5GHN7MJ2ukOXOffonmHKoVdNJ7RpLBdz8moVKewhOF4jSh8GMbWu19W3uJsGHyS1oMfZz17whuWdetF77cf5cSUF7HXJEW77zDGRUVo0PWqg2CNEdCaonasScWKLQcT1AjhrQ+ctiXZcjoZGRRxFIZ8qR6t3lwn+sZIGszRkdhEI3lKW7EfZl4PfJVieip8m4sccbcetUzjLeSJeJKoZIhvcNAQcGoIICxDCCAsACAQAwggK5BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGyzVF8QSt9pZg9PIg==

You can then use the returned authorization token in the same docker login command aws ecr get-login generates.

Authentication with ECR in Codeship

Traditionally, static Docker credentials are encoded in the project databag and decrypted in order to push or pull images from a registry.

Due to the short-lived nature of ECR credentials, Codeship has added support for dockercfg generation services. You can now specify a service with which to generate a dockercfg for authenticating a specific service or step. You can find out more about this in the Codeship documentation. For the purposes of ECR support, we maintain a Docker image that you can extend or use directly.

A typical integration involves defining a service to generate a dockercfg for your registry in your services file and listing that as the authentication source for specific services and/or steps.

# codeship-services.yml
app: 
  build:
    image: 108487657846.dkr.ecr.us-east-1.amazonaws.com/myuser/myapp
    dockerfile_path: ./Dockerfile
aws_generator: 
  image: codeship/aws-ecr-dockercfg-generator
  encrypted_env_file: aws_creds.encrypted # contains Secret, AccessKey and Region
  add_docker: true
# codeship-steps.yml
- type: push
  service: app
  registry: https://108487657846.dkr.ecr.us-east-1.amazonaws.com
  image_name:  108487657846.dkr.ecr.us-east-1.amazonaws.com/myuser/myapp
  dockercfg_service: aws_generator

The aws_generator service will be run as needed and provides credentials to allow Codeship to interact with your ECR registry. In this example, we’re simply building an image and having our CD pipeline push it to ECR.

Using ECR Base Images and Caching

Along with explicitly pushing images to ECR as part of your pipeline, you can leverage your registry to host private base images used during a Codeship build.

In this case, as long as the step or service referencing the image being built references the dockercfg_service, and the FROM declaration in your Dockerfile references the full ECR image name, the build will work. Similarly, this will allow you to use ECR to populate your local Docker image cache during builds by specifying cached: true on relevant services.

# Dockerfile
FROM 108487657846.dkr.ecr.us-east-1.amazonaws.com/myuser/myapp

ADD ./ /opt/app
# codeship-services.yml
app: 
  build:
    image: myuser/myapp
    dockerfile_path: ./Dockerfile
  dockercfg_service: aws_generator
  cached: true
aws_generator: 
  image: codeship/aws-ecr-dockercfg-generator
  encrypted_env_file: aws_creds.encrypted # contains Secret, AccessKey and Region

Conclusion

Using dockercfg generator services from Codeship and integrating your existing CI/CD pipeline with ECR is a simple task. By adding a generator service, updating your image names, and replacing any existing dockercfg references with references to the new dockercfg generator service, you can push your images to ECR without making complex changes to your pipeline.

With the ability to co-locate your image registry with your containers, ECR is a powerful tool to help speed up the deployment and scaling of your AWS infrastructure.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles.
Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.



We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

  • Pingback: Integrating Your Codeship CI/CD Pipeline with AWS ECR-IT大道()

  • Pingback: AWS Week in Review – December 21, 2015 – SMACBUZZ()

  • Pingback: AWS Week in Review – December 21, 2015 | cloudmantra | SMAC, Cloud Consulting & Managed Services()

  • David Banham

    You have a really gnarly typo in one of your examples.

    encrypted_env: aws_creds.encrypted # contains Secret, AccessKey and Region

    Should be:

    encrypted_env_file: aws_creds.encrypted # contains Secret, AccessKey and Region

    Great feel when we finally figured out what was wrong! Less great feel that it took two of us a couple hours to do so. So much burned time :(

    • Thanks for letting us know, David. I’ve updated the post so we can save others the same pain!

    • I updated the code block, sorry for the trouble this caused and thanks for letting us know :)

  • Andres Tuul

    Marko Locher you still forgot to change the last example L10:
    encrypted_env_file: aws_creds.encrypted # contains Secret, AccessKey and Region

  • Giedrius Rimkus

    Only part of the examples were updated :-)

  • Kevin Art

    I find this a bit confusing. Which is the preferred way to generate credentials? Do I have to generate credentials at all when using Codeships predefined Docker Image. How do I encrypt the AWS credentials?

  • Ryan Bowlby

    When strictly pulling an image I had to add the add_docker: true line to the service file; unlike in the example. Just a heads up for anyone working on this.