Optimizing Your Container Registry: Pushing Helm Charts to AWS ECR

Optimizing Your Container Registry: Pushing Helm Charts to AWS ECR

Traditionally we have used container registries to store only container images, but they offers much more than that. Let's explore in this blog.

ยท

7 min read

We have always used container registries like AWS ECR, Docker Hub, etc. to host our container images but did you know you can also push other artifacts like Helm Charts to your favorite registry?

Continue Season 9 GIF by The Office

Let's learn how can we do that with AWS ECR.

Bonus! - Along with AWS ECR, we will also explore how we can use Docker Hub to host our helm charts.


Containers and OCI

Containers have transformed the landscape of modern software development and deployment, enabling faster and more reliable shipping of software. Organizations are running containers at a massive scale.

Back when containerization was new, evolving, and getting popular following Docker's release, a large community emerged and started developing new container runtimes to solve their needs.

To solve the issue of incompatibility between different runtimes and image formats, the community formed Open Container Initiative(OCI).

"The Open Container Initiative (OCI) is a lightweight, open governance structure (project), formed under the auspices of the Linux Foundation, for the express purpose of creating open industry standards around container formats and runtimes. The OCI was launched on June 22nd 2015 by Docker*,* CoreOS and other leaders in the container industry."

Currently, it defines three specifications:

OCI Runtime Specification

  • A technical specification developed by the Open Container Initiative (OCI) that defines the configuration and lifecycle of containers at the runtime level.

  • Includes various aspects like :

    • Container Filesystem

    • Container Lifecycle

    • Container Processes

    • Container Configuration

    • Container Security

  • More info here: https://github.com/opencontainers/runtime-spec

OCI Image Specification

  • A technical specification developed by the Open Container Initiative (OCI) that defines the format and structure of container images.

  • It includes various aspects like :

    • Image Layout

    • Layers

    • Image Manifest

    • Image Configuration

    • Image References

  • More info here: https://github.com/opencontainers/image-spec

OCI Distribution Specification

  • It defines an API protocol to facilitate and standardize the distribution of content.

  • More info here: https://github.com/opencontainers/distribution*-spec*

  • "This is designed generically enough to be leveraged as a distribution mechanism for any type of content. The format of uploaded manifests, for example, need not necessarily adhere to the OCI Image Format Specification so long as it references the blobs which comprise a given artifact."

Adoption

  • You can find various popular container runtimes that have adopted OCI standards, for example:

  • One of the benefits of having a standard like OCI is that it ensures that containers created using different container runtimes and tools can be easily shared and executed across different environments.

Enough with the theories and blah blah blah...Let's jump right into the action!

homer simpson GIF

Push your Helm chart to ECR

Prerequisites

Creating a Helm Chart

  1. Let's create a helm chart from scratch using helm cli

     helm create my-chart-for-ecr
    

    it's a standard command which will generate a basic helm chart (which basically deploys an NGINX server.)

  2. Let's read the content of Chart.yaml

    cat my-chart-for-ecr/Chart.yaml

     apiVersion: v2
     name: my-chart-for-ecr
     description: A Helm chart for Kubernetes
    
     # A chart can be either an 'application' or a 'library' chart.
     #
     # Application charts are a collection of templates that can be packaged into versioned archives
     # to be deployed.
     #
     # Library charts provide useful utilities or functions for the chart developer. They're included as
     # a dependency of application charts to inject those utilities and functions into the rendering
     # pipeline. Library charts do not define any templates and therefore cannot be deployed.
     type: application
    
     # This is the chart version. This version number should be incremented each time you make changes
     # to the chart and its templates, including the app version.
     # Versions are expected to follow Semantic Versioning (https://semver.org/)
     version: 0.1.0
    
     # This is the version number of the application being deployed. This version number should be
     # incremented each time you make changes to the application. Versions are not expected to
     # follow Semantic Versioning. They should reflect the version the application is using.
     # It is recommended to use it with quotes.
     appVersion: "1.16.0"
    

    Note that version of this helm chart is 0.1.0

  3. Let's package (packages a chart into a versioned chart archive file) the chart so we can push it.

     helm package ./my-chart-for-ecr/
    

Output :

Preparing ECR

  1. Create a Repo in ECR for our use

     aws ecr create-repository \
          --repository-name my-chart-for-ecr \
          --region us-east-1 
     # change region as per your need
    

Getting Creds for ECR Authorization

  1. Authenticate your Helm client for our ECR

     aws ecr get-login-password \
          --region us-east-1 | helm registry login \
          --username AWS \
          --password-stdin AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com
    

Pushing Helm Chart to the ECR

  1. Push the chart using helm CLI.
helm push my-chart-for-ecr-0.1.0.tgz oci://AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/
  1. Describe your Helm chart.
aws ecr describe-images \
     --repository-name my-chart-for-ecr \
     --region us-east-1

Output :

let's observe it in AWS Console > ECR

Using Helm Chart hosted on AWS ECR

  1. Login to AWS ECR and Authenticate your helm client. (Just like we did in the above step.)

     aws ecr get-login-password \
          --region us-east-1 | helm registry login \
          --username AWS \
          --password-stdin AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com
    
  2. Install the helm chart ( --dry-run to see the output)

     helm install chart-from-ecr oci://AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/my-chart-for-ecr --version 0.1.0 --dry-run
    

Output:

Pulled: 333333333333.dkr.ecr.us-east-1.amazonaws.com/my-chart-for-ecr:0.1.0
Digest: sha256:5b14b2094ab4581a74ed2bc036993da6cdee10c06cd463027a50f2b227ebf7da
NAME: chart-from-ecr
LAST DEPLOYED: Sat Jun  3 12:04:42 2023
NAMESPACE: crafto
STATUS: pending-install
REVISION: 1
HOOKS:
---
# Source: my-chart-for-ecr/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "chart-from-ecr-my-chart-for-ecr-test-connection"
  labels:
    helm.sh/chart: my-chart-for-ecr-0.1.0
    app.kubernetes.io/name: my-chart-for-ecr
    app.kubernetes.io/instance: chart-from-ecr
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['chart-from-ecr-my-chart-for-ecr:80']
  restartPolicy: Never
MANIFEST:
---
# Source: my-chart-for-ecr/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: chart-from-ecr-my-chart-for-ecr
  labels:
    helm.sh/chart: my-chart-for-ecr-0.1.0
    app.kubernetes.io/name: my-chart-for-ecr
    app.kubernetes.io/instance: chart-from-ecr
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: my-chart-for-ecr/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: chart-from-ecr-my-chart-for-ecr
  labels:
    helm.sh/chart: my-chart-for-ecr-0.1.0
    app.kubernetes.io/name: my-chart-for-ecr
    app.kubernetes.io/instance: chart-from-ecr
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: my-chart-for-ecr
    app.kubernetes.io/instance: chart-from-ecr
---
# Source: my-chart-for-ecr/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chart-from-ecr-my-chart-for-ecr
  labels:
    helm.sh/chart: my-chart-for-ecr-0.1.0
    app.kubernetes.io/name: my-chart-for-ecr
    app.kubernetes.io/instance: chart-from-ecr
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: my-chart-for-ecr
      app.kubernetes.io/instance: chart-from-ecr
  template:
    metadata:
      labels:
        app.kubernetes.io/name: my-chart-for-ecr
        app.kubernetes.io/instance: chart-from-ecr
    spec:
      serviceAccountName: chart-from-ecr-my-chart-for-ecr
      securityContext:
        {}
      containers:
        - name: my-chart-for-ecr
          securityContext:
            {}
          image: "nginx:1.16.0"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}

NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace crafto -l "app.kubernetes.io/name=my-chart-for-ecr,app.kubernetes.io/instance=chart-from-ecr" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace crafto $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace crafto port-forward $POD_NAME 8080:$CONTAINER_PORT

So Yes! ๐ŸŽ‰ ๐ŸŽ‰ ๐ŸŽ‰ We were able to see all the manifest this helm chart was going to install.

SpongeBob gif. SpongeBob pumps his arms up and down excitedly, biting his little yellow lip.

[Bonus] Push your Helm chart to DockerHub

  1. Let's continue from the part where we ran the helm package

  2. Log in to Docker Hub using your Helm client.

     helm registry login registry-1.docker.io -u DOCKERHUB_USERNAME
    
  3. Push your chart to a DockerHub.

     helm push my-chart-for-ecr-0.1.0.tgz oci://registry-1.docker.io/DOCKERHUB_USERNAME
    
  4. This will create a docker repository called my-chart-for-ecr in your DockerHub account. (Image would be DOCKERHUB_USERNAME/my-chart-for-ecr)

Note - we are able to push/host our helm charts to our same old container registries because Helm Chart is another OCI artifact! (see reference links for more info)


Final words

This was just a way to store your helm charts on ECR / Docker Hub or any other OCI-compliant repositories. You can version control this (either with your services or in a separate repo) and also set up some CI/CD pipeline with some automated testing(maybe using k3s/kind to spin a temp local k8s cluster and to test deploying your helm chart there) to achieve a complete lifecycle of your helm chart, just like a regular software project.

Thank you so much for being here! Have a nice one and see you in the next blog.

Season 9 Nbc GIF by The Office


References

  1. https://docs.docker.com/docker-hub/oci-artifacts/#using-oci-artifacts-with-docker-hub

  2. https://helm.sh/docs/topics/registries/#using-an-oci-based-registry

  3. https://docs.aws.amazon.com/AmazonECR/latest/userguide/push-oci-artifact.html

  4. https://docs.aws.amazon.com/AmazonECR/latest/userguide/ECR_on_EKS.html#using-helm-charts-eks

Did you find this article valuable?

Support Kratik Jain by becoming a sponsor. Any amount is appreciated!

ย