Skip to content

Helm Chart Support Design Doc#3099

Closed
alpeb wants to merge 8 commits intomasterfrom
alpeb/rfc-helm
Closed

Helm Chart Support Design Doc#3099
alpeb wants to merge 8 commits intomasterfrom
alpeb/rfc-helm

Conversation

@alpeb
Copy link
Member

@alpeb alpeb commented Jul 17, 2019

Note: Helm support for Linkerd 2 is now being tracked under the project https://github.com/orgs/linkerd/projects/13

Project kick-off presentation:Helm Support.pdf

The current CLI install process uses Helm libraries under the hood, but just as a template library. There is currently a Helm chart under the charts directory that allows installing the control plane, but uninjected, and there's no values.yaml file provided.

The intention here is to provide a new chart with an injected control plane so that users can install linkerd through a simple helm install incubator/linkerd2 command.

Helm charts can't rely on go code besides the functions provided by the go template library, the Sprig library, and a few extra functions provided by Helm itself. This implies a few compromises:

  • We can't have the same level of validation that linkerd install currently provides. Some of the basic checks can be performed and have helm install fail using Sprig's fail. The other checks can be performed with a new command linkerd check --chart values.yaml.
  • We can't validate the install options provided in values.yaml. Instead, a new set of linkerd check checks could help catching invalid options, post-install.
  • We should provide a comprehensive values.yaml file that would contain the most common settings, but heavily annotated to instruct users about alternate settings for advanced scenarios.
  • Other alternative values.yaml files should be available, for different configuration scenarios like HA for example.
  • Helm's crypto functions only allow us to use RSA certs/keys. We can move the cert/keys from the webhook configs (proxy-injector and sp-validator) to use RSA. As for the trust root for identity, we decided the user should provide their own in values.yaml. The docs should have instructions on how to generate that cert/key.

These compromises entail a less straightforward experience than what linkerd install provides, so the Helm installation alternative should be considered an "advanced" feature.

New alternative install workflow through Helm

values.yml validation

linkerd check --chart values.yaml

This is an optional pre-install check that validates the options in the provided values.yaml file. Some of these validations are also performed when helm install runs, but given Helm's limitations some others can only be done through go with this new check.

Installing the chart

helm install incubator/linkerd2

# or, for users with cluster-level privileges
helm install incubator/linkerd2 --set stage=config
# followed by, for users with just namespace-level privileges
helm upgrade incubator/linkerd2 --set stage=control-plane

That would install linkerd using the most common settings. The NOTES.txt file (rendered and shown when this command completes) could provide the follow instructions/warnings:

  • Instructions on how to, optionally, install the linkerd CLI
  • Instructions on running linkerd check to verify everything is ok
  • Instructions on how to change the most basic settings
  • Instructions on how to get ahold of values.yaml containing all the possible settings?

New Chart Layout

This will replace the current single chart under the charts directory.

charts
├── linkerd2
│   ├── charts
│   │   └── partials -> ../../partials
│   ├── Chart.yaml
│   ├── templates
│   │   ├── config.yaml
│   │   ├── controller-rbac.yaml
│   │   ├── controller.yaml
│   │   ├── grafana-rbac.yaml
│   │   ├── grafana.yaml
│   │   ├── identity-rbac.yaml
│   │   ├── identity.yaml
│   │   ├── namespace.yaml
│   │   ├── NOTES.txt
│   │   ├── prometheus-rbac.yaml
│   │   ├── prometheus.yaml
│   │   ├── proxy_injector-rbac.yaml
│   │   ├── proxy_injector.yaml
│   │   ├── psp.yaml
│   │   ├── _resources.yaml
│   │   ├── serviceprofile-crd.yaml
│   │   ├── sp_validator-rbac.yaml
│   │   ├── sp_validator.yaml
│   │   ├── tap-rbac.yaml
│   │   ├── tap.yaml
│   │   ├── trafficsplit-crd.yaml
│   │   ├── web-rbac.yaml
│   │   └── web.yaml
│   └── values.yaml
├── patch
│   ├── charts
│   │   └── partials -> ../../partials
│   ├── Chart.yaml
│   ├── templates
│   │   └── patch.json
│   └── values.yaml
└── partials
    ├── charts
    ├── Chart.yaml
    ├── templates
    │   ├── _debug.yaml
    │   ├── _metadata.yaml
    │   ├── _proxy.yaml
    │   ├── _proxy-init.yaml
    │   └── _volumes.yaml
    └── values.yaml

Current mechanisms and changes

The user experience for the current way of doing things remains the same. There will be some changes under the hood though.

Automated proxy injection

Currently the proxy-injector webhook is invoked for any pod that gets created. If the pod's namespace or the pod itself contains a linkerd.io/inject: enabled annotation then the webhook relies on the library pkg/inject/inject.go to programatically generate a proxy container (and a proxy-init container if necessary) using go-client structs. Those structs are transformed into JSON-patch format which is returned to Kubernetes, which will do the actual injection of the container into the pod.

The functionality and contract for pkg/inject/inject.go will remain the same but with a different mechanism underneath. The JSON-patch will be generated through the new patch chart which itself depends on the _proxy.yaml template under the charts/partials chart. _proxy.yaml will be the sole place containing the structure of the proxy container and it will replace the go-client structs currently used. This partial will also be used when doing helm install (see below) thus avoiding having more than one place for the proxy structure source-of-truth.

linkerd inject

Currently this only adds the linkerd.io/inject: enabled annotation to the pod template, and when Kuberenetes creates the pod it invokes the webhook as just explained. This remains as-is.

linkerd inject --manual

Currently this calls pkg/inject/inject.go to perform the injection, just as the webhook does. So the changes detailed above also affect this execution path, but the experience remains the same.

linkerd install

Currently this relies on the Helm go library and the charts under the charts directory to generate the control plane resources. The options passed as CLI arguments are converted into template values that are passed to the Helm template engine to be replaced in the placeholders inside those charts. Then, the generated yaml is fed into the pkg/inject/inject.go library to inject the proxies, just as linkerd inject --manual would do.

Here we will be replacing the current chart with a new one under charts/linkerd2 to create the control plane resources, and that chart depends on various templates under the charts/partials charts, in particular the _proxy.yaml template for inserting the yaml for the proxy into all the control plane resources. Note that charts/linkerd2 doesn't depend on charts/patch because the latter's sole purpose is to generate a JSON-patch, which Helm can't interpret.

The user experience for linkerd install remains the same as well.

New alternative helm install mechanism

The mechanism is the same as linkerd install just explained; the main chart will be charts/linkerd2 which depends on charts/partials for, among other things, the proxy insertion. The main difference will be that the chart values will come from values.yml (or provided by the user through --set on Helm's CLI).

Tasks

Not necessarily for the first iteration of this project

Definitely for later

  • Consider install-cni. Could be as a separate stage (--set stage=cni).

To-do

  • Validate how all this plays with Helm v3

(Moved from the wiki to a PR so it can be commented more easily)

The current CLI install process uses Helm libraries under the hood, but just as a template library. There is currently a Helm chart under the `charts` directory that allows installing the control plane, but uninjected, and there's no `values.yaml` file provided.

The intention here is to provide a new chart with an injected control plane so that users can install linkerd through a simple `helm install incubator/linkerd2` command.

Helm charts can't rely on go code besides the functions provided by the go template library, the Sprig library, and a few extra functions provided by Helm itself. This implies a few compromises:

- We can't validate the install options provided in `values.yaml`. Instead, a new set of `linkerd check` checks could help catching invalid options, post-install.
- We should provide a comprehensive `values.yaml` file that would contain the most common settings, but heavily annotated to instruct users about alternate settings for advanced scenarios.
- Helm's crypto functions only allow us to use RSA certs/keys. We can move the cert/keys from the webhook configs (`proxy-injector` and `sp-validator`) to use RSA. As for the trust root for identity, we decided the user should provide their own in `values.yaml`. The docs should have instructions on how to generate that cert/key.

These compromises entail a less straightforward experience than what `linkerd install` provides, so the Helm installation alternative should be considered an "advanced" feature.

New alternative install workflow through Helm
-----------------------------------------------
```
helm install incubator/linkerd2
```
That would install linkerd using the most common settings. The `NOTES.txt` file (rendered and shown when this command completes) could provide the follow instructions/warnings:
- Warn that the identity trust root should have been provided, and show instructions on how to generate it.
- Instructions on how to, optionally, install the linkerd CLI
- Instructions on running `linkerd check` to verify everything is ok
- Instructions on how to change the most basic settings
- Instructions on how to get ahold of `values.yaml` containing all the possible settings?

This will replace the current single chart under the `charts` directory.
```
charts
├── control-plane
│   ├── charts
│   │   └── partials -> ../../partials
│   ├── Chart.yaml
│   ├── templates
│   │   ├── config.yaml
│   │   ├── controller-rbac.yaml
│   │   ├── controller.yaml
│   │   ├── grafana-rbac.yaml
│   │   ├── grafana.yaml
│   │   ├── identity-rbac.yaml
│   │   ├── identity.yaml
│   │   ├── namespace.yaml
│   │   ├── NOTES.txt
│   │   ├── prometheus-rbac.yaml
│   │   ├── prometheus.yaml
│   │   ├── proxy_injector-rbac.yaml
│   │   ├── proxy_injector.yaml
│   │   ├── psp.yaml
│   │   ├── _resources.yaml
│   │   ├── serviceprofile-crd.yaml
│   │   ├── sp_validator-rbac.yaml
│   │   ├── sp_validator.yaml
│   │   ├── tap-rbac.yaml
│   │   ├── tap.yaml
│   │   ├── trafficsplit-crd.yaml
│   │   ├── web-rbac.yaml
│   │   └── web.yaml
│   └── values.yaml
├── data-plane
│   ├── charts
│   │   └── partials -> ../../partials
│   ├── Chart.yaml
│   ├── templates
│   │   └── patch.json
│   └── values.yaml
└── partials
    ├── charts
    ├── Chart.yaml
    ├── templates
    │   ├── _debug.yaml
    │   ├── _metadata.yaml
    │   ├── _proxy.yaml
    │   ├── _proxy-init.yaml
    │   └── _volumes.yaml
    └── values.yaml
```

Current mechanisms and changes
-------------------------------

The user experience for the current way of doing things remains the same. There will be some changes under the hood though.

Currently the `proxy-injector` webhook is invoked for any pod that gets created. If the pod's namespace or the pod itself contains a `linkerd.io/inject: enabled` annotation then the webhook relies on the library `pkg/inject/inject.go` to programatically generate a proxy container (and a proxy-init container if necessary) using go-client structs. Those structs are transformed into JSON-patch format which is returned to Kubernetes, which will do the actual injection of the container into the pod.

The functionality and contract for `pkg/inject/inject.go` will remain the same but with a different mechanism underneath. The JSON-patch will be generated through the new `data-plane` chart which itself depends on the `_proxy.yaml` template under the `charts/partials` chart. `_proxy.yaml` will be the sole place containing the structure of the proxy container and it will replace the go-client structs currently used. This partial will also be used when doing `helm install` (see below) thus avoiding having more than one place for the proxy structure source-of-truth.

Currently this only adds the `linkerd.io/inject: enabled` annotation to the pod template, and when Kuberenetes creates the pod it invokes the webhook as just explained. This remains as-is.

Currently this calls `pkg/inject/inject.go` to perform the injection, just as the webhook does. So the changes detailed above also affect this execution path, but the experience remains the same.

Currently this relies on the Helm go library and the charts under the `charts` directory to generate the control plane resources. The options passed as CLI arguments are converted into template values that are passed to the Helm template engine to be replaced in the placeholders inside those charts. Then, the generated yaml is fed into the `pkg/inject/inject.go` library to inject the proxies, just as `linkerd inject --manual` would do.

Here we will be replacing the current chart with a new one under `charts/control-plane` to create the control plane resources, and that chart depends on various templates under the `charts/partials` charts, in particular the `_proxy.yaml` template for inserting the yaml for the proxy into all the control plane resources. Note that `charts/control-plane` doesn't depend on `charts/data-plane` because the latter's sole purpose is to generate a JSON-patch, which Helm can't interpret.

The user experience for `linkerd install` remains the same as well.

New alternative `helm install` mechanism
---------------------------------------------
The mechanism is the same as `linkerd install` just explained; the main chart will be `charts/control-plane` which depends on `charts/partials` for, among other things, the proxy insertion. The main difference will be that the chart values will come from `values.yml` (or provided by the user through `--set` on Helm's CLI).

Published charts
----------------
The `control-plane` chart should be the main chart, published under `https://github.com/helm/charts/incubator/linkerd2`, copying the `partials` chart to `control-plane/charts` prior to publication. I'm not sure if there's a better way of doing this, given `partials` isn't suitable as as stand-alone public chart.

`data-plane` can remain unpublished.

Tasks
---------
- Refactor `injectPodSpec()` and `injectProxyInit()` in `pkg/inject/inject.go` that currently generates a JSON patch, but have it use the `data-plane` chart instead of the hard-coded go-client structs.
- Refactor the TLS libraries relied upon by the `proxy-injector` and `sp-validor` webhooks to have them work with RSA as well (they currently only deal with EC).
- Refactor the  `proxy-injector` and `sp-validator` charts so that they generate the certs/keys with Helm's `genSelfSignedCert()`.
- Create `values.yaml` with all the default values, by hand (later, we can have this be automated based off of protobuf for the config part). The trust root for identity is expected to be provided by the user in this file.
- Have a well annotated main `values.yaml` file with the most common settings by default.
- Create a detailed `NOTES.txt` file with the instructions/warnings detailed above.
- Create a new website doc for Helm. A section should have a tutorial for generating the cert/key for identity.
- Enhance `linkerd check` with new checks that cover the options validations currently done in `linkerd install` that can't be performed with `helm install` (Maybe be leave this for later?)

To-do
------
- Validate how all this plays with Helm v3

Signed-off-by: Alejandro Pedraza <[email protected]>
@alpeb alpeb requested review from grampelberg and ihcsim July 17, 2019 20:25
@alpeb alpeb self-assigned this Jul 17, 2019
@alpeb alpeb changed the title Helm Chart Support Helm Chart Support Design Doc Jul 17, 2019

Helm charts can't rely on go code besides the functions provided by the go template library, the Sprig library, and a few extra functions provided by Helm itself. This implies a few compromises:

- We can't validate the install options provided in `values.yaml`. Instead, a new set of `linkerd check` checks could help catching invalid options, post-install.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this idea and hadn’t thought of it myself! We could have a CLI command that consumes a values.yaml file and runs checks on it linkerd check chart or something like that. It could be run pre-install and maybe even highlight changes between versions (as I’m sure we’ll be mutating the values pretty extensively).

Copy link
Contributor

@ihcsim ihcsim Jul 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this check run through the existing install validation code; i.e. validate the values in values.yaml as if they are CLI options (so a bit like linkerd install --values values.yaml)? I'd imagine this plus some additional validation code, since it'd it won't be a 1:1 mapping between the values and the CLI options.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that sounds like it; the more code we can share here, the better 👍

```
charts
├── control-plane
│ ├── charts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we’ll want helm to manage charts and do a requirements.yaml here, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought requirements.yaml is only needed if the dependency (in this case, charts/partials) is published?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helm assumes that it manages the charts directory and it is only populated on helm package. We could hack it with some symlinks, and that might be the best solution, I just wanted to call out the one true way (tm).

You can have local paths in requirements.yaml as well, fwiw. No need to publish.

Copy link
Member Author

@alpeb alpeb Jul 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if any of this makes sense:

When pushing the control-plane chart to the helm charts repo (https://github.com/helm/charts), some CI job there will package it to publish it to https://kubernetes-charts.storage.googleapis.com.
The only charts that job will have access to are the already published ones and the ones under control-plane/charts. For that reason it seems to me having a requirements.yaml file can't work here. OTOH when I test helm package with symlinks, it produces a tgz file that when unpacked will have charts/partials populated. I guess that unpacked directory is what we'd have to push to the helm charts repo.

Also note that helm package complains if the directory name doesn't match the chart name. So I'll have to rename control-plane to linkerd2.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me to package up partials as .tgz file inside control-plane/charts, since we aren't publishing partials.

So I'll have to rename control-plane to linkerd2.

Makes sense. The data-plane folder name may not even make sense; maybe just patch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alpeb in the past I've written a helm package script that just copies the directory to tmp and renames it. I don't mind calling it linkerd2.

This raises a good point though, if we want this to be helm install stable/linkerd2, we won't be able to run helm package or push the compiled release anywhere. It will end up being in raw form in the stable repo (https://github.com/helm/charts/tree/master/stable/prometheus-adapter). This would require publishing both the partials and control-plane charts.

My proposal - don't worry about it, do what we need to do internally and write a publishing script for stable/linkerd2 that copies the partials into the control-plane chart on "publish".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just pushed a change with the charts renaming and a new task for the chart publication.
I've left the symlinks under charts. @grampelberg Let me know if you still deem necessary to have a requirements.yml file instead.

Signed-off-by: Alejandro Pedraza <[email protected]>
@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 18, 2019

Signed-off-by: Alejandro Pedraza <[email protected]>

Helm charts can't rely on go code besides the functions provided by the go template library, the Sprig library, and a few extra functions provided by Helm itself. This implies a few compromises:

- We can't validate the install options provided in `values.yaml`. Instead, a new set of `linkerd check` checks could help catching invalid options, post-install.
Copy link
Contributor

@ihcsim ihcsim Jul 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this check run through the existing install validation code; i.e. validate the values in values.yaml as if they are CLI options (so a bit like linkerd install --values values.yaml)? I'd imagine this plus some additional validation code, since it'd it won't be a 1:1 mapping between the values and the CLI options.

@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 18, 2019

Signed-off-by: Alejandro Pedraza <[email protected]>
@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 18, 2019

@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 19, 2019

@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 19, 2019

@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 19, 2019

@alpeb alpeb mentioned this pull request Jul 24, 2019
@l5d-bot
Copy link
Collaborator

l5d-bot commented Jul 24, 2019

@alpeb
Copy link
Member Author

alpeb commented Oct 3, 2019

This project has been completed. A few notes:

The Helm repos at https://github.com/helm/charts are getting deprecated, and our PR for inclusion in the incubator repo never got merged helm/charts#16472
So we created our own repos at https://helm.linkerd.io/stable and https://helm.linkerd.io/edge and we submitted them to the Helm hub:
https://hub.helm.sh/charts/linkerd2/linkerd2
https://hub.helm.sh/charts/linkerd2-edge/linkerd2
Those packages are automatically generated and uploaded during CI when an edge or stable tag succeeds.
The docs have been updated with instructions to use those Helm packages.

Note that additionally to the standard default values in values.yaml we're also providing values for HA in value-ha.yaml. The website doc also explains how to use it.

Things not implemented yet:

@alpeb alpeb closed this Oct 3, 2019
@olix0r olix0r deleted the alpeb/rfc-helm branch October 13, 2020 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants