Closed gcapizzi closed 2 years ago
I've just done an experiment with @danail-branekov regarding templating config maps using kustomize.
To setup the files:
mkdir -p base overlay
cat <<EOF >base/kustomization.yaml
configMapGenerator:
- name: the-map
literals:
- altGreeting="Have a pineapple!"
- enableRisky="true"
- something-common="ok"
EOF
cat <<EOF >overlay/config-values.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Have a banana!"
enableRisky: "false"
EOF
cat <<EOF >overlay/kustomization.yaml
resources:
- ../base
patchesStrategicMerge:
- config-values.yaml
EOF
To see the base configmap:
kubectl kustomize base
To see the overlay one:
kubectl kustomize overlay
Notice that the overlay can replace individual values from the base config map, but will preserve unmodified values. One downside is that we can't do nested structures this way. But this approach seems better for tweaking values than our existing file-based config map approach.
Here are some general notes and observation that me and @kieron-dev made:
There is duplication between configmap values and kustomization yamls
install-dependencies mixes korifi dependency configuration with dependency installation
ConfigMap values can be overridden simply in our existing code by taking advantage of file load ordering
Kelsey Hightower env var suggestion (https://medium.com/@kelseyhightower/12-fractured-apps-1080c73d481c)
HTTPProxy seems a bit of a problem (controllers/config/base/ingress.yaml)
Overall, it seems that using plain kustomize to configure our redistributable seems awkward. However, moving away from kustomize is awkward as well as kubebuilder
generates kustomize overlays when introducing new stuff (e.g. new CRD or new webhooks). Therefore, from developers' point of view, having kustomize around is useful.
Branch explore/1509-ytt-kustomize
experiments combining ytt
and kustomize
.
Here are the goals of the experiment:
kustomize
around as it is useful for developmentytt
overlay that replaces values in the kustomize
files from provided property fileytt
followed by running kustomize
, for exampleytt -f api/config/base -f api/config/ytt -f api/config/values/kind-local-registry.yml --output-files $(TMPDIR)
kubectl kustomize build $(TMPDIR) | kubectl apply -f -
Therefore, in a release tarball we could just ship the kustomize
that have proper image SHA filled by the CI and the ytt
overlay. Users are not expected to mess with those files, they are just expected to come up with a properties one.
The approach above has the following benefits
ytt
with your own properties file, then run kustomize
Migrating to this way of doing things requires:
install-depencies.sh
to just installing dependencies without performing kpack configuration or user setupWith regards to automating the configuration bits, there might be an issue that kustomize is putting prefixes on all the objects it produces. That might be a problem for cases where we would need stable predictable names...
After gaining confidence that the kustomize + ytt approach described above would work for us we set out to explore if we can achieve similar dev and user experience by using just kustomize. The benefits of this approach would be the following:
kapp
for better state managementIt turned out that it is actually possible to achieve the goals above. The key things we will need to chage are:
Here is a WIP branch that demonstrated how this works: https://github.com/cloudfoundry/korifi/tree/explore/1509-kustomize
Users only need to edit the api/config/overlays/kind-local-registry/apiconfig/korifi_api_config.env file. The rest of the deployment procedure is unchanged.
This approach has the additional benefit of removing all overlays that we have had up to now with all the duplications in them and replaceing them with just one overlay that takes a simple key-value file.
We could even leverage the fact that kustomize can refer to bases from the internet and release a single overlay that refers to a release tag in github.
Today we carried on with the kustomize-only experiment. Here is what we tried:
Experimented with the potential release format
- korifi
- api
- config
- api.env
- kustomization.yml
- controllers
- config
- controllers.env
- kustomization.yml
- ...
In the kustomization file we are including the base from github via reference and have set the released image sha using kustomize edit set image
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/cloudfoundry/korifi/api/config/base/?ref=14f3d14d5aa201500622e03e871f14d815d642c2
configMapGenerator:
- behavior: merge
envs:
- apiconfig/korifi_api_config.env
name: config
images:
- name: cloudfoundry/korifi-api
newName: cloudfoundry/korifi-api
newTag: dev-0.3.0-bd46d89cb53bafc5b96d9ab5a9754f9172b1dac0
replacements:
- source:
fieldPath: data.externalFQDN
kind: ConfigMap
name: config
version: v1
targets:
- fieldPaths:
- spec.template.spec.containers.[name=korifi-api].env.[name=EXTERNAL_FQDN].value
select:
kind: Deployment
name: deployment
version: v1
- fieldPaths:
- spec.virtualhost.fqdn
select:
group: projectcontour.io
kind: HTTPProxy
name: proxy
version: v1
...
In this setup each component is installed like this:
kustomize build api | kubectl apply -f -
The project structure created by kubebuilder uses kustomize to optionally configure various resources. This is problematic if we wish to use a templating solution around the korifi yaml, such as helm or ytt templates. So we are considering the impact of moving from kubebuilder generated yaml requiring kustomize, to plain yaml, potentially with templating code.
We picked the statefulset-runner as a representative component to experiment on.
Although we subsequently realised this does not define its own resources!
We generated the full distribution yaml with kubectl kustomize config/default
inside statefulset-runner. Then we saved each yaml object in the output to a
separate file in the korifi/dist/templates/statefulset-runner/
directory.
We can continue to generate RBAC cluster role definitions by modifying the controller-gen command in the makefile, e.g.
./bin/controller-gen \
rbac:roleName=korifi-statefulset-runner-appworkload-manager-role \
output:rbac:artifacts:config=../dist/templates/statefulset-runner paths="./..."
This uses the fully prefixed name for the cluster-role, and switches its output
directory. The output file will be called role.yaml
CRDs can be generated as before. Again we just need to switch the output directory, similar to above.
There is a problem here. We use object selectors to make sure we only modify
korifi statefulset pods. However the +kubebuilder:webhook
annotation does not
allow us to specific object selector rules. Therefore this is implemented with
a kustomize patch, since each time the webhook manifest is regenerated, it
would overwrite the object selector changes.
Therefore we propose to stop the auto-generation of the webhook configurations.
It seems like helm is up to the task of the amount of templating we require. It can insert namespaces into the middle of strings, which is required for certificate DNS entries.
It will also suit the current configmap configuration approach, avoid the need to switch to env vars.
ytt templates would also work, though we note the ubiquity of helm and its ease of templating for this use case.
Therefore we propose to stop the auto-generation of the webhook configurations.
Could ytt
do this still though? I feel a ytt overlay provides the same amount of capability here as the kustomize patch, right? That said, I don't think it's a big deal to move away from auto-generation of webhook config. We could even just generate once for new ones and then make the object selector change ourselves.
@tcdowney Yes, I think the pair tried to do this with pure interpolation and without overlays, but we could use overlays for this special case. This is an extra point for ytt
vs Helm which doesn't have overlays.
Kustomize makes the config directory rather hard to understand. If you look at a yaml, you also need to check all the kustomization.yaml
s in the whole directory structure to see if it gets modified elsewhere. I don't think that is great. And keeping that sort of practice by overlaying object selectors onto the webhook configuration also makes the configuration less explicit and more magic.
When we extract the kustomize output into plain yaml files, it's surprising how simple the resultant configuration is (although I'm about to explore converting the controllers component, so it maybe that opinion is premature). The goal of this part of the explore is to have a set of simple yamls containing minimal templating (with helm, or inline ytt - not overlayed ytt), and see how that affects our current practices for local development, testing and release.
There should be a branch available to look at later, and we'll update the explore doc outlining the pros and cons of this approach compared with the pure kustomize approach we've already described.
Yeah I totally agree that templating is a lot easier to understand and to use. It feels like Kustomize was designed so that users could customise (duh) YAML from third parties without having to fork it (their homepage emphasises the "use of off-the-shelf applications", "without forking"), but our use case is different.
That said, it might be worth having just one overlay if that still allows us to autogenerate webhook configurations - it really depends on how complex those are to write from scratch and keep up-to-date.
Background
Currently, we distribute Korifi as a set of YAML files that the users need to manually edit before they can be applied. This is inconvenient and error-prone. Our hacking flow is also not great.
Instead, we'd like to distribute a package that can be easily customised via simple input values before being applied. This should simplify our automation scripts, our CI, and our installation instructions. Ideally it should be able to use the same flow for installing a local copy of Korifi just by running
kbld
locally.Here's an overview of the tools I'm aware of that could solve our problem:
ConfigMap
s. On the other hand, it's is built intokubectl
, making it automatically available to every user. It's worth exploring wether it could be used to provide a user-friendly experience to users installing Korifi.ytt
), installation (kapp
), image building (kbld
), dependency management (vendir
). It's quite powerful and it would easily cover our use case.Acceptance Criteria
An assessment of how our distribution story might look like using each one of the tools above, including:
Dev Notes
kbld
for this. Once our YAMLs have been generated, they get passed tokbld
which will:kubectl apply
able YAML file.kbld
, while YAML comment-based syntax (e.g.ytt
) will get stripped.kbld
on theValues.yml
file, which is standard YAML.ytt
, I think the most elegant solution would be to use lock files, which could then be fed toytt
as data values file.