sigs.k8s.io/cluster-api-provider-aws@v1.5.5/docs/proposal/20220712-garbage-collection.md (about)

     1  ---
     2  title: External Resource Garbage Collection
     3  authors:
     4    - "@richardcase"
     5    - "@andrewmyhre"
     6  reviewers:
     7    - "@sedefsavas"
     8    - "@dlipovetsky"
     9  creation-date: 2022-07-12
    10  last-updated: 2022-07-20
    11  status: implemented
    12  see-also:
    13  - https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/1718
    14  - https://github.com/kubernetes-sigs/cluster-api-provider-aws/pull/3518
    15  replaces: []
    16  superseded-by: []
    17  ---
    18  
    19  # External Resource Garbage Collection
    20  
    21  ## Table of Contents
    22  
    23  - [External Resource Garbage Collection](#external-resource-garbage-collection)
    24    - [Table of Contents](#table-of-contents)
    25    - [Glossary](#glossary)
    26    - [Summary](#summary)
    27    - [Motivation](#motivation)
    28      - [Goals](#goals)
    29      - [Non-Goals/Future Work](#non-goalsfuture-work)
    30    - [Proposal](#proposal)
    31      - [User Stories](#user-stories)
    32        - [Story 1](#story-1)
    33        - [Story 2](#story-2)
    34        - [Story 3](#story-3)
    35        - [Story 4](#story-4)
    36    - [Requirements](#requirements)
    37      - [Functional](#functional)
    38      - [Non-Functional](#non-functional)
    39      - [Implementation Details/Notes/Constraints](#implementation-detailsnotesconstraints)
    40        - [Proposed Changes](#proposed-changes)
    41          - [API Changes](#api-changes)
    42          - [Controller Changes](#controller-changes)
    43          - [New Garbage Collection Service](#new-garbage-collection-service)
    44          - [clusterawsadm changes](#clusterawsadm-changes)
    45      - [Alternative Approaches Considered](#alternative-approaches-considered)
    46        - [Using CCM to do the delete](#using-ccm-to-do-the-delete)
    47      - [Risks and Mitigations](#risks-and-mitigations)
    48        - [Replicating CCM](#replicating-ccm)
    49        - [Similar functionality in upstream CAPI](#similar-functionality-in-upstream-capi)
    50    - [Upgrade Strategy](#upgrade-strategy)
    51    - [Additional Details](#additional-details)
    52      - [Test Plan](#test-plan)
    53      - [Graduation Criteria](#graduation-criteria)
    54        - [Alpha](#alpha)
    55        - [Beta](#beta)
    56        - [Stable](#stable)
    57    - [Implementation History](#implementation-history)
    58  
    59  ## Glossary
    60  
    61  - CAPA - An abbreviation of Cluster API Provider AWS.
    62  - ELB - Elastic Load Balancer
    63  - NLB - Network Load Balancer
    64  - CCM - Cloud Controller Manager
    65  
    66  ## Summary
    67  
    68  If you create a workload cluster using CAPA which then in turn creates a `Service` of type `LoadBalancer` this results in a load balancer being created in AWS for that service. The type of load balancer created by default is a **Classic ELB** but you can also create a NLB by annotating your service. For example:
    69  
    70  ```yaml
    71  apiVersion: v1
    72  kind: Service
    73  metadata:
    74    name: podinfo-nlb
    75    annotations:
    76      service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    77  ```
    78  
    79  If you try to delete the workload cluster using CAPI/CAPA then it will fail to delete the clusters infrastructure fully in AWS as the VPC is still being used by the NLB that was created. For example:
    80  
    81  ```text
    82  E0609 15:49:16.022022  ###### API Changes) b │
    83  │ efore detaching the gateway.\n\tstatus code: 400, request id: 65dc0fa0-584f-4256-baf5-a2aac2d2dde4" "reconciler group"="controlplane.cluster.x-k8s.io" "reconciler kind"="AWSManaged │
    84  │ ControlPlane" "name"="capi-managed-test-control-plane" "namespace"="default" 
    85  ```
    86  
    87  Currently, CAPA will attempt to delete all the resources it has directly created as part of the cluster lifecycle management. However, if the CCM in the workload cluster has created any resources then these will not be attempted to be deleted.
    88  
    89  This proposal outlines a new feature that will be added to CAPA that will delete externally created resources, such as load balancers & security groups, of the workload cluster. This will be referred to as **garbage collection**.
    90  
    91  The new feature is expected to be compatible with unmanaged (i.e. EC2 control plane) and EKS CAPA created clusters.
    92  
    93  ## Motivation
    94  
    95  Adopters of CAPA expect that a request to delete a cluster should succeed and preferably that there be no external AWS resources for that cluster orphaned.
    96  
    97  The traditional thinking is that a user should delete all the workloads on the cluster before deleting the actual cluster. But the reality is that some clusters are short-lived (testing & dev clusters are a good example) and these are normally deleted via `kubectl delete Cluster mytest` without deleting the resources from the cluster first.
    98  
    99  This proposal aims to make this a better experience for the users of CAPA.
   100  
   101  ### Goals
   102  
   103  1. To delete AWS resources that were created by CCM in the workload cluster.
   104  2. To work across unmanaged (i.e. EC2 control plane) and managed (i.e. EKS) clusters.
   105  3. Solution must work in a scenario where GitOps is used.
   106  
   107  ### Non-Goals/Future Work
   108  
   109  - Delete EBS volumes created by the CCM
   110    - This will be considered as part of future work
   111  - Clean up other resources created by something other than the CCM (for example a custom operator)
   112  - Fine grained control of which clusters will be garbage collected or not
   113    - Initially if the feature is enabled it will be an opt-out model
   114    - We will add fine-grained control in a future enhancement
   115  
   116  ## Proposal
   117  
   118  ### User Stories
   119  
   120  #### Story 1
   121  
   122  As a platform operator/engineer
   123  I want to delete a cluster and all its associated AWS resources
   124  When not using GitOps
   125  So that there are no orphaned/unused AWS resources
   126  
   127  #### Story 2
   128  
   129  As a platform operator/engineer
   130  I want to be able to delete a cluster and all its associated AWS resources
   131  When using a GitOps tools (like Flux/Argo)
   132  So that there are no orphaned/unused AWS resources
   133  
   134  #### Story 3
   135  
   136  As a platform operator/engineer
   137  I want to be able to opt-out a cluster of being garbage collected
   138  
   139  #### Story 4
   140  
   141  As a platform operator/engineer
   142  I want to be able to opt-in/opt-out a cluster for garbage collection
   143  After it has been created
   144  So that i can investigate/overcome issues
   145  
   146  ## Requirements
   147  
   148  ### Functional
   149  
   150  <a name="FR1">FR1.</a> CAPA MUST support cleaning up of AWS resources created by the CCM for a tenant cluster when not using GitOps.
   151  
   152  <a name="FR2">FR2.</a> CAPA MUST support cleaning up of AWS resources created by the CCM for a tenant cluster when using GitOps.
   153  
   154  <a name="FR3">FR3.</a> CAPA MUST support cleaning up of AWS resources for unmanaged and managed clusters
   155  
   156  <a name="FR4">FR4.</a> CAPA MUST support a way to opt-out of garbage collection at any point before cluster deletion.
   157  
   158  <a name="FR5">FR5.</a> CAPI MUST not allow me to delete a cluster fully until garbage collection has occurred.
   159  
   160  <a name="FR6">FR6.</a> CAPA SHOULD provide a way for me to opt-in or opt-out a cluster from garbage collection AFTER it has been created.
   161  
   162  ### Non-Functional
   163  
   164  <a name="NFR8">NFR8.</a> CAPA MUST be able to easily add additional AWS resource clean up in the future.
   165  
   166  <a name="NFR9">NFR9.</a> Unit tests MUST exist for new garbage collection code.
   167  
   168  <a name="NFR10">NFR10.</a> e2e tests MUST exist for the new garbage collection code for both unmanaged and managed clusters.
   169  
   170  ### Implementation Details/Notes/Constraints
   171  
   172  #### Proposed Changes
   173  
   174  In the initial implementation of garbage collection, if the feature is enabled, all clusters will be garbage collected by default. However, we will supply a means to opt-out at any time prior to cluster deletion as per [FR4](#FR4).
   175  
   176  > NOTE: garbage collection will be experimental initially and will be enabled via a feature flag.
   177  
   178  Garbage collection occurs during the reconciliation for the deletion of a workload cluster. The following sequence diagram depicts what will happen when you delete a workload cluster with CAPI/CAPA with this change. The numbers will be referenced in the following descriptions.
   179  
   180  ![gc deletion](20220712-garbage-collection-delete.svg)
   181  
   182  ##### API Changes
   183  
   184  If the garbage collection feature has been enabled via the feature flag then a user can mark a cluster as opting out of garbage collection ([FR4](#FR4)) when they apply the yaml to the cluster or at any time prior to deletion. This will be accomplished by annotating the **AWSCluster** or **AWSManagedControlPlane** with the `aws.cluster.x-k8s.io/external-resource-gc` annotation and setting its value to **false**.
   185  
   186  If the `aws.cluster.x-k8s.io/external-resource-gc` annotation is absent or its value is set to **true** then the CAPA created cluster will be garbage collected.
   187  
   188  This annotation name will be in a publicly exported package.
   189  
   190  ##### Controller Changes
   191  
   192  The controllers for `AWSCluster` and `AWSManagedControlPlane` will be modified so that on the creation of the controllers you can indicate that the garbage collection feature flag is enabled. In [main.go](../../main.go) we will look to see if the feature flag is enabled and pass this in when creating the controllers.
   193  
   194  The **reconcileDelete** of the controllers for `AWSCluster` and `AWSManagedControlPlane` will be modified so that garbage collection is performed when a the infra cluster is deleted. Garbage Collection will be encapsulated in a new service (gc_service).
   195  
   196  The point at which we do the garbage collection is important. If we do it too soon we run the risk of the resources being re-created in AWS. The **reconcileDelete** will have 3 distinct phases:
   197  
   198  - Delete CAPA owned AWS resources for the workload cluster that are not related to the **NetworkSpec**. This will be done via the existing services in CAPA (5, 6).
   199  - If the gc feature is enabled then **ReconcileDelete** will be called (7) on the new garbage collection service. Its the role of the garbage collection service to determine if GC should be done, identify the CCM created AWS resources for the cluster and delete them (8).
   200  - Delete CAPA owned AWS resources for the workload cluster that are related to the **NetworkSpec**. This will be done via the existing network service (9,10).
   201  
   202  ##### New Garbage Collection Service
   203  
   204  For cluster deletion there will be a **ReconcileDelete** function. The first task of this function is to determine if the workload cluster's AWS resources should be garbage collected. The AWS resources will be garbage collected if either of these are true:
   205  
   206  - the `aws.cluster.x-k8s.io/external-resource-gc` annotation is absent
   207  - the `aws.cluster.x-k8s.io/external-resource-gc` annotation exists and its value is set to **true**
   208  
   209  If the AWS resources are to be garbage collected the next task of **ReconcileDelete** is to identify the AWS resources that have been created for the workload cluster via its CCM. And then for the identified resources delete them in AWS.
   210  
   211  To identify the resources that the CCM has created for the cluster we will use the **AWS Resource Tagging API** to query for all  resources that have a label called `kubernetes.io/cluster/[CLUSTERNAME]` with a value of `owned`. Note `[CLUSTERNAME]` will be replaced with the Kubernetes cluster name.
   212  
   213  Based on the list of resources returned we will group these by the owing AWS service (i.e. **ec2**, **elasticloadbalancing**). The grouped resources will then be passed to a function for that service which will take care of cleaning up the resources in AWS via API calls (8).
   214  
   215  The reason we are grouping by AWS service is that order can matter when deleting. For example, with the **elasticloadbalancing** service you need to delete the load balancers before any target groups.
   216  
   217  We will need to create the gc service so that it's easy to add new clean up functions for services in the future [NFR8](#NFR8).
   218  
   219  The **ReconcileDelete** implementation is idempotent. This is important because the controller could crash at any point and the delete reconciliation would restart from the beginning again. This means our clean up functions could be called multiple times.
   220  
   221  > NOTE: we will initally not handle clean-up of EBS volumes due to the potential risk of accidental data deletion. This will be considered for a future enhancement.
   222  
   223  ##### clusterawsadm changes
   224  
   225  We would like to supply a way for the user to manually mark a cluster as requiring garbage collection and vice versa opting out of garbage collection [FR6](#FR6).
   226  
   227  We will add 2 new commands to `clusterawsadm` to perform this:
   228  
   229  - **clusterawsadm gc enable** - this will add the `aws.cluster.x-k8s.io/external-resource-gc` annotation to the infra cluster object and set its value to `true`.
   230  - **clusterawsadm gc disable** - this will add the `aws.cluster.x-k8s.io/external-resource-gc` annotation to the infra cluster object and sets its value `false`.
   231  
   232  ### Alternative Approaches Considered
   233  
   234  #### Using CCM to do the delete
   235  
   236  The initial implementation of the garbage collector relied on the CCM in the workload cluster doing the delete. When a cluster is deleted CAPA would pause the delete reconciliation until garbage collection has been done.
   237  
   238  The garbage collector (a separate controller) would:
   239  
   240  - Connect to the tenant cluster and get a list of `Services` of type `LoadBalancer`.
   241  - Delete each of the `Services` of type `LoadBalancer`. At this point, the CCM in the workload cluster at this point will delete the resources it created in AWS.
   242  - Requeue until all the services as deleted.
   243  - Once all the `Services` have been deleted in the workload cluster then we would mark the CAPA infra cluster to indicate that it had been garbage collection. This would probably be done via adding an annotation.
   244  
   245  After the cluster has been marked as garbage collected the normal delete reconciliation has be unpaused and start.
   246  
   247  **Benefits**
   248  
   249  - We don't have to write out own deletion code as we rely on the CCM.
   250  
   251  **Downsides**
   252  
   253  - With GitOps this is problematic. The garbage collector may delete a service but the tha GitOps operator could reapply the Service and the resources will get re-created. This would potentially surface as a weird timing bug.
   254  - We need to add code to all controllers to pause delete until gc has been done
   255  
   256  ### Risks and Mitigations
   257  
   258  #### Replicating CCM
   259  
   260  As we are not relying on the CCM to do the deletion it means that we run the risk of replicating large parts of the CCM. To mitigate this we will only focus on cleaning up resources that can potentially block the CAPA deletion process.
   261  
   262  #### Similar functionality in upstream CAPI
   263  
   264  There is the possibility that similar and more generalised functionality will be added to upstream CAPI. If this happens and it meets our needs then we will refactor this code to work with the new mechanism and if required deprecate this feature. To mitigate the impact we should keep this feature as experimental for longer than we would do normally as this gives us the ability to deprecate it quickly.
   265  
   266  ## Upgrade Strategy
   267  
   268  There are no API changes. However, we have introduced a new feature that will need to be enabled. For existing management clusters you will have to enable the `ExternalResourceGC` feature. This can be done when via editing the `Deployment` for CAPA or at the time of `clusterctl init`.
   269  
   270  If you enabled the feature for an existing CAPI management cluster the existing clusters will not be marked as requiring garbage collection. If you wanted to enabled garbage collection for those existing clusters then you can use the the new `clusterawsadm gc enable` command, or add annotations using any API client.
   271  
   272  ## Additional Details
   273  
   274  ### Test Plan
   275  
   276  - Unit tests to validate the functionality of the new garbage collection service
   277  - Unit tests to validate the functionality of the new **clusterawsadm** commands.
   278  - e2e tests to test clean-up for un-managed and managed clusters
   279  
   280  ### Graduation Criteria
   281  
   282  #### Alpha
   283  
   284  - Initial version as defined by this proposal
   285  
   286  #### Beta
   287  
   288  - At least 1 year in alpha
   289  - More control over which clusters will be garbage collected (i.e. via label selectors)
   290  - Ability to enable/disable which resources will be clean-up (i.e. optionally include EBS volumes)
   291  - Full e2e coverage.
   292  
   293  #### Stable
   294  
   295  - At least 6 months in beta
   296  - No alternative CAPI solution in active development
   297  
   298  ## Implementation History
   299  
   300  - [x] 2022/07/11: Change discussed CAPA office hours
   301  - [x] 2022/07/12: Initial proposal
   302  - [ ] 2022/07/20: Open proposal PR
   303  
   304  <!-- Links -->