Garage Kubernetes Operator

repository·main·Indexed 42 days ago

https://github.com/rajsinghtech/garage-operator

A Kubernetes operator for managing Garage, a distributed S3-compatible object storage system. It provides a declarative way to handle cluster lifecycles, storage and gateway tiers, bucket and key management, and multi-cluster federation using Custom Resource Definitions (CRDs) such as GarageCluster, GarageBucket, and GarageKey. Requires Kubernetes 1.25+ and Garage v2.0.0 or newer.

Tokens
76.9K
Snippets
167
Records
282
Agent score
53%

What's inside Garage Operator

  1. Overview of Garage Operator Custom Resources

    main

    The Garage Operator manages several Kubernetes Custom Resources (CRDs) to control Garage topology, storage, and access. The primary resource is GarageCluster, which handles the overall layout and configuration. Other resources manage specific components like buckets, S3 keys, nodes, and administrative access.

    Short names for quick usage with kubectl include:

    • gc for GarageCluster
    • gb for GarageBucket
    • gk for GarageKey
    • gn for GarageNode
    • gat for GarageAdminToken
    • grg for GarageReferenceGrant

    Always verify available resources on your cluster using kubectl api-resources.

    kubectl api-resources
  2. Overview of Garage Kubernetes Operator

    main

    The Garage Kubernetes Operator manages Garage, a distributed, self-hosted, S3-compatible object storage system. It provides a declarative way to manage the entire cluster lifecycle, including storage and gateway tiers, bucket management, and multi-cluster federation.

    Key Capabilities:

    • Declarative Lifecycle: Manage StatefulSets, configurations, and layouts via Custom Resources (CRDs).
    • Unified Tiers (v1beta2): Combine storage pods and persistent-identity S3 gateways in a single GarageCluster resource.
    • Node-local Pools: Bind Garage identities to specific Kubernetes Nodes and HostPath disks (requires Kubernetes 1.27+).
    • Bucket & Key Management: Provision buckets, quotas, and S3 credentials using kubectl.
    • Multi-cluster Federation: Automatically discover nodes across different Kubernetes clusters.
    • Scale Subresource: Supports kubectl scale for auto-managed storage groups.
    • COSI Driver: Optional Kubernetes-native object storage provisioning.
  3. API Versioning and Pool Compatibility

    main

    Node-local pools are only supported in v1beta2 of the API.

    v1beta1 Compatibility

    A v1beta1 view is maintained for backward compatibility. It keeps the default StatefulSet/PVC group editable and carries the pool list in a reserved conversion annotation:

    garage.rajsingh.info/v1beta2-node-local-pools

    When converting back to v1beta1, only the pool list is restored; the v1beta2-only marker is removed. The v1beta1 webhook protects against accidental erasure by rejecting mutations to the payload if the original request was made via v1beta1.

    Migration Warning for Unreleased Prototypes

    If you are using unreleased prototypes with the following keys, you must stop them and recreate or migrate them before installing the final CRD to avoid identity conflicts:

    • storage.workload: DaemonSet
    • storage.pools
    • storage.nodePools
    • backing: StoragePool
    • backing: NodePool
    • poolName
    • nodePoolName
    • storage-pool/node-pool labels
    • conversion annotations used in prototypes
  4. Understand the GarageCluster rollout guard and recovery behavior

    main

    The GarageCluster controller implements a safety mechanism to prevent unsafe layout mutations during storage rollouts. When a rollout is in progress, the controller may enter a state where it waits for coordination with other nodes.

    Key Behaviors:

    • Rollout Wait State: If the controller encounters errLayoutMutationPending, it treats this as a retryable safety wait rather than a terminal failure. It will publish StorageRolloutReady=False with the reason Waiting.
    • Health Projection: The operator clears the previous Status.Health observation before each new status pass. This ensures that if the Garage Admin API becomes unavailable, the operator does not incorrectly report a stale "healthy" status.
    • Rollout Restrictions: While a storage rollout boundary is active, the operator restricts operations to prevent workload outages. It will not import roles or stage/apply layout mutations until the rollout proof completes.
    • Allowed Operations: During an active rollout wait, only the following are permitted:
      1. Federation RPC reconnect work.
      2. Explicitly requested connect-nodes operations.

    Operational Recovery

    The operator does not automatically perform high-risk recovery actions. It will not clear a Garage layout guard, invoke skip-dead-nodes, delete Pods, or infer that a disconnected node is safe to remove. These require separate operational supervision.

  5. Use GarageReferenceGrant for cross-namespace access

    main
    When using v1beta1, cross-namespace references (e.g., a GarageKey in namespace team-a referencing a GarageCluster in namespace storage) require a GarageReferenceGrant resource to be present in the target namespace. Without this grant, the admission webhook will reject the resource creation.
  6. Understand GarageCluster operation status

    main

    All one-shot operations triggered via annotations record their outcome in status.lastOperation. This allows you to verify if a command succeeded or failed.

    Success Schema:

    status:
      lastOperation:
        type: "Repair:Blocks"
        triggeredAt: "2026-05-02T10:00:00Z"
        succeeded: true

    Failure Schema: If an operation fails, succeeded will be false and an error field will contain the error message. The annotation remains on the resource so the operator can automatically retry the operation during the next reconciliation cycle.

  7. Configure bucket deletion behavior

    main

    The spec.deletionPolicy field determines how the remote Garage bucket is handled when the GarageBucket Kubernetes resource is deleted:

    • Delete (default): The operator attempts to delete the Garage bucket. This only succeeds if the bucket is empty. If the bucket contains objects, the resource will stay in a Deleting state and set DeletionBlocked=True with the reason BucketNotEmpty. You must manually remove objects/multipart uploads via an S3 client to allow deletion.
    • Retain: The Kubernetes resource is removed, but the bucket, its data, aliases, and settings remain in Garage.

    Important: If using Retain, record the status.bucketId before deleting the CR so you can re-attach to it later using spec.bucketId.

    apiVersion: garage.rajsingh.info/v1beta1
    kind: GarageBucket
    metadata:
      name: postgres-backups
      namespace: storage
    spec:
      clusterRef:
        name: external-garage
      deletionPolicy: Retain
  8. Authorize cross-namespace access with GarageReferenceGrant

    main

    The GarageReferenceGrant resource allows administrators to define trust boundaries for cross-namespace access to Garage resources.

    How it works

    The grant lives in the destination namespace. It uses spec.from to list allowed source resources.

    Configuration

    • spec.from: A list of source entries. Each entry contains a source kind (GarageBucket, GarageKey, or GarageAdminToken) and either an exact namespace or a namespaceSelector (using standard Kubernetes matchLabels or matchExpressions).
    • spec.to: Narrows the destination. If omitted, it allows all resources of the target kind.
      • Omitting spec.to defaults to allowing GarageCluster and GarageBucket targets.
      • To allow GarageKey targets, you must explicitly add an entry in spec.to.

    Important Constraints

    • Namespace Labels: If using namespaceSelector, access is granted to any namespace that carries the matching label. Ensure these labels are controlled by a trusted policy.
    • No Cross-Namespace Admin Tokens: While GarageAdminToken is in the schema for compatibility, a grant cannot make static admin credentials cross-namespace.
    • No Cross-Namespace Nodes: GarageNode is never allowed cross-namespace.
    • Cleanup: Before removing a grant, check status.inUseBy. It is safe to remove a grant only when this list is empty. Removing a grant does not revoke already-issued Garage permissions; it only prevents future reconciliations from succeeding.
  9. How Garage cluster federation works

    main

    Garage federation allows creating a single distributed layout across multiple physical sites. To function correctly, the federation treats the layout as shared state. This requires:

    1. Shared RPC Secret: Every writer in the federated layout must use the exact same RPC secret.
    2. Identity-Specific Addresses: Every identity-bearing Garage process must advertise an externally routable RPC address.
    3. Serialized Topology Changes: Changes to the cluster topology must be serialized across Kubernetes clusters.

    Note: The Admin API endpoint is intended for control-plane calls and does not replace the requirement for per-node RPC routing.

  10. Conversion caveats between GarageCluster v1beta1 and v1beta2

    main

    The v1beta2 version of GarageCluster introduces a structural change by separating spec.storage and spec.gateway tiers, whereas the legacy v1beta1 used a flat spec.replicas and a boolean spec.gateway.

    Data Loss Warning: A v1beta2 object containing both storage and gateway tiers has no faithful representation in v1beta1. When converting to v1beta1, the webhook returns only the storage view and stores v1beta2-only data in an internal reserved payload.

    Limitation: A v1beta1 client cannot edit the hidden gateway or node-local-pool payload.

  11. Understand NodeLocalPool ownership and resources

    main

    When a nodeLocalPool is active, the operator manages several resources to ensure safe membership transitions and identity stability.

    Managed Resources for a pool named local-700 in cluster garage:

    • DaemonSet: garage-storage-local-700.
    • ConfigMap: Active revision garage-storage-local-700-config-<config-hash>.
    • Internal GarageNode: One per active Kubernetes Node (with backing: NodeLocalPool). This resource manages the Garage layout role but does not create a StatefulSet or PVC.
    • Activation Gate: A scheduler-enforced gate on every DaemonSet Pod to prevent premature scheduling during membership changes.
    • Labels & Annotations:
      • garage.rajsingh.info/node-local-pool=<pool name>
      • garage.rajsingh.info/kubernetes-node=<node name or stable hash>

    Note: Node-local pool-backed GarageNode resources cannot be created manually by users; the admission webhook prevents forged ownership.

  12. Understand Garage Operator Custom Resource (CRD) responsibilities

    main

    The operator manages several Custom Resources (CRDs) under the API group garage.rajsingh.info. Each resource has specific responsibilities and manages corresponding Kubernetes artifacts:

    ResourcePrimary responsibilityKubernetes artifacts
    GarageClusterCluster config, tier topology, services, layout coordination, health, federation, operationsStatefulSets, node-local-pool DaemonSets, Services, ConfigMaps, PDBs, endpoint Services, generated GarageNodes
    GarageNodeOne Garage identity and layout roleUsually one single-replica StatefulSet, PVCs, ConfigMap, and RPC Service; node-local pool members run in the parent pool DaemonSet
    GarageBucketBucket, aliases, quotas, website, lifecycle, grantsGenerated Secret relationships and Garage-side bucket state
    GarageKeyS3 key material and permissionsGenerated Kubernetes Secret and Garage-side key/grants
    GarageAdminTokenStatic Admin API bootstrap token SecretGenerated Kubernetes Secret; it does not create a revocable Garage token row
    GarageReferenceGrantCross-namespace reference authorizationNo workload; status reports users