Add flag --enable-helm

This commit is contained in:
monopole
2021-04-02 15:32:01 -07:00
parent aeba50488b
commit 240cda089a
14 changed files with 1081 additions and 740 deletions

View File

@@ -1,49 +1,63 @@
# kustomization of a helm chart
[`helm`]: https://helm.sh
[last mile]: https://testingclouds.wordpress.com/2018/07/20/844/
[stable chart]: https://github.com/helm/charts/tree/master/stable
[helm charts]: https://github.com/helm/charts
[_minecraft_]: https://github.com/helm/charts/tree/master/stable/minecraft
[artifact hub]: https://artifacthub.io
[_minecraft_]: https://artifacthub.io/packages/helm/minecraft-server-charts/minecraft
[plugin]: ../docs/plugins
[built]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization
[Helm charts] aren't natively read by kustomize, but
kustomize has a builtin HelmChartInflationGenerator that allows one to
access helm charts.
Kustomize is [built] from _generators_ and
_transformers_; the former make kubernetes YAML, the
latter transform said YAML.
One pattern combining kustomize and helm is
the [last mile] modification, where
one uses an inflated chart as a base, then
modifies it on the way to the cluster using
kustomize.
Kustomize, via the `helmCharts` field, has the ability to
use the [`helm`] command line program in a subprocess to
inflate a helm chart, generating YAML as part of (or as the
entirety of) a kustomize base.
The example arbitrarily uses [_minecraft_] in [stable chart] repository,
but should work for any chart in any valid chart repository.
This YAML can then be modified either in the base directly
(transformers always run _after_ generators), or via
a kustomize overlay.
Either approach can be viewed as [last mile] modification
of the chart output before applying it to a cluster.
The example below arbitrarily uses the
[_minecraft_] chart pulled from the [artifact hub]
chart repository.
## Preparation
This example defines the `helm` command as
<!-- @defineHelmCommand @testHelm -->
```
helmCommand=~/go/bin/helmV3
```
This value is needed for testing this example in CI/CD.
A user doesn't need this if their binary is called
`helm` and is on their shell's `PATH`.
The following example assumes you have `helm`
on your `$PATH`. The plugin only supports helm V3 or later.
Make a place to work:
<!-- @makeWorkplace @helmtest -->
<!-- @makeWorkplace @testHelm -->
```
DEMO_HOME=$(mktemp -d)
mkdir -p $DEMO_HOME/base
mkdir -p $DEMO_HOME/dev
mkdir -p $DEMO_HOME/prod
mkdir -p $DEMO_HOME/base $DEMO_HOME/dev $DEMO_HOME/prod
```
## Use a remote chart
## Define some variants
Define a kustomization representing your _development_
variant (aka environment).
variant.
This could involve any number of kustomizations (see
other examples), but in this case just add the name
prefix `dev-` to all resources:
<!-- @writeKustDev @helmtest -->
prefix '`dev-`' to all resources:
<!-- @writeKustDev @testHelm -->
```
cat <<'EOF' >$DEMO_HOME/dev/kustomization.yaml
namePrefix: dev-
@@ -53,10 +67,9 @@ EOF
```
Likewise define a _production_ variant, with a name
prefix `prod-`:
<!-- @writeKustProd @helmtest -->
prefix '`prod-`':
<!-- @writeKustProd @testHelm -->
```
cat <<'EOF' >$DEMO_HOME/prod/kustomization.yaml
namePrefix: prod-
@@ -67,45 +80,34 @@ EOF
These two variants refer to a common base.
Define this base:
<!-- @writeKustDev @helmtest -->
Define this base the usual way by creating a
`kustomization` file:
<!-- @writeKustBase @testHelm -->
```
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
generators:
- chartInflator.yaml
helmCharts:
- name: minecraft
valuesInline:
minecraftServer:
eula: true
difficulty: hard
rcon:
enabled: true
releaseName: moria
version: 3.1.3
repo: https://itzg.github.io/minecraft-server-charts
EOF
```
The base refers to a generator configuration file
called `chartInflator.yaml`.
The only thing in this particular file is a `helmCharts`
field, specifying a single chart.
This file lets one specify the name of a [stable chart],
and other things like a path to a values file, defaulting
to the `values.yaml` that comes with the chart.
Create the config file `chartInflator.yaml`, specifying
the arbitrarily chosen chart name _minecraft_ and the repository
url that will be used to find the chart:
<!-- @writeGeneratorConfig @helmtest -->
```
cat <<'EOF' >$DEMO_HOME/base/chartInflator.yaml
apiVersion: builtin
kind: HelmChartInflationGenerator
metadata:
name: notImportantHere
chartName: minecraft
chartRepoUrl: https://kubernetes-charts.storage.googleapis.com
EOF
```
The `valuesInline` field overrides some native chart values.
Check the directory layout:
<!-- @tree -->
```
tree $DEMO_HOME
```
@@ -115,155 +117,249 @@ Expect something like:
> ```
> /tmp/whatever
> ├── base
>    ├── chartInflator.yaml
> │   └── kustomization.yaml
> └── kustomization.yaml
> ├── dev
>    └── kustomization.yaml
> └── kustomization.yaml
> └── prod
> └── kustomization.yaml
> ```
Define a helper function to run kustomize with the
correct environment and flags for plugins:
### Helm related flags
<!-- @defineKustomizeIt @helmtest -->
Attempt to build the `base`:
<!-- @checkFailure @testHelm -->
```
cmd="kustomize build --helm-command $helmCommand $DEMO_HOME/base"
if ($cmd); then
echo "Build should fail!" && false # Force test to fail.
else
echo "Build failed because no --enable-helm flag (desired outcome)."
fi
```
This `build` fails and complains about a missing
`--enable-helm` flag.
The flag `--enable-helm` exists to have the user
acknowledge that kustomize is running an external program as
part of the `build` step. It's like the
`--enable-plugins` flag, but with a helm focus.
The flag `--helm-command` has a default value (`helm` of
course) so it's not suitable as an enablement flag. A user
with `helm` on their `PATH` need not awkwardly specify
`'--helm-command helm'`.
Given the above, define a helper function to run `kustomize` with the
flags required for `helm` use in this demo:
<!-- @defineKustomizeIt @testHelm -->
```
function kustomizeIt {
XDG_CONFIG_HOME=$DEMO_HOME \
kustomize build --enable_alpha_plugins \
kustomize build \
--enable-helm \
--helm-command $helmCommand \
$DEMO_HOME/$1
}
```
### Build the base and the variants
Finally, build the `prod` variant. Notice that all
resource names now have the `prod-` prefix:
<!-- @doProd @helmtest -->
```
clear
kustomizeIt prod
```
Compare `dev` to `prod`:
<!-- @doCompare -->
```
diff <(kustomizeIt dev) <(kustomizeIt prod) | more
```
To see the unmodified but inflated chart, run kustomize
on the base. Every invocation here is re-downloading
and re-inflating the chart.
<!-- @showBase @helmtest -->
Now build the `base`:
<!-- @showBase @testHelm -->
```
kustomizeIt base
```
## Use a local chart
This works, and you see an inflated chart complete
with a `Secret`, `Service`, `Deployment`, etc.
The example above fetches a new copy of the chart
and render it with each kustomize
build, because a local chart home isn't specified
in the configuration.
To suppress fetching, specify a _chart home_
explicitly, and just make sure the chart is already
there.
To demo this so that it won't interfere with your
existing helm environment, do this:
**This demo uses Helm V3**
<!-- @helmInit @helmtest -->
```
helmHome=$DEMO_HOME/dothelm
chartHome=$DEMO_HOME/base/charts
repoUrl=https://kubernetes-charts.storage.googleapis.com
function doHelm {
helm --home $helmHome $@
}
```
Now download a chart; again use _minecraft_
(but you could use anything):
<!-- @fetchChart @helmtest -->
```
doHelm pull --untar \
--untardir $chartHome \
--repo $repoUrl \
minecraft
```
The tree has more stuff now; helm config data
and a complete copy of the chart:
As a side effect of this build, kustomize pulled the chart
and placed it in the `charts` subdirectory of the base.
Take a look:
<!-- @tree -->
```
tree $DEMO_HOME
```
Add a `chartHome` field to the generator config file so
that it knows where to find the local chart:
If the chart had already been there, kustomize would
not have tried to pull it.
<!-- @modifyGenConfig @helmtest -->
To change the location of the charts, use this
in your kustomization file:
> ```
> helmGlobals:
> chartHome: charts
> ```
Change `charts` as desired, but it's best to keep it
in (or below) the same directory as the `kustomization.yaml` file.
If it's outside the kustomization root, the `build` command will
fail unless given the flag `'--load-restrictor=none'` to
disable file loading restrictions.
Now build the two variants `dev` and `prod`
and compare their differences:
<!-- @doCompare -->
```
echo "chartHome: $chartHome" >>$DEMO_HOME/base/chartInflator.yaml
diff <(kustomizeIt dev) <(kustomizeIt prod) | more
```
Change the values file, to show that the results
generated below are from the _locally_ stored chart:
This shows so-called _last mile hydration_ of two variants
made from a common base that happens to be generated from a
helm chart.
<!-- @valueChange @helmtest -->
## How does the pull work?
The command kustomize used to download the chart
is something like
> ```
> $helmCommand pull \
> --untar \
> --untardir $DEMO_HOME/base/charts \
> --repo https://itzg.github.io/minecraft-server-charts \
> --version 3.1.3 \
> minecraft
> ```
The first use of kustomize above (when the `base` was
expanded) fetched the chart and placed it in the `charts`
directory next to the `kustomization.yaml` file.
This chart was reused, _not_ re-fetched, with the variant
expansions `prod` and `dev`.
If a chart exists, kustomize will not overwrite it (so to
suppress a pull, simply assure the chart is already in your
kustomization root). kustomize won't check dates or version
numbers or do anything that smells like cache management.
> kustomize is a YAML manipulator. It's not a manager
> of a cache of things downloaded from the internet.
## The pull happens once.
To show that the locally stored chart is being re-used, modify
its _values_ file.
First make note of the password encoded in the production
inflation:
<!-- @checkPassword @testHelm -->
```
sed -i 's/CHANGEME!/SOMETHINGELSE/' $chartHome/minecraft/values.yaml
sed -i 's/LoadBalancer/NodePort/' $chartHome/minecraft/values.yaml
test 1 == $(kustomizeIt prod | grep -c "rcon-password: Q0hBTkdFTUUh")
```
Finally, built it
The above command succeeds if the value of the generated
password is as shown (`Q0hBTkdFTUUh`).
<!-- @finalProd @helmtest -->
Now change the password in the local values file:
<!-- @valueChange @testHelm -->
```
kustomizeIt prod
values=$DEMO_HOME/base/charts/minecraft/values.yaml
grep CHANGEME $values
sed -i 's/CHANGEME/SOMETHING_ELSE/' $values
grep SOMETHING_ELSE $values
```
and observe the change from `LoadBalancer` to `NodePort`, and
the change in the encoded password.
Run the build, and confirm that the same `rcon-password`
field in the output has a new value, confirming that the
chart used was a _local_ chart, not a chart freshly
downloaded from the internet:
Clean the directory.
<!-- @showBase @helmtest -->
<!-- @checkPassword2 @testHelm -->
```
test 1 == $(kustomizeIt prod | grep -c "rcon-password: U09NRVRISU5HX0VMU0Uh")
```
Finally, clean up:
<!-- @showBase @testHelm -->
```
rm -r $DEMO_HOME
```
## How to migrate from old plugin to builtin plugin
## Performance
[bash-based helm chart inflator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/chartinflator
[go-based builtin helm chart inflator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/builtin/helmchartinflationgenerator
To recap, the helm-related kustomization fields make
kustomize run
The [bash-based helm chart inflator] is intended as an example of using bash
to write a generator plugin.
> ```
> helm pull ...
> helm template ...
> ```
It proved to be popular for inflating helm charts,
so there's now a [go-based builtin helm chart inflator].
_as a convenience for the user_ to generate YAML from a helm chart.
This newer generator is supported as part of the core code, so anyone using the
old bash-based example plugin would probably benefit from switching to the
newer built-in plugin.
Helm's `pull` command downloads the chart. Helm's `template`
command inflates the chart template, spitting the inflated
template to stdout (where kustomize captures it) rather than
immediately sending it to a cluster as `helm install`
would.
Be advised that at the time of writing, the built-in plugin only supports helm v3.
To improve performance, a user can retain the chart after
the first pull, and commit the chart to their configuration
repository (below the `kustomization.yaml` file that refers
to it). kustomize only tries to pull the chart if it's not
already there.
To further improve performance, a user can inflate the
chart themselves at the command line, e.g.
> ```
> helm template {releaseName} \
> --values {valuesFile} \
> --version {version} \
> --repo {repo} \
> {chartName} > {chartName}.yaml
> ```
then commit the resulting `{chartName}.yaml` file to a git
repo as a configuration base, mentioning that file as a
`resource` in a `kustomization.yaml` file, e.g.
> ```
> resources:
> - minecraft_v3.1.3_Chart.yaml
> ```
The user should choose when or if to refresh their local
copy of the chart's inflation. kustomize would have no
awareness that the YAML was generated by helm, and kustomize
wouldn't run `helm` during the `build`. This is analogous
to `Go` module vendoring.
### But it's not really about performance.
Although the `helm` related fields discussed above are handy
for experimentation and development, it's best to avoid them
in production.
The same argument applies to using _remote_ git URL's in
other kustomization fields. Handy for experimentation,
but ill-advised in production.
It's irresponsible to depend on a remote configuration
that's _not under your control_. Annoying enablement flags
like `'--enable-helm'` are intended to _remind_ one of a
risk, but offer zero protection from risk. Further, they
are useless are reminders, since __annoying things are
immediately scripted away and forgotten__, as was done above
in the `kustomizeIt` shell function.
## Best practice
Don't use remote configuration that you don't control in
production.
Maintain a _local, inflated fork_ of a remote configuration,
and have a human rebase / reinflate that fork from time to
time to capture upstream changes.