Skip to content

Commit

Permalink
add more example
Browse files Browse the repository at this point in the history
  • Loading branch information
hongchaodeng committed May 18, 2020
1 parent 1cf3d59 commit c411efa
Showing 1 changed file with 120 additions and 38 deletions.
158 changes: 120 additions & 38 deletions design/20200516-dependency.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# Resource Dependency in OAM

Owner: Hongchao Deng (@hongchaodeng)
Date: 05/17/2020
- Owner: Hongchao Deng (@hongchaodeng)
- Date: 05/17/2020

## Background

Before creating some application services or resources, we often need to rely on
other services or resources to be ready or have some data available first.
Creating one application service or resource often relies on other services or resources to be ready or.
This is a well-known pattern called dependency.

In OAM, we talk about dependency in two different contexts:
Expand All @@ -28,7 +27,7 @@ This proposal includes the following goals:
1. **Describe ordering for k8s-style resources**.
One key part of depedency is ordering, i.e. creating resources one after another.
For example, resource A could not be created unless resource B has been ready (we will describe the meaning of ready below).
We want to and could achieve this by generic k8s resource design.
We want to apply this to k8s-style resource.
Since both components and traits are k8s-style resources, as long as we achieve this goal it should satisfy OAM requirements too.
2. **Describe field data passing between resources**.
If we think dependency as ordering, it is still not enough. We also need to handle field data passing.
Expand All @@ -44,13 +43,13 @@ In the following we will first go through the use cases and then propose a solut

## Use Cases

## 1. Web app and database
### 1. Web app and database

An applicationConfiguration consists of two components: web and db (database).
Web component should not be created until db component is created,
including public endpoint and connection secret to be made ready.

## 2. ML app workflow
### 2. ML app workflow

An ML applicationConfiguration from [Hypercycle ML](http://www.4paradigm.com/product/hypercycleml)
has three components:
Expand All @@ -65,12 +64,12 @@ They have strict ordering:
preprocess -> training -> serving
```

Without the previous stage finished, the next couldn't start due to lack of data.
Each component has to wait for data to be finished processing by the previous stage.
Today, ML frameworks including Tensorflow do not handle such dependency issue.
They rely on external tools (e.g. Argo Workflow) to handle it.
If we want to model them as Components in the same ApplicationConfiguration, the external tools could not help in this case.

## 3. Service-binding Trait
### 3. Service-binding Trait

Service binding is an OAM trait that we develop to bind user specified input such as env or file path
to specific data sources such as secrets or events.
Expand All @@ -79,19 +78,19 @@ More background introduction can be found in this [slides](https://docs.google.c
A service binding trait needs to be applied before creating the component.
Moreover, this kind of trait ordering is quite general and applies to many other traits we have seen.

## 4. NSQ deployment
### 4. NSQ deployment

An [NSQ](https://github.com/nsqio/nsq) cluster is composed with three components, that is nsqd nsqlookup and nsqadmin.
If we want to setup a nsq cluster, we should make sure nsqd is up before nsqlookup, then run a nsqadmin workload at last.
That is, nsqlookup consumes the result of nsqd component instance, nsqadmin consumes the result of nsqlookup component instance.
An [NSQ](https://github.com/nsqio/nsq) cluster is composed with three components: nsqd, nsqlookup and nsqadmin.
To set up a `nsq` cluster, one needs to first make sure that the `nsqd` component is up, then start and make sure that the `nsqlookup` component is up, and run the `nsqadmin` component at last.
nsqlookup consumes the result of nsqd component instance, and nsqadmin consumes the result of nsqlookup component instance.
If we couldn't describe the dependencies order between the components, the component couldn't run properly.

This is similar to [kubernetes#65502](https://github.com/kubernetes/kubernetes/issues/65502) , the issue describes dependencies between containers on the same Pod, while we are discussing dependencies between components.


## Proposal

The overall idea is to have each resource object specify data inputs which are from fields of other resources.
The overall idea is to have each resource object specify data inputs coming from other resources' fields.
The OAM runtime will build the dependency graph accordingly and only create resources once the data inputs are ready.
To achieve this, we propose:

Expand All @@ -115,24 +114,26 @@ To achieve this, we propose:
}
```

For example, a web application may specify input of mysql secret:
For example, a web application may specify input of mysql secret in yaml style:

```yaml
datainputs:
- toFieldPaths: ["spec.connSecret"]
fromDataOutput: mysql-conn-secret # check below DataOutput of the same name
```

The DataInputs object will be json-marshaled and put into an annotation key `core.oam.dev/datainputs`.
The above DataInputs will be json-marshaled and put into an annotation key `core.oam.dev/datainputs` of my-app Deployment:

```yaml
kind: Deployment
metadata:
name: my-web-app
name: my-app
annotations:
"core.oam.dev/datainputs": "[{\"toFieldPaths\": [\"spec.connSecret\"],\"fromDataOutput\": \"mysql-conn-secret\"}]"
```

Note that my-app won't be created since it has DataInputs dependency to satisfy.
2. Add a new CRD `DataOutput` with the following definition:
```go
Expand Down Expand Up @@ -167,7 +168,7 @@ To achieve this, we propose:
}
```
For example, a mysql connection secret data output would look like:
For example, a mysql connection secret DataOutput coming from RDSInstance my-rds would look like:
```yaml
apiVersion: core.oam.dev/v1alpha2
Expand All @@ -182,21 +183,70 @@ To achieve this, we propose:
fieldPath: "status.connSecret"
```
The OAM runtime will automatically build a dependency between the `my-web-app` and `my-rds`.
It will wait until `my-rds` object's field `status.connSecret` has value,
and then creates `my-web-app` object with corresponding values passed to its fields.
In this case, the OAM runtime will automatically build a dependency between the `my-app` and `my-rds`.
It will wait until `my-rds` object's field `status.connSecret` has value, and marks the DataOutput status ready:

```yaml
apiVersion: alibaba.oam.crossplane.io/v1alpha1
kind: RDSInstance
metadata:
name: my-rds
spec:
...
status:
connSecret: mysql-conn # OAM runtime will wait for this field to have value
---
apiVersion: core.oam.dev/v1alpha2
kind: DataOutput
metadata:
name: mysql-conn-secret
...
status:
ready: true # once the above connSecret has value this will be marked true
```

At this point, the dependency has been satisfied.
The OAM runtime will create `my-app` object with corresponding values passed to its fields.

```yaml
kind: Deployment
metadata:
name: my-app
..
spec:
connSecret: mysql-conn
```

3. In OAM Component we put a special syntax sugar in `Parameters` fields:
as long asthe name of the Parameter matches the name of the DataOutput, it becomes a DataInput.

3. In order to integrate with OAM, we could model this feature as an OAM Trait as follows:
For example, the following parameter mysql-conn-secret in Component matches the name of DataOutput trait
in the same ApplicationConfiguration,

```yaml
kind: Component
metadata:
name: my-app
spec:
workload:
kind: Deployment
metadata:
name: my-app
parameters:
- name: mysql-conn-secret # this matches the name of DataOutput
fieldPaths: ["spec.connSecret"]
---
# In ApplicationConfiguration
components:
- componentName: my-web-app
- componentName: my-app
traits:
- trait:
apiVersion: core.oam.dev/v1alpha2
kind: DataOutput
metadata:
name: mysql-conn-secret
name: mysql-conn-secret # this matches the name of a component's parameter
spec:
resource:
apiVersion: alibaba.oam.crossplane.io/v1alpha1
Expand All @@ -205,29 +255,61 @@ To achieve this, we propose:
fieldPath: "status.connSecret"
```

In the component object, we could set a convention that the name of the DataOutput object
will correspond to the name of the Parameter.
In this case, the OAM runtime will build a dependency from my-app to my-rds,
wait until the field data ready, and create the workload patched with DataInputs annotation.

```yaml
kind: Component
kind: Deployment
metadata:
name: my-web-app
spec:
parameters:
- name: mysql-conn-secret
fieldPaths: ["spec.connSecret"]
name: my-app
annotations:
"core.oam.dev/datainputs": "[{\"toFieldPaths\": [\"spec.connSecret\"],\"fromDataOutput\": \"mysql-conn-secret\"}]"
```

In this case, the OAM runtime will match the parameter with the DataOutput trait, and use it to build dependency graph.
### Limitations

All the resources in the dependency graph should be created and managed by OAM runtime.

## Solution examples

In the proposal section we walk through how the proposal solves the web and db use case.
In this section we are providing more solution examples for the rest of use cases:
In this section we are providing more solution examples for dependency use cases.

1. Service binding trait:

1. ML app: TODO.
ApplicationConfiguration (partial):

2. Service binding: TODO.
```yaml
components:
- componentName: my-app
traits:
- trait:
apiVersion: core.oam.dev/v1alpha1
kind: ServiceBinding
metadata:
name: my-secret-binding
spec:
from:
secret:
name: my-secret
to:
env: true
- trait:
kind: DataOutput
resource:
apiVersion: core.oam.dev/v1alpha1
kind: ServiceBinding
name: my-secret-binding
fieldPath: "status.ready"
```

3. NSQ: TODO.
Component:

```yaml
kind: Component
metadata:
name: my-app
spec:
parameters:
- name: my-secret-binding
fieldPaths: [] # This parameter is for ordering purpose only
```

0 comments on commit c411efa

Please sign in to comment.