Containers

OCI Artifact Support In Amazon ECR

By Shubhra Deshpande and Michael Hausenblas

In the container roadmap issue 308 you asked us about making Amazon Elastic Container Registry (ECR) understand artifact types beyond container images. We now launched support for Open Container Initiative (OCI) artifacts, such as Helm charts. In this post we give you some background on OCI artifacts and walk you through some hands-on usage of this new Amazon ECR feature.

While our initial focus with Amazon ECR was on managing container images, the community soon figured that those are not the only build or runtime artifacts worthy of having in a registry. Indeed, from Kubernetes-specific deployment artifacts (YAML manifests and the like) found in Helm to policy documents such as the Open Policy Agent (OPA) project’s bundles. You want to store and manage them in a uniform manner in one location. In addition, you want to offload the management of the registry to us and focus on the handling of the artifacts themselves.

With today’s launch, we support pushing and pulling OCI artifacts in Amazon ECR. The scope of this launch is SDK and CLI support, with AWS console following later this year. Based on your feedback, we can iterate on the current feature set and especially, going forward, concerning the UX of the artifact management in the console. Before we get into the inner workings, let’s step back a bit and make sure we’re on the same page concerning the foundations: what are OCI artifacts?

OCI artifacts specification

The OCI artifact specification defines artifact types, akin to file extensions, represented by the config.mediaType field of a manifest. For example, an OCI image, that is, something that you can use in Amazon EKS or Amazon ECS, would have a value of application/vnd.oci.image.config.v1+json. Custom types can also be defined, following the format:

[registration-tree].[org|company|entity].[objectType].[optional-subType].config.[version]+[optional-configFormat]

You can learn more about the details of the format from the spec.

NOTE If you want to allocate a public registration-tree, you need to interact with IANA and register your globally unique media type as per RFC 6838. In most cases this is not necessary and Amazon ECR accepts any media type.

Further, the artifact’s content comprises one or more layers and an (optional) configuration object with the layer format being up to the artifact author. On the layer level we see a similar pattern as on the overall artifact, this time using layer.mediaTypes to define its content format. For example, application/vnd.cncf.helm.chart.layer.v1.tar+gzip would represent a gzip-compressed Helm chart as the format.

In Amazon ECR we store content-addressable layers and an accompanying manifest containing sha256 digests of
each layer, an overall config element, and an optional overall annotations element. In addition, each layer and the single config element contain a media type describing stored contents. In order to support OCI artifacts we decided to enhance the existing APIs rather than adding new commands. This allows maximum backwards compatibility and least surprises.
In a nutshell, this means that you push and pull arbitrary OCI artifacts just like container images and use filter parameters
for content types as needed. For example, in the aws ecr describe-images command you’d use the --manifest-config flag to specify the artifact types. More about that later.

One way to visualize the artifact types and their relations is a tree view:

In above figure any of the non-index types such as an OCI image can also be nested under an index or under a manifest list. And an index can be under an index as well., turtles all the way down. With the theory out of the way, let’s see OCI artifacts in action, now!

OCI artifacts in action

In the following we will show you the new OCI artifacts feature in Amazon ECR in action. First off, we set up a few things we will be using in the two scenarios below:

export REPO_NAME=artifact-test
export REPO_URI=$(aws ecr create-repository --repository-name $REPO_NAME \
                  --query 'repository.repositoryUri' --output text)
export REGISTRY=${REPO_URI/"/$REPO_NAME"/}

With that out of the way, let’s look at the first use case: Helm charts, used for Kubernetes deployments.

Helm charts in Amazon ECR

Let’s first see how you can use Amazon ECR to publish and consume Helm charts with Helm 3.

First off some preparation, setting up environment variables:

export CHART_TAG=helm-ehw-0
export REPO_CHART=$REPO_URI:$CHART_TAG
export HELM_EXPERIMENTAL_OCI=1

Now we create and export the Helm chart using:

helm create example-hello-world
helm chart save example-hello-world $REPO_CHART

Next, log into Amazon ECR with:

aws ecr get-login-password --region eu-west-1 | \
 helm registry login -u AWS --password-stdin $REGISTRY

NOTE In case you’re having issues with the login command, check out the respective docs section.

And finally publish the Helm chart to ECR:

helm chart push $REPO_CHART

Once that’s done you can consume the Helm chart as follows:

$ aws ecr batch-get-image \
    --repository-name $REPO_NAME \
    --image-ids imageTag=$CHART_TAG \
    --query 'images[].imageManifest' --output text
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.cncf.helm.config.v1+json",
    "digest": "sha256:d10882dfb2c97a6a8ff886f42a174a475822b3aa1a6bc48c67e4ddc6183ef2a6",
    "size": 153
  },
  "layers": [
    {
      "mediaType": "application/tar+gzip",
      "digest": "sha256:5f0823edaeed5d40a1dffeb5029454426ab2e4626c39c999ceba9661f035d6ce",
      "size": 3591
    }
  ]
}

NOTE If you want to learn more about Helm charts with ECR, check out our ECR docs on Pushing a Helm chart and Installing a Helm chart hosted on Amazon ECR with Amazon EKS.

Helm charts are not the only artifact types that you can use Amazon ECR as a repository for. Let’s see another example.

OPA bundles in Amazon ECR

We move on to publishing and consuming Open Policy Agent (OPA) bundles in this part. For demonstration purposes we’re using a simple, static, self-containing setup. Paste the following into a file called demo.rego:

 

package temporal

ts_in_rfc3339 := "2020-08-07T06:05:04Z"
ts := time.parse_rfc3339_ns(ts_in_rfc3339)
now := time.now_ns()

adate = { "Y" : year, "M": month, "D": day } {
  [year, month, day] := time.date(ts)
}

atime = { "h" : hour, "m": minute, "s": second } {
  [hour, minute, second]:= time.clock(ts)
}

aweekday := time.weekday(ts)

one_month_in_the_future = output {
  y := 0
  m := 1
  d := 0
  omif_ts := time.add_date(ts, y, m, d)
  output := epoch2rfc3339(omif_ts)
}

epoch2rfc3339(ts) = res {
  [Y, M, D] := time.date(ts)
  [h, m, s]:= time.clock(ts)
  res := sprintf("%d-%02d-%02dT%02d:%02d:%02dZ", [Y, M, D, h, m, s])
}

Now you publish the OPA bundle as follows (you can verify the content with tar tzf bundle.tar.gz):

tar -czvf bundle.tar.gz temporal-demo/

NOTE If you’re on macOS, when compressing the content using above tar command, make sure to set COPYFILE_DISABLE=1 so to exclude extended attributes.

For the publishing part we’re using the oras CLI (containerized here, but you can directly install it):

alias oras='docker run -it --rm -v $(pwd):/workspace orasbot/oras:v0.8.1'
OPA_BUNDLE=opa-temporal-0
OPA_MEDIA_TYPE_LAYER=application/vnd.cncf.openpolicyagent.manifest.layer.v1+json
echo '{}' > empty-config.json
OPA_MEDIA_TYPE_CONFIG=empty-config.json:application/vnd.cncf.openpolicyagent.config.v1+json

Next, let’s push the OPA bundle into our ECR repo:

oras push -u AWS -p $(aws ecr get-login-password) \
     $REPO_URI:$OPA_BUNDLE \
     --manifest-config $OPA_MEDIA_TYPE_CONFIG \
     bundle.tar.gz:$OPA_MEDIA_TYPE_LAYER

Using the OPA bundle starts with a pull from the Amazon ECR repo:

oras pull -u AWS -p $(aws ecr get-login-password) \
     $REPO_URI:$OPA_BUNDLE \
     --media-type $OPA_MEDIA_TYPE_LAYER

We are now in a position to use the OPA bundle from some client; again, for demonstration purposes we simply use the OPA command line in server mode, but really this could be anything that consumes Rego rules and data in JSON format:

$ opa run --server bundle.tar.gz 
{"addrs":[":8181"],"diagnostic-addrs":[],"insecure_addr":"","level":"info","msg":"Initializing server.","time":"2020-08-10T14:39:32+01:00"}

In a second terminal, we query the rules (assuming jq is available, for nicer formatting):

$ curl -s localhost:8181/v1/data/temporal | jq .
{
  "result": {
    "adate": {
      "D": 7,
      "M": 8,
      "Y": 2020
    },
    "atime": {
      "h": 6,
      "m": 5,
      "s": 4
    },
    "aweekday": "Friday",
    "now": 1597065946075459000,
    "one_month_in_the_future": "2020-09-07T06:05:04Z",
    "ts": 1596780304000000000,
    "ts_in_rfc3339": "2020-08-07T06:05:04Z"
  }
}

OK, that was a lot. Let’s relax a bit and turn our attention to the next steps and what you can do to help us making the artifact feature even more useful.

Next steps

Hopefully you now have a better understanding of OCI artifacts and how we support them in Amazon ECR. We plan to continue our involvement in OCI, contributing to opencontainers/artifacts, upstream. Further, we want to advance the support of artifact types and make the feature a first class citizen in the AWS console, in the near to mid future. Try out CloudFormation templates, Docker compose files, or CNAB bundles. We’d like to learn from you where and how you plan to use this new feature and as you toto give us feedback what other related functionality you would consider useful via the containers roadmap.

Shubhra Deshpande

Shubhra Deshpande

Shubhra is a Software Development Engineer in the Amazon ECR team. Before joining AWS, Shubhra graduated with master’s degree in computer science and engineering from The State University of New York.