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 ```