Anemos vs Helm
Generating Kubernetes Manifests
Helm uses a templating engine to manage Kubernetes applications. This requires you to write YAML files with special templating syntax, which can be complex and hard to read. In addition, all the contents of a manifest must be defined in a single file, which can lead to large and unwieldy files that are difficult to maintain. The way to move parts of the manifest to separate files is to use named templates, which are essentially functions that can be reused across multiple manifests. However, defining logic in these templates is hard and readability and maintainability suffers as a result.
Anemos, on the other hand, provides an SDK for managing Kubernetes manifests using JavaScript or TypeScript. It allows template-based, object-based, and YAML node-based generation of manifests. Templates use JavaScript's template literals, which are more readable and maintainable than Helm's templating syntax. Objects allow you to define your manifests using JavaScript or TypeScript objects, which brings type safety and better tooling support. YAML node-based approach allows you to manipulate YAML documents directly, which can be useful for modifying deeply nested structures without having to write complex templates or objects.
Take a look at the following examples from HashiCorp Vault Helm chart and their equivalents in Anemos:
- Helm Manifest
- Helm Named Template
{{/*
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
*/}}
{{ template "vault.serverServiceAccountEnabled" . }}
{{- if .serverServiceAccountEnabled -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "vault.serviceAccount.name" . }}
namespace: {{ include "vault.namespace" . }}
labels:
helm.sh/chart: {{ include "vault.chart" . }}
app.kubernetes.io/name: {{ include "vault.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Values.server.serviceAccount.extraLabels -}}
{{- toYaml .Values.server.serviceAccount.extraLabels | nindent 4 -}}
{{- end -}}
{{ template "vault.serviceAccount.annotations" . }}
{{ end }}
{{/*
Compute if the server serviceaccount is enabled.
*/}}
{{- define "vault.serverServiceAccountEnabled" -}}
{{- $_ := set . "serverServiceAccountEnabled"
(and
(eq (.Values.server.serviceAccount.create | toString) "true" )
(or
(eq (.Values.server.enabled | toString) "true")
(eq (.Values.global.enabled | toString) "true"))) -}}
{{- end -}}
// Compute if the server serviceaccount is enabled.
function serverServiceAccountEnabled(): boolean {
return options.server.serviceAccount.create && (options.server.enabled || options.global.enabled);
}
function addServiceAccount(context: anemos.BuildContext): void {
if (!serverServiceAccountEnabled()) {
return;
}
const document = anemos.parseDocument(
"server-serviceaccount.yaml",
`
apiVersion: v1
kind: ServiceAccount
metadata:
name: ${options.name}
namespace: ${options.namespace}
labels:
app.kubernetes.io/name: vault
app.kubernetes.io/instance: ${options.name}
app.kubernetes.io/managed-by: anemos
`
);
// You can get labels from options and pass to the document. Alternatively users themselves can set
// labels on generated documents.
document.setLabels(options.labels);
context.addDocument(document);
}
Modifying Kubernetes Manifests
Helm doesn't provide a good way to modify generated manifests. It requires you to execute other commands as post render hooks to modify the manifests, which can be cumbersome and error-prone. Since post render hooks are not practical, Helm charts often contain large amounts of boilerplate code to define parts of the manifest such as node selectors, tolerations, affinity rules, etc. Central Helm charts repo is archived, but it has a good example of how much effort goes into defining these boilerplate parts. Just searching for affinity brings up 277 pull requests, and most of them are about adding affinity rules to the charts.
Anemos supports manipulating YAML nodes directly, which can be useful for quick modifications or when you need to adapt YAML documents that are generated by another part of your codebase or by a third-party package. It allows you to centralize boilerplate code in a single place. For example, you can define a function that sets the image pull policy for all your workloads, or a function that adds common labels to all your manifests. This powerful feature allows you to modify any YAML document, without waiting for the package maintainers to add support for your use case or fix a bug in the package.
Another benefit of programmatic access to YAML nodes is that it allows you to validate and lint your manifests according to your organization's standards. This allows you to enforce best practices and avoid common pitfalls before deploying your manifests to the cluster.
Managing Multiple Applications
Helm charts mostly focus on managing a single application. While it is possible to use Helm to manage multiple applications with subcharts, they only cover the dependencies of the main chart. Unrelated applications are managed separately, which can lead to a fragmented and inconsistent configuration. Common values such as image registries or annotations are often duplicated across multiple values files.
Anemos projects are designed to manage multiple applications in a single codebase. All applications for the target cluster can be defined in a single Anemos project. It's also possible to manage multiple environments (e.g., development, staging, production) in the same project with different configurations for each environment. This allows you to manage common logic accross all environments and applications, while still being able to customize each application and environment as needed.
Third-Party Packages
Helm doesn't provide a mechanism to use third-party libraries. Reusable code is only accessible through the Helm chart itself. Code duplication is common accross charts and since everything is based on string interpolation, it is hard to define data structures or refactor code without introducing bugs.
Anemos allows you to use third-party packages from the NPM ecosystem. These packages can generate manifests,
provide utilities for manipulating manifests, validate or lint manifests, generate reports, or provide any other functionality
unrelated to Kubernetes manifests. For example, you can use the @grafana/grafana-foundation-sdk
package to generate Grafana dashboards
using staticly typed objects and embed them into your manifests. This allows you to leverage the power of the JavaScript ecosystem
and use existing libraries to enhance your Anemos projects.
Applying Manifests
Helm has a built-in command to apply manifests to the cluster. This command makes it easy to quickly deploy
your manifests to a cluster. Anemos, currently, does not have a built-in command to apply manifests. This
may change in the future, but for now, you can use other CLI tools such as kubectl
or kapp
, or GitOps tools like
ArgoCD or Flux to apply your manifests to the cluster.
Interoperability with Helm Charts
Anemos supports generating documents from Helm charts, allowing you to use your existing Helm charts along the migration process. Visit the Helm Interoperability page for more details on how to use Helm charts in Anemos.