Skip to content

Commit f49fee2

Browse files
authored
Merge pull request #37 from paperclipinc/feat/crd-v1-and-k3s
feat!: graduate HCloudNodeClass CRD to v1; add k3s bootstrap support
2 parents 9596604 + c4bc12d commit f49fee2

35 files changed

Lines changed: 1005 additions & 136 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ CI runs `make test`, `golangci-lint`, `make generate-verify` (fails if generated
2222

2323
## Code generation
2424

25-
The `HCloudNodeClass` CRD and `zz_generated.deepcopy.go` are generated from the API types in `pkg/apis/v1alpha1` by `controller-gen`. If you change those types, run `make generate` and commit the regenerated files. CI will fail if they are out of date.
25+
The `HCloudNodeClass` CRD and `zz_generated.deepcopy.go` are generated from the API types in `pkg/apis/v1` by `controller-gen`. If you change those types, run `make generate` and commit the regenerated files. CI will fail if they are out of date.
2626

2727
## Testing conventions
2828

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ The CRD ships in the chart's `crds/` directory and is installed automatically by
7474
Create an `HCloudNodeClass` describing how nodes are built, and a `NodePool` describing what Karpenter may provision:
7575

7676
```yaml
77-
apiVersion: karpenter.hetzner.cloud/v1alpha1
77+
apiVersion: karpenter.hetzner.cloud/v1
7878
kind: HCloudNodeClass
7979
metadata:
8080
name: default
@@ -156,12 +156,14 @@ comments explaining every field.
156156
|------|-------------|
157157
| [`examples/talos-nodeclass.yaml`](examples/talos-nodeclass.yaml) | Talos Linux, private-network cluster, image pinned via label selector, machineconfig from a Secret |
158158
| [`examples/ubuntu-nodeclass.yaml`](examples/ubuntu-nodeclass.yaml) | Ubuntu 24.04, kubeadm join via inline cloud-init `userData` |
159+
| [`examples/k3s-nodeclass.yaml`](examples/k3s-nodeclass.yaml) | k3s agent join via cloud-init on an Ubuntu image (for k3s-based clusters) |
159160
| [`examples/nodepool-multiarch.yaml`](examples/nodepool-multiarch.yaml) | Multi-arch pattern: one NodeClass, two NodePools (amd64 CCX + arm64 CAX) |
160161

161162
### Bootstrap guides
162163

163164
- [Talos bootstrap guide](docs/talos-bootstrap.md) — obtaining the worker machineconfig, pinning images, and verifying node join.
164165
- [Ubuntu bootstrap recipe](docs/ubuntu-bootstrap.md) — kubeadm join via cloud-init, keeping tokens out of git, trade-offs vs Talos.
166+
- [k3s bootstrap recipe](docs/k3s-bootstrap.md) — k3s agent join via cloud-init, using the hcloud CCM for providerID; the natural path for k3s installers (hetzner-k3s, kube-hetzner).
165167

166168
## Configuration
167169

charts/karpenter-provider-hetzner/Chart.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ apiVersion: v2
22
name: karpenter-provider-hetzner
33
description: Karpenter cloud provider for Hetzner Cloud
44
type: application
5-
version: 1.0.0
6-
appVersion: "1.0.0"
5+
version: 2.0.0
6+
appVersion: "2.0.0"
77
# home points at Paperclip.inc (the project's canonical page) so listing
88
# "Homepage" links drive backlinks to the domain; GitHub stays as source.
99
home: https://paperclip.inc/karpenter-hetzner
@@ -26,4 +26,12 @@ annotations:
2626
url: https://github.com/paperclipinc/karpenter-provider-hetzner
2727
- name: Talos bootstrap guide
2828
url: https://github.com/paperclipinc/karpenter-provider-hetzner/blob/main/docs/talos-bootstrap.md
29+
- name: k3s bootstrap guide
30+
url: https://github.com/paperclipinc/karpenter-provider-hetzner/blob/main/docs/k3s-bootstrap.md
2931
artifacthub.io/category: integration-delivery
32+
artifacthub.io/containsSecurityUpdates: "false"
33+
artifacthub.io/changes: |
34+
- kind: changed
35+
description: "BREAKING: HCloudNodeClass graduated from karpenter.hetzner.cloud/v1alpha1 to /v1; re-apply node classes (no conversion webhook)"
36+
- kind: added
37+
description: "k3s agent bootstrap example and guide (examples/k3s-nodeclass.yaml, docs/k3s-bootstrap.md)"

charts/karpenter-provider-hetzner/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ helm install karpenter-provider-hetzner ./charts/karpenter-provider-hetzner \
1515

1616
`clusterName` is required; the controller fails to start without it.
1717

18+
## Upgrading to 2.0.0 (CRD `v1`)
19+
20+
Chart 2.0.0 graduates the `HCloudNodeClass` CRD from
21+
`karpenter.hetzner.cloud/v1alpha1` to the stable `/v1`. There is **no
22+
conversion webhook**, so this is a breaking change:
23+
24+
```bash
25+
helm upgrade karpenter-provider-hetzner ./charts/karpenter-provider-hetzner -n kube-system
26+
# Helm does not upgrade CRDs in crds/ on its own — apply the new CRD, then
27+
# re-apply your node classes with apiVersion: karpenter.hetzner.cloud/v1:
28+
kubectl apply -f charts/karpenter-provider-hetzner/crds/
29+
kubectl apply -f your-nodeclasses-v1.yaml
30+
```
31+
32+
Existing `v1alpha1` objects are not migrated automatically; recreate them under
33+
`v1` (the spec is unchanged — only the `apiVersion` differs).
34+
1835
## Values
1936

2037
| Key | Default | Description |

charts/karpenter-provider-hetzner/crds/karpenter.hetzner.cloud_hcloudnodeclasses.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ spec:
2222
- jsonPath: .status.conditions[?(@.type=='Ready')].status
2323
name: Ready
2424
type: string
25-
name: v1alpha1
25+
name: v1
2626
schema:
2727
openAPIV3Schema:
2828
properties:

cmd/controller/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import (
1111
"sigs.k8s.io/karpenter/pkg/controllers/state"
1212
"sigs.k8s.io/karpenter/pkg/operator"
1313

14-
// Register our v1alpha1 types.
15-
_ "github.com/paperclipinc/karpenter-provider-hetzner/pkg/apis/v1alpha1"
14+
// Register our HCloudNodeClass v1 types.
15+
_ "github.com/paperclipinc/karpenter-provider-hetzner/pkg/apis/v1"
1616

1717
hetznercp "github.com/paperclipinc/karpenter-provider-hetzner/pkg/cloudprovider"
1818
"github.com/paperclipinc/karpenter-provider-hetzner/pkg/controllers/nodeclass"

docs/integrations/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Integrating karpenter-provider-hetzner with Hetzner installers
2+
3+
Guidance and ready-to-use drafts for offering Karpenter autoscaling on top of
4+
the popular Hetzner Kubernetes installers.
5+
6+
## Strategy
7+
8+
All three target installers **already ship the Cluster Autoscaler**, so
9+
Karpenter is an *alternative* (per-pod instance-type selection + consolidation),
10+
not a missing feature. Two consequences shape every integration here:
11+
12+
1. **Opt-in only.** Default-off; never replace the existing autoscaler. Guard
13+
against running both at once.
14+
2. **Discussion before PR.** All three projects ask for it, none has a prior
15+
Karpenter thread, and the provider is young — lead by gauging appetite and
16+
disclosing the maintainer affiliation.
17+
18+
## Targets
19+
20+
| Installer | Distro | Fit | Status of draft here |
21+
|--|--|--|--|
22+
| [hcloud-k8s/terraform-hcloud-kubernetes](https://github.com/hcloud-k8s/terraform-hcloud-kubernetes) | Talos | **Best** — provider has Talos bootstrap; module already does worker machineconfig + inline manifests | `karpenter.tf` proposal + wiring guide |
23+
| [vitobotta/hetzner-k3s](https://github.com/vitobotta/hetzner-k3s) | k3s / Ubuntu | **Medium** — CLI-driven; k3s userData now documented; addresses a real CA limitation | discussion post + k3s example/guide in repo |
24+
| [kube-hetzner](https://github.com/kube-hetzner/terraform-hcloud-kube-hetzner) | k3s / MicroOS | **Hardest** — immutable MicroOS bootstrap is OS-specific | discussion post (feasibility-first) |
25+
26+
## Contents
27+
28+
- `terraform-hcloud-kubernetes/` — proposed `karpenter.tf`, `karpenter.variables.tf`,
29+
and `INTEGRATION.md` (the lighthouse integration).
30+
- `discussion-posts/` — copy-paste GitHub Discussion drafts for all three.
31+
32+
## Prerequisite work (in this repo)
33+
34+
The k3s targets needed a worked k3s bootstrap path, which the provider now has:
35+
- [`examples/k3s-nodeclass.yaml`](../../examples/k3s-nodeclass.yaml)
36+
- [`docs/k3s-bootstrap.md`](../k3s-bootstrap.md)
37+
38+
## Recommended sequence
39+
40+
1. Post the three Discussions; gauge appetite.
41+
2. Land the **hcloud-k8s (Talos)** integration first — cleanest fit, draft ready.
42+
3. For **hetzner-k3s**, prototype the `autoscaling.provider` switch if the
43+
maintainer is interested.
44+
4. For **kube-hetzner**, prove a MicroOS+k3s bootstrap end-to-end *before* any
45+
module PR.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!-- Post as a GitHub Discussion (Ideas) on hcloud-k8s/terraform-hcloud-kubernetes -->
2+
<!-- Title: Optional Karpenter autoscaling (karpenter-provider-hetzner) as an alternative to Cluster Autoscaler? -->
3+
4+
### Summary
5+
6+
Would you be open to an **opt-in** Karpenter integration alongside the existing
7+
Cluster Autoscaler, using [`karpenter-provider-hetzner`](https://github.com/paperclipinc/karpenter-provider-hetzner)?
8+
9+
**Disclosure:** I help maintain `karpenter-provider-hetzner`, so I have an
10+
interest here. I'm raising this as a Discussion (not a surprise PR) precisely
11+
because the module already has solid Hetzner autoscaling — I want to check
12+
appetite before building anything.
13+
14+
### Why this module specifically
15+
16+
Of the popular Hetzner installers, this one is the cleanest fit for Karpenter:
17+
18+
- The provider already supports **Talos worker bootstrap**.
19+
- This module already **generates Talos worker machineconfigs** and ships
20+
components as **Talos inline manifests** — so the integration reuses existing
21+
machinery (`data.talos_machine_configuration`, the `*_manifest` pattern)
22+
rather than adding new bootstrap logic.
23+
- The hcloud CCM you already install provides the `hcloud://<id>` providerID
24+
Karpenter needs.
25+
26+
### What I'm proposing (not a replacement)
27+
28+
Karpenter is an *alternative* to the Cluster Autoscaler, not a gap-filler — it
29+
does per-pod instance-type selection and consolidation instead of pre-declared
30+
ASGs. So I'd keep it strictly opt-in:
31+
32+
- New `karpenter_nodepools` variable; empty by default → **zero change** for
33+
current users.
34+
- A guard so Karpenter and `cluster_autoscaler_nodepools` can't both be enabled
35+
(both want to own provisioning).
36+
- Follows `cluster_autoscaler.tf` exactly: `data.helm_template` to render the
37+
chart, a generated worker machineconfig as the node `userData`, manifests
38+
appended to `talos_inline_manifests`.
39+
40+
I have a working draft of `karpenter.tf` + variables ready to share.
41+
42+
### Open questions
43+
44+
1. Is an alternative autoscaler something you'd want to carry, or do you prefer
45+
to keep one autoscaling path?
46+
2. `karpenter-provider-hetzner` is young — would you want a stability bar (e.g.
47+
N months / pinned chart, marked beta) before merge?
48+
3. Preference on shape: single shared `HCloudNodeClass` + per-pool NodePools
49+
(what I drafted), or a richer per-pool NodeClass mapping?
50+
51+
Happy to open the PR if there's interest. Thanks for the module — it's the
52+
nicest Talos-on-Hetzner setup out there.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!-- Post as a GitHub Discussion on vitobotta/hetzner-k3s (per CONTRIBUTING: open a Discussion for feature suggestions first) -->
2+
<!-- Title: Optional Karpenter-based autoscaling as an alternative to Cluster Autoscaler? -->
3+
4+
### Summary
5+
6+
Would you consider an **opt-in** Karpenter autoscaling backend using
7+
[`karpenter-provider-hetzner`](https://github.com/paperclipinc/karpenter-provider-hetzner),
8+
selectable per cluster instead of the current Cluster Autoscaler?
9+
10+
**Disclosure:** I help maintain `karpenter-provider-hetzner`. Following your
11+
CONTRIBUTING guidance, I'm opening a Discussion before any PR to gauge interest.
12+
13+
### The motivation
14+
15+
`hetzner-k3s` already documents a real limitation of the CA path:
16+
17+
> "You can't specify different images for each autoscaled pool yet."
18+
19+
More broadly, the CA Hetzner provider pre-declares fixed pools, so you can't mix
20+
instance types within a pool or get automatic consolidation. Karpenter is built
21+
exactly for this: it picks the cheapest Hetzner type that fits each pending pod
22+
(CX/CPX/CAX/CCX, amd64+arm64) and consolidates underused nodes.
23+
24+
### How it would fit hetzner-k3s
25+
26+
Karpenter is bootstrap-agnostic — Hetzner runs the node's `userData` as
27+
cloud-init, so a k3s agent join works the same way your CA cloud-init does
28+
today. The natural shape, mirroring how you already install the CA:
29+
30+
- A config switch, e.g. `autoscaling: { provider: karpenter | cluster_autoscaler }`.
31+
- When `karpenter`, the CLI installs the provider (Helm) and generates the
32+
`HCloudNodeClass` + `NodePool` manifests, with `userData` doing the k3s agent
33+
join using the existing cluster token + server endpoint.
34+
- Reuses the hcloud CCM you already install (it sets the `hcloud://<id>`
35+
providerID Karpenter needs; k3s servers run with `--disable-cloud-controller`
36+
and agents with `cloud-provider=external`).
37+
38+
I've written a worked k3s bootstrap example + guide for the provider so the join
39+
path is concrete:
40+
- https://github.com/paperclipinc/karpenter-provider-hetzner/blob/main/examples/k3s-nodeclass.yaml
41+
- https://github.com/paperclipinc/karpenter-provider-hetzner/blob/main/docs/k3s-bootstrap.md
42+
43+
### Open questions
44+
45+
1. Is a second, opt-in autoscaler something you'd want in `hetzner-k3s`, or out
46+
of scope?
47+
2. The provider is young — would you want it marked experimental / pinned until
48+
it has soak time?
49+
3. Config ergonomics: a global `autoscaling.provider` switch, or per-pool?
50+
51+
Not looking to replace the CA — just to offer Karpenter where its
52+
consolidation / per-pod sizing helps. Happy to prototype if you're open to it.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!-- Post as a GitHub Discussion on kube-hetzner/terraform-hcloud-kube-hetzner -->
2+
<!-- Title: Interest in optional Karpenter autoscaling (k3s on MicroOS)? Feasibility check -->
3+
4+
### Summary
5+
6+
Gauging interest in an **opt-in** Karpenter autoscaling option using
7+
[`karpenter-provider-hetzner`](https://github.com/paperclipinc/karpenter-provider-hetzner),
8+
as an alternative to the current Cluster Autoscaler — and being upfront that
9+
MicroOS makes this the hardest of the Hetzner installers to support.
10+
11+
**Disclosure:** I help maintain `karpenter-provider-hetzner`. This is a
12+
Discussion, not a PR — I want to validate feasibility and appetite with you
13+
first, because the honest answer may be "not yet."
14+
15+
### The hard part (stated plainly)
16+
17+
kube-hetzner runs **k3s on immutable openSUSE MicroOS**. Karpenter is
18+
bootstrap-agnostic (Hetzner runs the node `userData` as cloud-init), but the
19+
provider currently ships worked examples only for Ubuntu/kubeadm and Talos. A
20+
MicroOS path would need a cloud-init that reproduces your
21+
`autoscaler-cloudinit.yaml.tpl` flow — the k3s-agent install on the
22+
transactional-update OS, snapshot/reboot semantics, the shared k3s token and
23+
endpoint. That's real, MicroOS-specific work, not a thin addon.
24+
25+
It would also overlap directly with your well-integrated Cluster Autoscaler
26+
path (same token/`HCLOUD_CLUSTER_CONFIG` plumbing), so it'd be a *second*
27+
autoscaler to maintain, not a gap-filler.
28+
29+
### Why it might still be worth it
30+
31+
Karpenter does per-pod instance-type selection across CX/CPX/CAX/CCX and
32+
consolidation, rather than fixed pre-declared pools. For users who want
33+
mixed-type bin-packing and aggressive scale-down, that's a meaningful upgrade
34+
over the CA model.
35+
36+
### What I'd want from you before building anything
37+
38+
1. Is an opt-in second autoscaler something kube-hetzner would consider, given
39+
the maintenance surface?
40+
2. If yes — would you prefer I first prove a **MicroOS + k3s-agent** Karpenter
41+
bootstrap works end-to-end (as a standalone example against a kube-hetzner
42+
cluster) before any module PR? That de-risks the OS-specific part.
43+
3. Any prior context I'm missing — I searched issues/discussions and found no
44+
existing Karpenter thread, but you'd know if it's been considered.
45+
46+
For reference, here's the k3s (Ubuntu) bootstrap path I've already documented;
47+
the MicroOS variant would build on it:
48+
- https://github.com/paperclipinc/karpenter-provider-hetzner/blob/main/docs/k3s-bootstrap.md
49+
50+
Thanks — kube-hetzner is the gold standard for k3s-on-Hetzner, so I'd rather do
51+
this right (or not at all) than bolt on something fragile.

0 commit comments

Comments
 (0)