diff --git a/README.md b/README.md index 060ea03..8e0b201 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,7 @@ If the ICMP raw socket cannot be opened — typically because the binary is runn | `VK_PROVIDER_ID` | unset | Cloud-provider ProviderID for the virtual node (e.g. `gce:////`). Prevents cloud node lifecycle controllers from deleting the virtual node. | | `VK_TLS_CERT` | `/etc/cocoon/vk/tls/vk-kubelet.crt` | Path to the kubelet serving TLS certificate. | | `VK_TLS_KEY` | `/etc/cocoon/vk/tls/vk-kubelet.key` | Path to the kubelet serving TLS private key. | +| `VK_KUBELET_PORT` | `10250` | Port the virtual node's kubelet API listens on and advertises. Override when a real kubelet on the same host already owns `:10250` (e.g. a co-located k3s server node). | | `VK_METRICS_ADDR` | `:9091` | Plain-HTTP prometheus listener. | | `VK_RESERVE_PERCENT` | `20` | Percentage of host resources reserved for the host OS (0-100). Allocatable = Capacity × (100 - reserve) / 100. | | `VK_NODE_CPU` | auto-detected | Override CPU capacity (auto: `runtime.NumCPU()`). | diff --git a/main.go b/main.go index b100571..496428e 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "os" "os/signal" "slices" + "strconv" "strings" "syscall" "time" @@ -51,12 +52,12 @@ const ( defaultMetricsAddr = ":9091" defaultOrphanPolicy = string(provider.OrphanDestroy) - defaultTLSCert = "/etc/cocoon/vk/tls/vk-kubelet.crt" - defaultTLSKey = "/etc/cocoon/vk/tls/vk-kubelet.key" - kubeletAPIPort = 10250 - endpointPatchWait = 5 * time.Second - endpointPatchRetry = 2 * time.Second - shutdownTimeout = 10 * time.Second + defaultTLSCert = "/etc/cocoon/vk/tls/vk-kubelet.crt" + defaultTLSKey = "/etc/cocoon/vk/tls/vk-kubelet.key" + defaultKubeletAPIPort = 10250 + endpointPatchWait = 5 * time.Second + endpointPatchRetry = 2 * time.Second + shutdownTimeout = 10 * time.Second ) func main() { @@ -153,7 +154,7 @@ func main() { cfg.Node.Status.Capacity = nodeCapacity cfg.Node.Status.Allocatable = nodeAllocatable cfg.Node.Status.DaemonEndpoints = corev1.NodeDaemonEndpoints{ - KubeletEndpoint: corev1.DaemonEndpoint{Port: kubeletAPIPort}, + KubeletEndpoint: corev1.DaemonEndpoint{Port: kubeletAPIPort()}, } if providerID != "" { cfg.Node.Spec.ProviderID = providerID @@ -195,7 +196,7 @@ func main() { metricsServer := commonhttpx.NewServer(metricsAddr, metricsMux) go func() { - logger.Infof(signalCtx, "vk-cocoon node %s kubelet API on :%d", nodeName, kubeletAPIPort) + logger.Infof(signalCtx, "vk-cocoon node %s kubelet API on :%d", nodeName, kubeletAPIPort()) if err := n.Run(signalCtx); err != nil { logger.Fatalf(signalCtx, err, "virtual-kubelet node exited") } @@ -263,9 +264,18 @@ func buildProvider(ctx context.Context, opts buildOpts) (*cocoon.Provider, error return p, nil } +// kubeletAPIPort is overridable via VK_KUBELET_PORT so a co-located kubelet (e.g. k3s) can keep :10250. +func kubeletAPIPort() int32 { + if p, err := strconv.ParseUint(os.Getenv("VK_KUBELET_PORT"), 10, 16); err == nil && p > 0 { + return int32(p) + } + return defaultKubeletAPIPort +} + func withHandler(h http.Handler) nodeutil.NodeOpt { return func(cfg *nodeutil.NodeConfig) error { cfg.Handler = h + cfg.HTTPListenAddr = fmt.Sprintf(":%d", kubeletAPIPort()) return nil } } @@ -287,7 +297,7 @@ func patchKubeletEndpoint(ctx context.Context, clientset kubernetes.Interface, n continue } nodeObj.Status.DaemonEndpoints = corev1.NodeDaemonEndpoints{ - KubeletEndpoint: corev1.DaemonEndpoint{Port: kubeletAPIPort}, + KubeletEndpoint: corev1.DaemonEndpoint{Port: kubeletAPIPort()}, } if _, err := clientset.CoreV1().Nodes().UpdateStatus(ctx, nodeObj, metav1.UpdateOptions{}); err != nil { logger.Errorf(ctx, err, "patch daemon endpoints attempt %d", attempt) @@ -296,7 +306,7 @@ func patchKubeletEndpoint(ctx context.Context, clientset kubernetes.Interface, n } continue } - logger.Infof(ctx, "node %s kubelet endpoint set to :%d", nodeName, kubeletAPIPort) + logger.Infof(ctx, "node %s kubelet endpoint set to :%d", nodeName, kubeletAPIPort()) return } }