Deploying Kubernetes with Ansible
erichreich.com
erichreich.com
Had one a while back where quoted strings containing padded numbers in were used for something. "01" fine ... "05" fine, "06" fine, "07" fine, "08" kaboom. 01-07 were treated as strings and 08 was suddenly being treated as octal, which it's not and exploded.
Try Tanka + Jsonnet to restore some of your sanity, also allows you to keep using Helm during your transition to a better place.
Definitely room for someone to make something better than Helm but I think you should give Jsonnet a shot if you haven't already.
How does it try? How does it fail?
I don't see any interactive CLI commands that could be seen as a replacement for kubectl.
kubectl apply, especially with server side apply is effective just a mechanism to push the declarative definition of your k8s objects to the server.
Pulumi instead tries to manage the lifecycle of each of those objects itself when it really has no business doing so. This results in a lot of roundtrips to the Pulumi state store (which is implemented horrendously inefficiently btw) and generally takes forever and a day compared to kubectl apply.
Most of this is down to an impedance mismatch between the models themselves. Pulumi + k8s works but it's just not a great experience. I say this with an absolute -ton- of experience with both options.
For instance Pulumi also generates random names for resources, again you can override these but again, not very ergonomic to work around etc.
IMO tools like Tanka + Jsonnet, or Cue are just better fit for k8s because you can stick with server side apply which is a much more native experience.
https://www.pulumi.com/registry/packages/kubernetes/how-to-g...
You don’t know something is going to work unless you actually do it.
I wonder if anyone's converted the API spec into json-schema, might be easier to plug into tools then.
edit: yes, and the tooling in front of it. https://github.com/yannh/kubeconform
following on original work from https://github.com/instrumenta/kubernetes-json-schema
What your Ansible is doing (looping over the inventory, imperatively executing tasks) is reinventing what a Kubernetes-internal Operator is doing after you asked it to reconcile your changes. Thing is: You're not only reinventing the internal thing, you will eventually recreate internal logic, e.g. some internal dependencies and knowledge "what to do in which order". Good luck with further developments on the Kubernetes upstream. Not cool.
Kubernetes does what it can, to give the user a declarative DX. You might not want to fight this. kustomize and some _trivial_ scripting is usually all you'll need. If you need more, consider writing custom Operators.
So not just k8s alone, k8s + kustomize, no Helm charts, right?
or can you do k8s + Helm, no kustomize?
or can you do kustomize to deploy Helm charts which abstracts away and translates to underlying k8s API calls/generated YAML?
I forgot to mention the CD tools we have, e.g. Flux or ArgoCD. Use customize to render a "final" yaml for your env/version/whatever and throw it at Flux/Argo (e.g. using GitOps) and be happy. That's imho the Kubernetes way of life.
although i think kustomize wouldn't make a nicer picture than ansible, but push vs pull .. i prefer pull
If helm is how some application is distributed we just render the template with our values file and store that in git, even though argocd can also use helm charts. We'd just prefer to see the diff in git instead of argocd so the deployment is still automated.
Just...Why?
I can imagine some cases where you might want to setup infrastructure-related software on your cluster and to do so through Ansible (using manifests or Helm charts under the hood), so that you have one template that you can use for setting up clusters later, for various use cases. For example, you might want a custom ingress controller, or to deploy a service mesh like Istio, or maybe Kiali as well. Perhaps you want to launch Rancher on your cluster to give your engineers an arguably easier way to interact with the cluster, or to explore it in a more visual way than just using something like the kubectl CLI (though tools like k9s are great, too: https://k9scli.io/).
Edit: Many suggest that using something like Argo CD would be a better tool for this, due to its specialization, though.
I actually utilized a similar approach when working with Ansible + Docker Swarm (since I needed a more lightweight orchestrator that played nicely with Docker, but to manage the nodes with Ansible): I deployed my own "ingress" (just a web server container with some configuration), some supporting software like Sonatype Nexus for storing custom images, Portainer for graphic cluster management and some other software for similar concerns. Once that was done and other server configuration (e.g. user accounts, groups, permissions, folders, services etc.; though most less relevant for the containers themselves) were set up, other CI/CD pipelines could deploy apps across the cluster without Ansible being involved - but it was good for setting up the base for it all.
But if you wanted to use Ansible for deploying almost all of your apps, like in the article? I guess you could do that if you have a lot of buy-in into using Ansible and just prefer the way you do things with it. Admittedly, using Helm or one of the alternatives in this case might be a better choice.
Also thank you for the very apt and red flagging summary kind sir.
Declarative configuration is wholly expressible by imperative languages. The converse is not true. Declarative configuration is appropriate for declaring state (i.e., values), and not for describing procedures.
Stop using half-baked YAML crap and start using real tools.
But I do think we need better tools to generate said declarative state, regardless of its serialization format (yaml, JSON or other). Hand editing the raw declarative state is clearly untenable, hence the development of the various templating solutions. But I don't see an issue with using an imperative program to generate the declarative state either, as long as the declarative state is what forms the substance of the API to the cluster.
The problem is that people want to express functions as extensions to declarations in yaml, XML, etc, instead of in a functional language that is heavy on declaration.
Ansible makes you comfortable with mutable infra, but with enough time I always regret using it.
I managed to grab 5 or so Raspberry Pi 4s before the availability issues. I've been (slowly) getting a home Kubernetes cluster running over the last year or two, and Ansible helps immensely; I:
* Write Ubuntu 20.04 (or now 22.04) to MicroSD card.
* Configure the initial user + SSH-PK, hostname, and network via config added to the SD
* Boot up the new Pi
* Run Ansible playbook to get it ready for k3s (lightweight but "full" k8s); including bind-mounting all high use paths for containerd/logs/etc to a USB NVME-SSD, adding boot args to the Pi's GRUB, etc.
* Push all my bashrc/tmux/vim/etc configs for consistent environment if I ssh in.
* Use Ansible ad-hoc for misc changes or package updates.
In terms of the "deploying to Kubernetes", I've recently got FluxCD working and love it. I think "GitOps" is the way to go in terms of clear reproducible config.
I have GitLab running on a local Synology (NAS) which kicks off any container builds and hosts the Git and container registry. The FluxCD operator on the cluster polls my "cluster as code" repo and syncs state. Each project defines a Helm chart and Flux pulls the chart from each repo and applies any variables (as configured in the "cluster repo") or "kustomizations".
Flux let's you setup "dependency" relationships for components. So I have it add the sealed-secrets operator early in, and put PK-encrypted secrets in directly the Git repo for it to"unpack" in the cluster.
That being said, the author's Anisble-driven approach is a much quicker way get something on a cluster ASAP, versus setting up the initial Flux "pull from cluster" system.
I'm primarily a software engineer, so this was also largely an exercise in getting more comfortable with the operations side of things.
Just thought I'd share my experience since only so many people let me rant about my FluxCD/k3s/GitLab home dev environment.
Edit: I'm not saying Ansible is an appropriate substitute (it absolutely isn't), rather that `kubectl -f` is not a scalable solution.
Not really hard, you get it up and running fast enough (who ever wants to setup a k8s cluster like this has the week to do so).
But honestly: don't!
I switched to rke2. Alternativly try out gardener from sap or anything else between Ansible and managed.
You do not want to take care of bare metal k8s.
And rke2 is still not perfect but it's rancher support will be better and better and more than just you are using it.
But be real: if you don't need air gap, go to a cloud provider.
Ah and btw open nebula also started supporting k8s by deploying rke2!
Use argocd or flux
Can and should be configured as IaC.
Argocd can manage itself!
In the pre-managed kubernetes world I did see an ansible stack for standing up a cluster that seemed ok, but once you get past setting up a base cluster ready to take 'customer' workloads ansible is just not a great tool.
Ideally the complexity of deploying ON a cluster (i.e. 'kubectl apply') doesn't increase with its size. In some ways it actually gets simpler, as you can worry less about per-node scheduling due to resource constraints/packing.