I was interested to see if I could blend a Helm Chart (packing and deployment) and an Operator (day-2 operations) in a single solution, so I developed a proof of concept for ChartMuseum.
I have been wanting to play with the new Operator SDK from RedHat for a while. I like the concept of having an api for the life-cycle management. IMHO, operators are a development pattern that matches declarative Kubernetes API objects to lifecycle events like backup, restore, and other type of maintenance tasks.
In general, most operator implementations that I have played with also take care of the deployment of application. In some cases, like the etcd operator, they manage the life-cycle of pods directly rather than using standard objects like deployments or replicasets .
I have been doing some Helm Chart development and I really like the flexibility that Helm gives you to parametrise deployments. It seemed to me that I could still use a Helm Chart for packing and deployment, and enhance it with an operator to be included in the releases to manage the application management beyond deployment.
As a proof of concept, I decided to try to extend the existing upstream chartmuseum chart with an operator that took care of adding and packaging charts from a github repo.
The basic operations that I set up to automate were:
- Pulling a new git repository and helm packaging its contents
- Regularly pulling updates and repackage a repo
- Removing a git repository and its contents
I needed to be able to ask the chartmuseum to perform these activities. Using the adapter/sidecar pattern, I developed a new container to expose these as http endpoints and package dependencies (git and helm) to be bolted to chartmuseum’s container within the same pod.
So a Custom Resource for this operator will look like this:
apiVersion: "cm.bitnami.com/v1alpha1" kind: "Chartmuseum" metadata: name: "myrepo" spec: git: "https://github.com/foo/mycharts" updateEveryMinutes: 15 dependencies: - name: bitnami url: "https://charts.bitnami.com/bitnami"
The git repository has to be publicly available. The helm charts might be pulling dependencies from other repos, so I also added the ability to define these.
updateEveryMinutes is an integer value that indicates how often, in minutes, the git repo should be updated. Instead of having this functionality baked into the operator, it creates Cronjob objects to trigger the update. This objects are gettable and visible to the user.
On creation of a new custom resource of type Chartmuseum, the operator will:
- Add dependencies as required – POST /repo/dependency
- Then add a new repository, which will trigger a git clone, and a packaging of all folders in the repo that contain a “Chart.yaml” file – POST /repo/new
- A new Cronjob will be created with the same name and namespace than the Custom Resource that will updateEveryMinutes hit GET /repo/name/update
On deletion of the custom resource, the operator will:
- Delete the repo and its artifacts – DELETE /repo/name
- Delete the Cronjob object
Incorporating this into an existing Helm Chart was relatively simple. I created a new value flag called operator.enabled, which if set to true will add additional manifests to the release by:
- modifying the main deployment to add the sidecar container,
- modifying the main service to expose a new port for sync operations,
- adding CRD for Chartmuseum and a deployment for the Operator,
- adding RBAC support for the operator to read CRD and create Cronjobs
In summary, I found the operator-sdk really simple to use and work with. It really takes away the fiddly parts of creating an operator and lets you focus on the logic. I also found that the operator and the helm chart worked nicely together and provided a good transparency and control to the user.
If you want to try it, just clone this repo: https://github.com/vtuson/chartmuseum-chart and run:
helm install . -n mychartmuseum