ExplanationKubernetes

rke2spray vs. Kubespray

Compare the Ansible automation layer separately from Kubespray's kubeadm execution layer and rke2spray's RKE2 execution layer.

Updated Verified SourceEdit this page

Key takeaways

  • The direct Ansible comparison is between the inventory, playbook, role, variable, tag, and validation contracts of Kubespray and rke2spray
  • The bootstrap, runtime, and PKI differences between kubeadm and RKE2 belong to the Kubernetes execution-engine layer rather than the Ansible repository layer
  • Kubespray is the upstream kubeadm-based implementation, while rke2spray is a separate Ansible implementation that translates its public API into an RKE2 lifecycle
  • Shared cluster.yml, inventory groups, and role paths preserve names and operational intent without guaranteeing identical task bodies, variable semantics, or resulting state
  • rke2spray provides only the reusable core and sample inventory, while jamie-kr-gitops owns the real jamie-kr inventory as consumer code
  • Selection must consider both Ansible reuse and whether the team wants to own kubeadm component choice or RKE2 standardization

The comparison separates three layers

LayerKubespray pathrke2spray pathComparison question
Ansible automationOriginal Kubespray playbooks and rolesRKE2 adapters that track the Kubespray APIHow are inputs and tasks interpreted
Kubernetes bootstrapkubeadm init/joinRKE2 server and agent joinHow is the cluster created
Node execution and operationComposed Kubernetes componentsRKE2 service and release unitWhat unit is upgraded and recovered
  • The Ansible layer interprets inventory and commands before passing them to the execution-engine layer
  • The engine difference causes the implementation differences in Ansible roles, variables, and tags
  • The rke2spray findings follow the verified main code and docs/kubespray-api-compatibility.yml
Shared Inventory, Playbook, and Role names provide a common entry shape; kubeadm and RKE2 still use different internal task graphs

jamie-kr-gitops owns the real inventory

  • rke2spray and jamie-kr-gitops are separated as provider and consumer
    • rke2spray provides reusable playbooks, roles, plugins, defaults, and inventory/sample
    • jamie-kr-gitops owns real hosts, group variables, host variables, encrypted Vault values, and execution wrappers
  • The dependency remains one-way, with jamie-kr-gitops selecting a pinned rke2spray release
    • The rke2spray core does not reference jamie-kr names, addresses, domains, or topology
    • jamie-kr-gitops invokes rke2spray through a Collection or pinned checkout
jamie-kr-gitops/
├── inventory/
│   └── jamie-kr/
│       ├── hosts.yml
│       ├── group_vars/
│       └── host_vars/
├── ansible/
│   └── rke2spray.rev
└── apps/
    └── ...

            └── consumes a pinned rke2spray checkout

rke2spray/
├── cluster.yml
├── roles/
├── plugins/
└── inventory/
    └── sample/
  • jamie-kr-gitops manages both Ansible IaC and Argo CD GitOps declarations for the environment
    • inventory/ and ansible/rke2spray.rev are reconciliation inputs used before the Kubernetes API exists
    • bootstrap/ and apps/ are reconciliation inputs used by Argo CD after the Kubernetes API exists
  • After the inventory migration, commands run from the jamie-kr-gitops repository
make inventory-vault-check
make inventory-preflight
make cluster-converge
make gitops-bootstrap
  • GitOps in the repository name describes the management model and does not exclude Ansible IaC ownership
    • Inventory is declarative cluster desired state consumed before the Kubernetes API exists
    • apps/ is platform desired state reconciled by Argo CD after the Kubernetes API exists
    • Both states can be reviewed as one environment release while rke2spray and Argo CD remain separate execution engines

C4 · Level 1 · System context

Platform operatorReviews desired state and runs guarded operations
jamie-kr-gitopsOwns environment inventory and Kubernetes desired state
rke2sprayInterprets Kubespray-shaped input as an RKE2 lifecycle
RKE2 platformRuns Kubernetes API, Argo CD, platform and data workloads
External systemsGitHub, DNS, TCP load balancer and managed NFS
Arrows show control and reconciliation relationships, not runtime packet flow

The Ansible layer interprets the public API differently

Ansible areaKubesprayrke2spray
Project roleUpstream implementation maintained under Kubernetes SIGsAdapter between the Kubespray API and RKE2
Collectionkubernetes_sigs.kubesprayjongminchung.rke2spray
API baselineCurrent repository playbooks, roles, defaults, and tagsUpstream API pinned by KUBESPRAY_BASELINE
Inventory groupsOriginal meaning of kube_control_plane, kube_node, and etcdSame names reinterpreted as RKE2 server, agent, and datastore topology
Root playbooksCanonical lifecycle implementationAdapter entrypoints that preserve names and operational intent
Role bodiesExecute kubeadm, runtime, CNI, and etcd tasks directlyClassify roles as supported, adapter, conditional, or unsupported
Default variablesConsumed directly by the corresponding Kubespray rolesMapped to RKE2 configuration or rejected at preflight when no equivalent exists
TagsSelect upstream tasksPreserve parity names while selecting RKE2 tasks
Extension pointsUpstream roles/, contrib/, and documentationRKE2 extra_playbooks/, native add-ons, and acceptance harness
Compatibility failureDetermined by upstream validation and role executionForbids silent ignore and reports a preflight error with an alternative
Change intakeConsumes Kubespray releases and master directlyReviews mappings, exceptions, and tests in baseline update pull requests
  • The central Ansible difference is which task graph a shared public name resolves to
    • Kubespray's kubernetes/kubeadm role executes kubeadm tasks
    • The rke2spray role at that path connects RKE2 bootstrap, node, and control-plane adapters
  • A variable present in rke2spray does not automatically mean that the Kubespray feature is supported
    • container_manager: docker, container_manager: crio, kubeadm patches, and direct external CNI installation are currently rejected
    • Registry mirror, authentication, and TLS inputs that can be represented losslessly map to RKE2 registries.yaml
  • Shared inventory and playbook names can reduce runbook translation cost without creating drop-in compatibility

The kubeadm layer describes Kubespray's execution model

  • This layer describes the kubeadm-based cluster model automated by Kubespray, not the Ansible API difference between the two repositories
AreaKubespray implementation on kubeadm
BootstrapUses kubeadm init, kubeadm join, and phases
Control planeBuilds API server, scheduler, and controller configuration through kubeadm configuration and static Pod manifests
KubeletKubespray roles manage its binary, configuration, and systemd unit
RuntimeSeparate roles install and configure containerd, CRI-O, or the Docker path
CNIAnsible controls the ordering between Kubernetes API bootstrap and CNI-specific roles or manifests
etcdKubespray roles configure colocated or separate topology according to the etcd inventory group
PKIUses kubeadm and Kubespray certificate commands and paths
VersionsComposes Kubernetes, etcd, runtime, and CNI versions by component
UpgradeCordons and drains before replacing components in a defined order
  • The kubeadm model provides flexibility to select and replace Kubernetes components individually
  • Its tradeoff is that the team owns compatibility, upgrade ordering, and failure boundaries across more combinations

The RKE2 layer describes rke2spray's execution model

  • This layer describes the RKE2 distribution automated by rke2spray, not the Ansible interface difference
Arearke2spray implementation on RKE2
BootstrapJoins servers and agents through a token, bootstrap server, and supervisor :9345
Control planeThe rke2-server service owns the API server, scheduler, and controller lifecycle as a bundle
KubeletIncluded in the rke2-server or rke2-agent lifecycle
RuntimeFixes execution to bundled containerd and manages only registry inputs through registries.yaml
CNISelects packaged Canal, Calico, Cilium, or Flannel through RKE2 cni configuration
etcdDefaults to embedded etcd and can connect to Kubespray-managed external etcd
PKIUses RKE2 certificate commands, paths, and automatic rotation contracts
VersionsUses a checksum-locked rke2_version that combines a Kubernetes patch and RKE2 revision
UpgradeConverges servers sequentially and agents in bounded batches to an RKE2 release
  • The RKE2 model standardizes the runtime, PKI, and control-plane lifecycle around a release and service unit
  • Its tradeoff is that requirements such as alternate runtimes, kubeadm phases, and external or custom CNIs become constraints outside the current standard contract
  • A feature technically possible in RKE2 must be distinguished from one supported by the current rke2spray adapter

Engine differences translate into Ansible contracts

Kubespray APIKubespray taskrke2spray interpretationClassification
kube_versionSelects Kubernetes component versionsValidates a release-locked rke2_version with the same patchAdapter
kubernetes/kubeadmExecutes kubeadm bootstrap and joinTranslates into RKE2 server and agent bootstrapAdapter
container-engineInstalls and tunes a runtimeManages only registry configuration for bundled containerdAdapter
container_manager: docker or crioInstalls the selected runtimeRejects before lifecycle mutationUnsupported
network_pluginExecutes CNI-specific roles and manifestsTranslates into packaged CNI selectionAdapter
External or custom CNIInstalls external manifestsRejects because the bootstrap and Ready-order adapter is incompleteUnsupported
etcd roleInstalls and configures etcdRuns only when external datastore mode is selectedConditional
recover-control-plane.ymlRecovers etcd and control-plane stateRuns RKE2 cluster reset and restore from an embedded-etcd snapshotConditional
  • An adapter translates intent without reproducing the original implementation
  • Conditional means the public API is valid only when a datastore or topology prerequisite holds
  • Unsupported can mean that the current rke2spray contract lacks a safe adapter, not that RKE2 can never implement the feature

Shared playbooks preserve intent only

Operational intentShared entrypointKubespray executionrke2spray execution
Install or reconcilecluster.ymlConverges kubeadm, runtime, CNI, and etcd tasksConverges RKE2 servers and agents
Add workersscale.ymlConfigures kubelet, runtime, and CNIInstalls the RKE2 agent
Upgradeupgrade-cluster.ymlReplaces components sequentiallyReplaces a checksum-locked RKE2 release
Remove a noderemove-node.ymlCleans Kubernetes, etcd, and host stateCleans the RKE2 service and state
Resetreset.ymlCleans kubeadm, runtime, and network stateUses the official RKE2 uninstaller path
  • rke2spray adds health, snapshots, certificate rotation, and add-on plan, converge, and prune operations under extra_playbooks/
  • Destructive removal, recovery, and ownership-transfer paths add confirmation and preflight gates

Selection evaluates Ansible reuse and the execution model together

RequirementEvaluate firstReason
Kubespray roles, variables, and tags must retain their original semanticsKubesprayIts upstream task graph is the public API implementation
RKE2 is the standard while Kubespray-shaped runbooks should be retainedrke2sprayIt translates the public names into the RKE2 lifecycle
Custom CNI, alternate runtime, or kubeadm phase control is mandatoryKubesprayThose inputs are rejected by the current rke2spray contract
Operations should use one release, service, and PKI contractrke2sprayIt treats the RKE2 distribution as the execution unit
Broad community, provider, and CNI combinations are the priorityKubesprayIt consumes the Kubernetes SIGs upstream directly
A dedicated owner can maintain an adapter across Kubespray and RKE2rke2sprayThe team accepts baseline, mapping, and acceptance-test maintenance
  • Moving an existing Kubespray cluster to rke2spray is a Kubernetes engine migration rather than an Ansible repository swap
    • Shared inventory groups do not convert current cluster state, PKI, datastore, or CNI state
    • Existing variables and custom roles must be audited against the supported, adapter, conditional, and unsupported classifications

Conclusion and next actions

  • At the Ansible layer, Kubespray is the original implementation and rke2spray is an RKE2 adapter that preserves public names
  • At the execution layer, kubeadm composes components while RKE2 integrates the distribution, service, and release lifecycle
  • At the ownership layer, jamie-kr-gitops owns the real inventory while rke2spray remains the reusable installation engine
  • A proof of concept should validate variable mappings, tag reachability, upgrades, node removal, and snapshot recovery in addition to running the shared cluster.yml entrypoint

Sources