github.com/oam-dev/kubevela@v1.9.11/design/vela-core/apply-once-only.md (about)

     1  # Apply-Once-Only: Apply workload/trait only when spec is changed
     2  
     3  - Owner: Yue Wang(@captainroy-hy), Jianbo Sun(@wonderflow)
     4  - Date: 11/24/2020
     5  - Status: Implemented
     6  
     7  ## Intro
     8  When an ApplicationConfiguration is deployed, 
     9  vela-core will create(apply) corresponding workload/trait instances and keep them stay align with the `spec` defined in ApplicationConfiguration through periodical reconciliation. 
    10  
    11  If we run vela-core with `--apply-once-only` flag enabled, vela-core will never apply the workload and trait instance after they are applied once. Even if they are changed by others (e.g., trait controller, workload controller,etc). 
    12  Since the create operation is the only one apply operation occurring on workload/trait, we call this mechanism as `Apply Once Only`.
    13  
    14  ## A Motivational Example
    15  Here is a scenario from production environment to demonstrate how `Apply Once Only` works.
    16  
    17  ```yaml
    18  apiVersion: core.oam.dev/v1alpha2
    19  kind: ApplicationConfiguration
    20  metadata:
    21    name: example-appconfig
    22  spec:
    23    components:
    24      - componentName: example-component
    25  ---
    26  apiVersion: core.oam.dev/v1alpha2
    27  kind: Component
    28  metadata:
    29    name: example-component
    30  spec:
    31    workload:
    32      apiVersion: apps/v1
    33      kind: Deployment
    34      spec:
    35        ...
    36        template:
    37          spec:
    38            containers:
    39              - image: sample/app:1.0
    40                name: sample-app
    41  ```
    42  
    43  After deploying above ApplicationConfiguration, vela-core will create a Deployment with corresponding `PodTemplateSpec`. 
    44  
    45  In production env, it's possible to change the Deployment according to particular requirements, e.g., RolloutTrait, AutoscalerTrait,etc.
    46  Currently, we just use `kubectl` to simulate workload is changed bypass changing the ApplicationConfiguration. 
    47  Below cmd changes `spec.template.spec.containers[0].image` of the Deployment from `sample/app:1.0` to `sample/app:2.0`.
    48  ```shell
    49  cat <<EOF | kubectl patch deployment example-deploy --patch
    50  spec:
    51    template:
    52      spec:
    53        containers:
    54        - name: sample-app
    55          image: sample/app:2.0
    56  EOF
    57  ```
    58  
    59  Above change will trigger recreate of Pods owned by the Deployment. 
    60  But vela-core will change it back to `sample/app:1.0` in the following reconciliation soon or late. 
    61  That's not what we expect in some scenarios.
    62  
    63  Instead, we hope vela-core ignore reconciling the workload we changed and leave them as what they are now until we change the `spec` of their parent ApplicationConfiguration .
    64  
    65  ## Goals
    66  
    67  Add a startup parameter for vela-core controller to allow users choose whether to enable apply only once or not. 
    68  
    69  If enabled, workload/trait will be applied only one time for each resource generation (only when the corresponding appconfig/component is created or updated).
    70  After workload/trait created/updated and aligned to the generation of appconfig, vela-core will not apply them EXCEPT below situations:
    71  
    72  - The `spec` of ApplicationConfiguration is changed (new generation created)
    73  - The revision of Component is changed
    74  
    75  By default, the mechanism is disabled, vela-core will always reconcile and apply workload/trait periodically as usual.
    76  
    77  ## Implementation
    78  
    79  In each round of reconciliation, vela-core compares below Labels & Annotations of existing workload/trait with newly rendered ones before applying.
    80  
    81  After deploying the ApplicationConfiguration in the motivational example, the Deployment created by vela-core will have such labels and annotations.
    82  
    83  
    84  ```yaml
    85  apiVersion: apps/v1
    86  kind: Deployment
    87  metadata:
    88    annotations:
    89      deployment.kubernetes.io/revision: "1" 
    90      app.oam.dev/generation: "1"
    91    generation: 1
    92    labels:
    93      app.oam.dev/component: example-component
    94      app.oam.dev/name: example-appconfig
    95      app.oam.dev/resourceType: WORKLOAD
    96      app.oam.dev/revision: example-component-v1
    97    ...
    98  ```
    99  
   100  - `annotations["app.oam.dev/generation"]:"1" ` refers to the generation of AppConfig
   101  - `labels["app.oam.dev/revision"]:"example-component-v1" ` refers to the revision of Component
   102  
   103  These crucial two are propogated from AppConfig and Component during reconciliation.
   104  Any change applied to the Deployment directly has no impact on these labels and annotations, e.g., change the Deployment spec just like what we do in the [motivational example](#a-motivational-example).
   105  If `--apply-once-only` is enabled, since no discrepancy is found on labels and annotations, 
   106  vela-core controller will ignore applying the Deployment and leave it as what it is at that moment.
   107  
   108  By contrast, changes on AppConfig (changing `spec` creates new generation) and Component (updating Component creates new revision) will change the value of these labels and annotations.
   109  For example, if we update the spec of AppConfig, newly rendered workload is supposed to contain such labels and annotations.
   110  
   111  ```yaml
   112  apiVersion: apps/v1
   113  kind: Deployment
   114  metadata:
   115    annotations:
   116      deployment.kubernetes.io/revision: "1" 
   117      app.oam.dev/generation: "2" # generation of AppConfig changed
   118    generation: 1
   119    labels:
   120      app.oam.dev/component: example-component
   121      app.oam.dev/name: example-appconfig
   122      app.oam.dev/resourceType: WORKLOAD
   123      app.oam.dev/revision: example-component-v1
   124    ...
   125  ```
   126  Since discrepancy is found, vela-core controller will apply(update) the Deployment with newly rendered one.
   127  Thus, the changes we made to the Deployment before will also be eliminated.
   128  
   129  The same mechanism also works for Trait as well as Workload.
   130  
   131  ### Apply Once Only Force
   132  
   133  Based on the same mechanism as `apply-once-only`, `apply-once-only-force` has a more strict method for apply only once.
   134  
   135  It allows to skip re-creating a workload or trait that has already been DELETED from the cluster if its spec is not changed. 
   136  
   137  Besides the condition in `apply-once-only`, `apply-once-only-force` has one more condition:
   138  
   139  - if the component revision not changed, the workload will not be applied.
   140  
   141  ## Usage
   142  
   143  Three available options are provided to a vela-core runtime setup flag named `apply-one-only`, referring to three modes:
   144  
   145  - off - `apply-once-only` is disabled, this is the default option
   146  - on - `apply-once-only` is enabled
   147  - force - `apply-once-only-force` is enabled
   148  
   149  You can set it through `helm` chart value `applyOnceOnly` which is "off" by default if omitted, for example
   150  
   151  ```shell
   152  helm install -n vela-system kubevela ./charts/vela-core --set applyOnceOnly=on
   153  ```
   154  or
   155  ```
   156  helm install -n vela-system kubevela ./charts/vela-core --set applyOnceOnly=force
   157  ```
   158  
   159