Package Manager for Kubernetes - Helm: Past, Present, Future

Original author: Matt Butcher
  • Transfer
Note perev. : With this article we open a series of publications about the package manager for Kubernetes, which we actively use in everyday work, - Helm. The original author of the material is Matt Butcher, one of the founders of the Helm project, working on Open Source projects at Microsoft and writing 8 technical books (in particular, “Go in Practice”). However, the article is supplemented by our (sometimes extensive) comments, and will soon be further expanded with new notes on Helm with a more practical focus. UPDATE (09/03/2018): the sequel came out - " Practical acquaintance with the package manager for Kubernetes - Helm ".



In June, Helm movedfrom Kubernetes lead project status to the Cloud Native Computing Foundation (CNCF). CNCF is becoming the parent organization for the best of its kind open source cloud native tools. Therefore, it is a great honor for Helm to become part of such a foundation. And our first significant project under the auspices of CNCF is truly large-scale: we create Helm 3.

A Brief History of Helm


Helm originally appeared as a Deis Open Source project. It was modeled after Homebrew (the package manager for macOS - approx. Transl. ) , And the task Helm 1 had was a facilitated opportunity for users to quickly install their first workloads on Kubernetes. The official announcement of Helm took place at the first KubeCon San Francisco conference in 2015.

Note trans .: From the first version, which was called dm (Deployment Manager), YAML syntax was chosen to describe Kubernetes resources, and Jinja templates and Python scripts were supported when writing configurations .

A simple web application template might look like this:

Yaml
resources:
- name: frontend
  type: github.com/kubernetes/application-dm-templates/common/replicatedservice:v1
  properties:
    service_port: 80
    container_port: 80
    external_service: true
    replicas: 3
    image: gcr.io/google_containers/example-guestbook-php-redis:v3
- name: redis
  type: github.com/kubernetes/application-dm-templates/storage/redis:v1
  properties: null

When describing the components of the rolled out application, the name, the template used, and also the necessary parameters of this template are indicated. In the example above, the resources frontendand the redisuse of the official repository templates.

Already in this version, you can use resources from a common knowledge base, create your own template repositories and build complex applications due to the parameters and nesting of templates.

The Helm 1 architecture consists of three components. The following diagram illustrates the relationship between them:



  1. Manager performs the function of a web server (communication with clients occurs via the REST API), manages the deployments in the Kubernetes cluster and is used as a data warehouse.
  2. The component expandybirdbrings user configurations to a flat shape, i.e. applies Jinja templates and executes Python scripts.
  3. Having received a flat configuration, it resourcifiermakes the necessary calls to kubectl and returns to the managerstatus and error messages, if any.

To understand the capabilities of the first version of Helm, I’ll give help on the commanddm :
Help output from dm
Usage: ./dm []  [( |  | ( [...]))]
Commands:
expand 			 Expands the supplied configuration(s) 
deploy 			 Deploys the named template or the supplied configuration(s)
list 			 Lists the deployments in the cluster
get 			 Retrieves the supplied deployment
manifest 		 Lists manifests for deployment or retrieves the supplied manifest in the form (deployment[/manifest])
delete 			 Deletes the supplied deployment
update 			 Updates a deployment using the supplied configuration(s)
deployed-types 		 Lists the types deployed in the cluster
deployed-instances 	 Lists the instances of the named type deployed in the cluster
templates 		 Lists the templates in a given template registry (specified with --registry)
registries 		 Lists the registries available
describe 		 Describes the named template in a given template registry
getcredential 		 Gets the named credential used by a registry
setcredential 		 Sets a credential used by a registry
createregistry 		 Creates a registry that holds charts
Flags:
  -apitoken string
    	Github api token that overrides GITHUB_API_TOKEN environment variable
  -binary string
    	Path to template expansion binary (default "../expandybird/expansion/expansion.py")
  -httptest.serve string
    	if non-empty, httptest.NewServer serves on this address and blocks
  -name string
    	Name of deployment, used for deploy and update commands (defaults to template name)
  -password string
    	Github password that overrides GITHUB_PASSWORD environment variable
  -properties string
    	Properties to use when deploying a template (e.g., --properties k1=v1,k2=v2)
  -regex string
    	Regular expression to filter the templates listed in a template registry
  -registry string
    	Registry name (default "application-dm-templates")
  -registryfile string
    	File containing registry specification
  -service string
    	URL for deployment manager (default "http://localhost:8001/api/v1/proxy/namespaces/dm/services/manager-service:manager")
  -serviceaccount string
    	Service account file containing JWT token
  -stdin
    	Reads a configuration from the standard input
  -timeout int
    	Time in seconds to wait for response (default 20)
  -username string
    	Github user name that overrides GITHUB_USERNAME environment variable
--stdin requires a file name and either the file contents or a tar archive containing the named file.
        a tar archive may include any additional files referenced directly or indirectly by the named file.

Now back to the original Helm story ...

A few months later, we joined forces with Google’s Kubernetes Deployment Manager and started working on Helm 2. The goal was to keep Helm simple to use by adding the following:

  1. chart templates (“chart” - an analogue of a package in the Helm ecosystem - approx. transl. ) for customization;
  2. cluster management for teams;
  3. full chart repository;
  4. stable and signed package format;
  5. strong commitment to semantic versioning and maintaining backward compatibility from version to version.

To achieve these goals, a second component has been added to the Helm ecosystem. It became the inside Tiller cluster, which provided the installation of Helm charts and their management.

Note perev .: Thus, in the second version of Helm, the only component remaining in the cluster is responsible for the installation life cycle ( release ), and configuration preparation is submitted to the Helm client.

If rebooting the cluster when using the first version of Helm led to a complete loss of service data (since they were stored in RAM), then in Helm 2 all data is stored in ConfigMaps, i.e. resources inside Kubernetes. Another important step was the transition from a synchronous API (where every request was blocking) to using asynchronous gRPC.

Since the launch of Helm 2 in 2016, the Kubernetes project has experienced explosive growth and significant new opportunities. Role Based Access Control (RBAC) has been added. Many new resource types are introduced. Invented third-party resources (Custom Resource Definitions, CRD). And most importantly, there are best practices. Going through all these changes, Helm continued to serve the needs of Kubernetes users. But it became clear to us that it was time to make major changes to it so that the needs of this developing ecosystem continued to be met.

So we came to Helm 3. Next I will talk about some of the innovations featured on the project roadmap.

Greetings Lua


In Helm 2, we introduced templates. At the early stage of Helm 2 development, we supported Go, Jinja templates, clean Python code, and we even had a prototype of ksonnet support. But the presence of many engines for templates gave rise to more problems than it solved. Therefore, we have come to the point of choosing one.

Go templates had four advantages:

  1. the library is built into Go ;
  2. templates are executed in a strictly limited sandbox environment;
  3. we could insert arbitrary functions and objects into the engine ;
  4. they worked well with YAML.

Although we retained the interface in Helm to support other template engines, Go templates have become our default standard. And the next few years of experience showed how engineers from many companies created thousands of charts using Go templates.

And we learned about their disappointments:

  1. The syntax is difficult to read and poorly documented.
  2. Language issues, such as immutable variables, intricate data types, and restrictive visibility rules, have turned simple things into complex ones.
  3. The inability to define functions within templates made the creation of reusable libraries even more difficult.

Most importantly, using the template language, we “truncated” Kubernetes objects to their line representation. (In other words, template developers had to manage Kubernetes resources as text documents in YAML format.)

Work on objects, not pieces of YAML


Again and again, we heard from users a request for the ability to inspect and modify Kubernetes resources as objects, not strings. At the same time, they were adamant that no matter what way of implementation we chose for this, it should be easy to learn and well maintained in the ecosystem.

After months of research, we decided to provide a built-in scripting language that can be packaged in a sandbox and customized. Among the top 20 languages, there was only one candidate that met the requirements: Lua .

In 1993, a group of Brazilian IT engineers created a lightweight scripting language for embedding in their tools. Lua has a simple syntax, it is widely supported and has been featured in the list of top 20 languages for a long time. It is supported by IDE and text editors, there are many manuals and tutorials. On such an existing ecosystem, we would like to develop our solution.

Our work on Helm Lua is still at the conceptual proof stage, and we expect syntax that is both familiar and flexible. Comparing the old and the new approaches, you can see where we are moving.

Here is an example of a hearth template with Alpine in Helm 2:

apiVersion: v1
kind: Pod
metadata:
  name: {{ template "alpine.fullname" . }}
  labels:
    heritage: {{ .Release.Service }}
    release: {{ .Release.Name }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    app: {{ template "alpine.name" . }}
spec:
  restartPolicy: {{ .Values.restartPolicy }}
  containers:
  - name: waiter
    image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
    imagePullPolicy: {{ .Values.image.pullPolicy }}
    command: ["/bin/sleep", "9000"]

In this straightforward template, you can immediately see all the built-in template directives, such as {{ .Chart.Name }}.

And here is the definition of the same hearth in the preliminary version of the Lua code:

unction create_alpine_pod(_)
  local pod = {
    apiVersion = "v1",
    kind = "Pod",
    metadata = {
      name = alpine_fullname(_),
      labels = {
        heritage = _.Release.Service or "helm",
        release = _.Release.Name,
        chart = _.Chart.Name .. "-" .. _.Chart.Version,
        app = alpine_name(_)
      }
    },
    spec = {
      restartPolicy = _.Values.restartPolicy,
      containers = {
        {
          name = waiter,
          image = _.Values.image.repository .. ":" .. _.Values.image.tag,
          imagePullPolicy = _.Values.image.pullPolicy,
          command = {
            "/bin/sleep",
            "9000"
          }
        }
      }
    }
  }
  _.resources.add(pod)
end

There is no need to look at every line of this example to understand what is happening. It’s immediately obvious that in the code it is defined under. But instead of using YAML strings with built-in template directives, we define it as an object in Lua.

Let's shorten this code


Since we work directly with objects (instead of manipulating a large glob of text), we can take full advantage of scripting. The opportunities to create shared libraries that appear here look really attractive. And we hope that by introducing specialized libraries (or allowing the community to create them), we can reduce the above code to something like this:

local pods = require("mylib.pods");
function create_alpine_pod(_)
  myPod = pods.new("alpine:3.7", _)
  myPod.spec.restartPolicy = "Always"
  -- set any other properties
  _.Manifests.add(myPod)
end

In this example, we use the ability to work with the definition of a resource as an object, which is easy to set properties, while maintaining conciseness and readability of the code.

Templates ... Lua ... Why not all together?


Although the templates are not so wonderful for all tasks, they still have certain advantages. Templates in Go is a stable technology with an established user base and many existing charts. Many chart developers claim to like writing templates. Therefore, we are not going to remove template support.

Instead, we want to allow both templates and Lua to be used at the same time. Lua scripts will have access to Helm templates before and after they are rendered, which will allow advanced chart developers to perform complex transformations on existing charts, while retaining the simple ability to create Helm charts with templates.

We are very encouraged by the support of scripts on Lua, but at the same time we are getting rid of a significant part of the Helm architecture ...

Saying goodbye to Tiller


During the development of Helm 2, we introduced Tiller as an integration component with Deployment Manager. Tiller played an important role for teams working on the same cluster: it made it possible to interact with the same set of releases for many different administrators.

However, Tiller acted as a giant sudo server, giving a wide range of rights to everyone who has access to Tiller. And our default installation scheme was permissive configuration. Therefore, DevOps and SRE engineers had to learn the additional steps to install Tiller in multi-tenant clusters.

Moreover, with the advent of CRD, we could no longer reliably rely on Tiller to maintain state or function as a central hub for Helm release information. We could only store this information as separate entries in Kubernetes.

The main goal of Tiller can be achieved without Tiller itself. Therefore, one of the first decisions made during the Helm 3 planning phase was to completely abandon Tiller.

Security improvement


Without Tiller, the Helm security model is radically simplified. User authentication is delegated by Kubernetes. And authorization too. Helm rights are defined as Kubernetes rights (via RBAC), and cluster administrators can restrict Helm rights at any required level of detail.

Releases, ReleaseVersions, and State Storage


In the absence of Tiller, to maintain the state of various releases within the cluster, we need a new way for all clients to interact (on release management).

To do this, we introduced two new entries:

  1. Release- for a specific installation of a particular chart. If we do helm install my-wordpress stable/wordpress, a release will be created with the name my-wordpressand maintained throughout the life of this WordPress installation.
  2. ReleaseVersion- each time you update the Helm chart, you need to consider what has changed and whether the change was successful. ReleaseVersiontied to a release and stores only records with information about updating, rolling back and deleting. When we execute helm upgrade my-wordpress stable/wordpress, the original object Releasewill remain the same, but a child object will appear ReleaseVersionwith information about the update operation.

Releasesand ReleaseVersionswill be stored in the same namespace as the chart objects.

With these features, Helm user teams will be able to track the records of Helm installations in the cluster without the need for Tiller.

But wait, that's not all!


In this article I tried to talk about some of the major changes in Helm 3. However, this list is not at all complete. The Helm 3 plan also includes other changes, such as improvements in the chart format, performance improvements for chart repositories, and a new event system that chart developers can use. We're also making Eric Raymond called code archeology by cleaning out the code base and updating components that have lost relevance in the last three years.

Note perev. : Paradox, but Helm 2 package manager on successful execution installor upgrade, i.e. having release in a state successdoes not guarantee that application resources are successfully rolled out (for example, there are no errors likeImagePullError) Perhaps the new event model will allow you to add additional hooks for resources and better control the rollout process - we will find out soon.

With the accession of Helm to CNCF, not only Helm 3, but also the Chart Museum , the wonderful Chart Testing utility , the official chart repository and other projects under the auspices of Helm at CNCF inspire us. We are confident that good package management for Kubernetes is just as important for the cloud native ecosystem as good package managers for Linux are.

PS from the translator


Read also in our blog:


Also popular now: