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:
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 setCOPYFILE_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.