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

     1  # Appfile: Extensible, User-friendly Application Config Format
     2  
     3  - Owner: Hongchao Deng (@hongchaodeng)
     4  - Date: 10/14/2020
     5  - Status: Implemented
     6  
     7  ## Table of Contents
     8  
     9  - [Intro](#intro)
    10  - [Goals](#goals)
    11  - [Proposal](#proposal)
    12    - [Registration via Definition/Capability](#registration-via-definitioncapability)
    13    - [Templating](#templating)
    14    - [CLI/UI interoperability](#cliui-interoperability)
    15    - [vela up](#vela-up)
    16  - [Examples](#examples)
    17  
    18  ## Intro
    19  
    20  Vela supports a user-friendly `docker-compose` style config format called `Appfile`. It allows you to define an application's workloads and traits with an opinionated, simplified API interface.
    21  
    22  Here's an example to deploy a NodeJS express service:
    23  
    24  ```yaml
    25  services:
    26    express-server:
    27      # this image will be used in both build and deploy config
    28      image: oamdev/testapp:v1
    29  
    30      build:
    31        # Here more runtime specific build templates will be supported, like NodeJS, Go, Python, Ruby.
    32        docker:
    33          file: Dockerfile
    34          context: .
    35  
    36      cmd: ["node", "server.js"]
    37  
    38      route:
    39        domain: example.com
    40        http: # match the longest prefix
    41          "/": 8080
    42  
    43      env:
    44        - FOO=bar
    45        - FOO2=sec:my-secret # map the key same as the env name (`FOO2`) from my-secret to env var
    46        - FOO3=sec:my-secret:key # map specific key from my-secret to env var
    47        - sec:my-secret # map all KV pairs from my-secret to env var
    48  
    49      files: # Mount secret as a file
    50        - /mnt/path=sec:my-secret
    51  
    52      scale:
    53        replicas: 2
    54        auto: # automatic scale up and down based on given metrics
    55          range: "1-10"
    56          cpu: 80 # if cpu utilization is above 80%, scale up
    57          qps: 1000 # if qps is higher than 1k, scale up
    58  
    59      canary: # Auto-create canary deployment. Only upgrade after verify successfully.
    60        replicas: 1 # canary deployment size
    61        headers:
    62          - "foo:bar.*"
    63  ```
    64  
    65  Save this file to project root dir, and run:
    66  
    67  ```bash
    68  vela up
    69  ```
    70  
    71  It will build container image, render deployment manifests in yaml, and apply them to the server.
    72  
    73  ### Extensible Design
    74  
    75  The Appfile could be extended with more configurations by adding more capabilities to the OAM system. The config fields in Appfile are strongly correlated to the [capabilities system of OAM](../../docs/en/design.md#capability-oriented) – Config fields are registered in the capabilities system and exposed via a [CUE template](https://cuelang.org/).
    76  
    77  Here is an example of a capability definition that platform builders register:
    78  
    79  ```yaml
    80  apiVersion: core.oam.dev/v1alpha2
    81  kind: WorkloadDefinition
    82  metadata:
    83    name: webservice
    84  spec:
    85    definitionRef:
    86      name: deployments.apps
    87    extension:
    88      template: |
    89        parameter: {
    90          // +vela:cli:enabled=true
    91          // +vela:cli:usage=specify commands to run in container
    92          // +vela:cli:short=c
    93          cmd: [...string]
    94        
    95          env: [...string]
    96        
    97          files: [...string]
    98  
    99          image: string
   100        }
   101        
   102        output: {
   103          apiVersion: "apps/v1"
   104          kind: "Deployment"
   105          metadata:
   106            name: context.name
   107          spec: {
   108            selector: {
   109              matchLabels:
   110                app: context.name
   111            }
   112            template: {
   113              metadata:
   114                labels:
   115                  app: context.name
   116              spec: {
   117                containers: [{
   118                  name:  context.name
   119                  image: parameter.image
   120                  command: parameter.cmd
   121                }]
   122              }
   123            }
   124          }
   125        }
   126  ```
   127  
   128  Apply the file to APIServer, and the fields will be extended into Appfile. Note that there are some conventions that differs Workloads and Traits, and around CLI flags. We will cover that more detailedly below.
   129  
   130  ## Goals
   131  
   132  The Appfile design has the following goals:
   133  
   134  1. Provide a user friendly, `docker-compose` style config format to developers.
   135  2. Configuration fields can be extended by registering more capabilities into OAM runtime.
   136  
   137  In the following, we will discuss technical details of the proposed design.
   138  
   139  ## Proposal
   140  
   141  ### Registration via Definition/Capability
   142  
   143  Vela allows platform builders to extend Appfile config fields by registering them via [capabilities system of OAM](../../docs/en/design.md#capability-oriented).
   144  
   145  The entire template should be put under `spec.extension.template` as raw string:
   146  
   147  ```yaml
   148  apiVersion: core.oam.dev/v1alpha2
   149  kind: WorkloadDefinition | TraitDefinition
   150  ...
   151  spec:
   152    extension:
   153      template: |
   154        parameter: {
   155        ...
   156  ```
   157  
   158  By running `vela system update` or other similar commands, vela cli will read all definitions from APIServer and sink necessary information locally including templates. The templates will be further used to render final deploy manifests.
   159  
   160  ### Templating
   161  
   162  Vela allows platform builders to write bespoke templates to extend Appfile configs. 
   163  
   164  #### Exposing Parameters
   165  
   166  A template starts with `parameter` and its definition:
   167  
   168  ```yaml
   169  parameter: {
   170    // +vela:cli:enabled=true
   171    // +vela:cli:usage=specify commands to run in container
   172    // +vela:cli:short=c
   173    cmd: [...string]
   174  }
   175  ```
   176  
   177  Here is the takeout:
   178  * The `parameter` defines the user input fields and is used to render final output with user input values. These fields will be exposed to users in Appfile.
   179  
   180  Note that there is difference in how Workload and Trait expose parameters.
   181  
   182  For Workload, each service will have a reserved field called `type` which is *webservice* by default. 
   183  Then all parameters are exposed as first level field under the service.
   184  
   185  ```yaml
   186  services:
   187    express-server:
   188      # type: webservice (default) | task
   189      cmd: ["node", "server.js"]
   190  ```
   191  
   192  For Trait, its type will be used as the name to contain its parameters. There is a restriction that the trait type should not conflict any of the Workload parameters' first level name.
   193  
   194  ```yaml
   195  services:
   196    express-server:
   197      route: # trait type
   198        domain: example.com
   199        http: # match the longest prefix
   200          "/": 8080
   201      
   202      # Workload parameters. The first level names do not conflict with trait type.
   203      cmd: ... 
   204      env: ...
   205  ```
   206  
   207  
   208  #### Rendering Outputs
   209  
   210  A template should also have an `output` block:
   211  
   212  ```yaml
   213  output: {
   214    apiVersion: "apps/v1"
   215    kind: "Deployment"
   216    metadata:
   217      name: context.name
   218    spec: {
   219          ...
   220          containers: [{
   221            name:  context.name
   222            image: parameter.image
   223            command: parameter.cmd
   224          }]
   225    }
   226  }
   227  ```
   228  
   229  Here is the takeout:
   230  * The object defined within `output` block will be the final manifest which is to `kubectl apply`.
   231  * `parameter` is used here to render user config values in.
   232  * A new object called `context` is used to render output. This is defined within vela-cli and vela-cli will fill its values based on each service dynamically. In above example, here is the value of the `context`:
   233      ```yaml
   234      context:
   235        name: express-server
   236      ```
   237      You can check the definition of `context` block via `vela template context`.
   238  
   239  Note that a TraitDefinition can have multiple outputs. In such case, just dismiss the `output` block and provide `outputs` block:
   240  
   241  ```yaml
   242  outputs: service: {
   243          ...
   244  }
   245  
   246  outputs: ingress: {
   247          ...
   248  }
   249  ```
   250  
   251  Under the hood, vela-cli will iterate over all services and generate one AppConfig to contain them, and for each service generate one Component and multiple traits.
   252  
   253  ### CLI/UI Interoperability
   254  
   255  For UI, The definition in a template will be used to generate v3 OpenAPI Schema and the UI will use that to render forms.
   256  
   257  For CLI, a one level parameter can be exposed via CLI by adding the following "tags" in the comment:
   258  
   259  ```yaml
   260  parameter: #webservice
   261  #webservice: {
   262    // +vela:cli:enabled=true
   263    // +vela:cli:usage=specify commands to run in container
   264    // +vela:cli:short=c
   265    cmd: [...string]
   266    ...
   267  }
   268  ```
   269  
   270  Here is the takeout:
   271  - The name of the parameter will be added as a flag, i.e. `--cmd`
   272  - "enabled" indicates whether this parameter should be exposed
   273  - "usage" is shown in help info
   274  - "short" is the short flag, i.e. `-c`
   275  
   276  ### `vela up`
   277  
   278  The vela-cli will have an `up` command to provide seamless workflow experience. Provide an `vela.yaml` Appfile in the same directory that you will run `vela up` and it is good to go. There is an example under `examples/testapp/` .
   279  
   280  ## Examples
   281  
   282  ## Multiple Services
   283  
   284  ```yaml
   285  services:
   286    frontend:
   287      build:
   288        image: oamdev/frontend:v1
   289        docker:
   290          file: ./frontend/Dockerfile
   291          context: ./frontend
   292      cmd: ["node", "server.js"]
   293  
   294    backend:
   295      build:
   296        image: oamdev/backend:v1
   297        docker:
   298          file: ./backend/Dockerfile
   299          context: ./backend
   300      cmd: ["node", "server.js"]
   301  ```
   302  
   303  ### Multiple Outputs in TraitDefinition
   304  
   305  ```yaml
   306  apiVersion: core.oam.dev/v1alpha2
   307  kind: TraitDefinition
   308  metadata:
   309    name: route
   310  spec:
   311    definitionRef:
   312      name: routes.standard.oam.dev
   313    extension:
   314      template: |
   315        parameter: #route
   316        #route: {
   317          domain: string
   318          http: [string]: int
   319        }
   320        
   321        // trait template can have multiple outputs and they are all traits
   322        outputs: service: {
   323          apiVersion: "v1"
   324          kind: "Service"
   325          metadata:
   326            name: context.name
   327          spec: {
   328            selector:
   329              app: context.name
   330            ports: [
   331              for k, v in parameter.http {
   332                port: v
   333                targetPort: v
   334              }
   335            ]
   336          }
   337        }
   338        
   339        outputs: ingress: {
   340          apiVersion: "networking.k8s.io/v1beta1"
   341          kind: "Ingress"
   342          spec: {
   343            rules: [{
   344              host: parameter.domain
   345              http: {
   346                paths: [
   347                  for k, v in parameter.http {
   348                    path: k
   349                    backend: {
   350                      serviceName: context.name
   351                      servicePort: v
   352                    }
   353                  }
   354                ]
   355              }
   356            }]
   357          }
   358        }
   359  ```