Skip to content

Commit c72831b

Browse files
committed
Pluggable components injection proposal
Signed-off-by: Marcos Candeia <[email protected]>
1 parent 938c124 commit c72831b

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
# Pluggable components injector
2+
3+
- Author(s): Marcos Candeia (@mcandeia)
4+
- State: Ready for Implementation
5+
- Updated: 11/21/2022
6+
7+
## Overview
8+
9+
Pluggable components are components that are not included as part of the runtime, as opposed to built-in ones that are included. The major difference between pluggable components and built-in components is the operational burden related to bootstrap/start the pluggable component process that are not necessary when using a built-in one since they run in the same process as Dapr runtime. This operational burden is present in many ways when using pluggable components and can lead to errors and hard debugging. In addition, there are certain configurations that are tied to the Dapr and how the runtime registers the pluggable component that is repetitive and can be better handled by Dapr instead of delegating this responsibility to the end-user. This proposal suggest the addition of a new mode of execution for selected pluggable components: injectable pluggable components.
10+
11+
## Background
12+
13+
#### Decrease the operational burden
14+
15+
Even considering the new pluggable components annotation from [#5402](https://github.com/dapr/dapr/issues/5402), setting up applications to properly work with pluggable components still not an easy task due to the operational related to bootstrapping containers over and over again for each application that the user needs, especially if you consider that components are often not well [scoped](https://docs.dapr.io/operations/components/component-scopes/). Without scope, a component make itself available for all applications within the same namespace, meaning that every deployment/pod should re-do the same manual job of mounting volumes, declaring environment variables and pinning container images.
16+
17+
So let's say you have an application named `my-app` and, another one named `my-app-2`, your two deployments/pods will look like the following:
18+
19+
```yaml
20+
apiVersion: apps/v1
21+
kind: Deployment
22+
metadata:
23+
name: app
24+
labels:
25+
app: app
26+
spec:
27+
replicas: 1
28+
selector:
29+
matchLabels:
30+
app: app
31+
template:
32+
metadata:
33+
labels:
34+
app: app
35+
annotations:
36+
dapr.io/pluggable-components: "component"
37+
dapr.io/app-id: "my-app"
38+
dapr.io/enabled: "true"
39+
spec:
40+
volumes:
41+
- name: my-component-required-volume
42+
emptyDir: {}
43+
containers:
44+
- name: my-app
45+
image: my-app-image:latest
46+
### This is the pluggable component container.
47+
- name: component
48+
image: component:v1.0.0
49+
volumes:
50+
- name: my-component-required-volume
51+
mountPath: "/my-data"
52+
env:
53+
- name: MY_ENV_VAR_NAME
54+
value: MY_ENV_VAR_VALUE
55+
56+
---
57+
apiVersion: apps/v1
58+
kind: Deployment
59+
metadata:
60+
name: app-2
61+
labels:
62+
app: app-2
63+
spec:
64+
replicas: 1
65+
selector:
66+
matchLabels:
67+
app: app-2
68+
template:
69+
metadata:
70+
labels:
71+
app: app-2
72+
annotations:
73+
dapr.io/pluggable-components: "component"
74+
dapr.io/app-id: "my-app-2"
75+
dapr.io/enabled: "true"
76+
spec:
77+
volumes:
78+
- name: my-component-required-volume
79+
emptyDir: {}
80+
containers:
81+
- name: my-app-2
82+
image: my-app-2-image:latest
83+
### This is the pluggable component container.
84+
- name: component
85+
image: component:v1.0.0
86+
volumes:
87+
- name: my-component-required-volume
88+
mountPath: "/my-data"
89+
env:
90+
- name: MY_ENV_VAR_NAME
91+
value: MY_ENV_VAR_VALUE
92+
```
93+
94+
Notice that everything related to the pluggable component container is repeated, and if you have a third application that doesn't require your pluggable component to work, so you have to scope your component to be initialized with only these two declared deployments/pods.
95+
96+
```yaml
97+
apiVersion: dapr.io/v1alpha1
98+
kind: Component
99+
metadata:
100+
name: my-component
101+
spec:
102+
type: state.my-component
103+
version: v1
104+
metadata: []
105+
scopes:
106+
- "my-app"
107+
- "my-app-2"
108+
```
109+
110+
For each deployment that you have to add in your cluster, if that requires such pluggable component, you must also add in the scope list of the component spec, which ends up being error prone and intrusive.
111+
112+
#### Component spec atomicity/self-contained
113+
114+
Allow interchangeable/swappable components are one of the top amazing features that we provide, with that, a user can, in runtime, swap out a component with the same interface for another. Pluggable components made this behavior more difficult to maintain as it requires coordination, for a small time window, the user must provide a way to Dapr access both components at same time, otherwise it becomes very difficult to orchestrate that change manually.
115+
To exemplify, suppose that we want to replace the Redis PubSub with the Kafka PubSub, and they are pluggable components. This is not only a matter of replacing the component spec itself, but it will require orchestrating the related deployments, otherwise it would lead in having an application pointing out to Kafka but with no Kafka pluggable component running and vice-versa.
116+
117+
The following diagram is exemplifying how that orchestrated change must applied:
118+
119+
<img width="466" alt="image" src="https://user-images.githubusercontent.com/5839364/201184828-d4e7357b-716a-4a3b-b7a5-dd22d1be7cda.png">
120+
121+
> That can't be avoided in scenarios where Dapr is not present as an orchestrator, for instance, self-hosted mode, but there are platforms that supports extensibility for orchestrating applications and its dependencies, like Kubernetes.
122+
123+
re: You can argue that Kubernetes solve this scenario by reconciling the cluster state until it succeeds, but still, it severely degrade the user experience when requires additional knowledge to build their applications with Dapr.
124+
125+
## Related Items
126+
127+
### Related proposals
128+
129+
[Pluggable components Annotations](https://github.com/dapr/dapr/issues/5402)
130+
131+
### Related issues
132+
133+
N/A
134+
135+
## Expectations and alternatives
136+
137+
### What is in scope for this proposal?
138+
139+
This proposal aims to add a new execution mode for pluggable components, the dapr-injected pluggable components.
140+
141+
### What is deliberately _not_ in scope?
142+
143+
This proposal does not aims to manage users' pluggable components code. The goal here is to provide a better UX when using pluggable components while decrease the operation burden.
144+
145+
## Implementation Details
146+
147+
### Design
148+
149+
This proposal aims to add a new execution mode for pluggable components, the dapr-injected pluggable components, that makes the operational behind remarkable like the built-in components. The operational burden is still present somewhere but divided into small reusable pieces.
150+
151+
<meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-69570229-7fff-318a-575c-cff928d2ef5b"><p dir="ltr" style="line-height:1.38;background-color:#ffffff;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">&nbsp;</span></p><div dir="ltr" style="margin-left:0pt;" align="left">
152+
153+
| Type | Injected by Dapr | Managed by User/Unmanaged |
154+
| ----------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
155+
| Configuration | Dapr Injects env vars and mount the shared volumes | The user manually mounts and declares shared volumes |
156+
| Container updates | Dapr automatically detects and applies, rolling out changes based on declared components | Users must redeploy their applications with the new desired version |
157+
| Persona | Cluster operator/End user | End user |
158+
| Scope | Does not need to be scoped | If not scoped, all applications should have deployed the pluggable component, otherwise runtime errors might happen |
159+
160+
</div></b>
161+
162+
#### Component spec annotations
163+
164+
The component spec is still the entry point for all component types being pluggable or not, given that the pluggable components are a subset of all users declared components, even more, the pluggable components can be inferred from the declared components, we can actually leverage that property to extend our component spec, by adding custom annotations to allow Dapr to inject the component container at the time the Injector is also injecting the Dapr sidecar container.
165+
166+
Example:
167+
168+
```yaml
169+
apiVersion: dapr.io/v1alpha1
170+
kind: Component
171+
metadata:
172+
name: my-component
173+
annotations:
174+
dapr.io/component-container-image: "component:v1.0.0"
175+
spec:
176+
type: state.my-componentƒ
177+
version: v1
178+
metadata: []
179+
```
180+
181+
Optionally you can mount volumes and add env variables into the containers by using the `dapr.io/component-container-volume-mounts(-rw)` and `dapr.io/component-container-env` annotations.
182+
183+
```yaml
184+
apiVersion: dapr.io/v1alpha1
185+
kind: Component
186+
metadata:
187+
name: my-component
188+
annotations:
189+
dapr.io/component-container-image: "component:v1.0.0"
190+
dapr.io/component-container-volume-mounts: "volume-name:/volume-path,volume-name-2:/volume-path-2" # read-only, "$VOLUME_NAME:$VOLUME_PATH,$VOLUME_NAME_2:$VOLUME_PATH2"
191+
dapr.io/component-container-volume-mounts-rw: "volume-name-rw:/volume-path-rw,volume-name-2-rw:/volume-path-2-rw" # read-write "$VOLUME_NAME:$VOLUME_PATH,$VOLUME_NAME_2:$VOLUME_PATH2"
192+
dapr.io/component-container-env: "env-var=env-var-value,env-var-2=env-var-value-2" #optional "$ENV_NAME=$ENV_VALUE,$ENV_NAME_2=$ENV_VALUE_2"
193+
spec:
194+
type: state.my-component
195+
version: v1
196+
metadata: []
197+
```
198+
199+
By default the injector creates undeclared volumes as `emptyDir` volumes, if you want a different volume type you should declare it by yourself in your pods.
200+
201+
#### Pod annotations
202+
203+
In order to allow users to turn off the component injector for their pod, a new annotation will be available, similar to the one that we have for enabling dapr: `dapr.io/inject-pluggable-components:"true"`. Let's rewrite the previous examples using the injected pluggable components feature, it would be something like:
204+
205+
The apps deployments/pods:
206+
207+
```yaml
208+
apiVersion: apps/v1
209+
kind: Deployment
210+
metadata:
211+
name: app
212+
labels:
213+
app: app
214+
spec:
215+
replicas: 1
216+
selector:
217+
matchLabels:
218+
app: app
219+
template:
220+
metadata:
221+
labels:
222+
app: app
223+
annotations:
224+
dapr.io/inject-pluggable-components: "true"
225+
dapr.io/app-id: "my-app"
226+
dapr.io/enabled: "true"
227+
spec:
228+
containers:
229+
- name: my-app
230+
image: my-app-image:latest
231+
---
232+
apiVersion: apps/v1
233+
kind: Deployment
234+
metadata:
235+
name: app-2
236+
labels:
237+
app: app-2
238+
spec:
239+
replicas: 1
240+
selector:
241+
matchLabels:
242+
app: app-2
243+
template:
244+
metadata:
245+
labels:
246+
app: app-2
247+
annotations:
248+
dapr.io/inject-pluggable-components: "true"
249+
dapr.io/app-id: "my-app-2"
250+
dapr.io/enabled: "true"
251+
spec:
252+
containers:
253+
- name: my-app-2
254+
image: my-app-2-image:latest
255+
```
256+
257+
And the component spec:
258+
259+
```yaml
260+
apiVersion: dapr.io/v1alpha1
261+
kind: Component
262+
metadata:
263+
name: my-component
264+
annotations:
265+
dapr.io/component-container-image: "component:v1.0.0"
266+
dapr.io/component-container-volume-mounts: "my-component-required-volume;/my-data"
267+
dapr.io/component-container-env: "MY_ENV_VAR_NAME;MY_ENV_VAR_VALUE"
268+
spec:
269+
type: state.my-component
270+
version: v1
271+
metadata: []
272+
```
273+
274+
### Feature lifecycle outline
275+
276+
#### Expectations
277+
278+
The feature is expected to be delivered as part of dapr/dapr v1.10.0 as a preview feature together with the new pluggable components SDK.
279+
280+
#### Compatability guarantees
281+
282+
Pluggable components that has been used will not be affected by this.
283+
284+
#### Deprecation / co-existence with existing functionality
285+
286+
N/A
287+
288+
### Acceptance Criteria
289+
290+
N/A
291+
292+
## Completion Checklist
293+
294+
What changes or actions are required to make this proposal complete? Some examples:
295+
296+
- [] Change the sidecar injector to make requests to the operator for listing components (or list it using its own role)
297+
- [] Add 1 more annotation for pods `dapr.io/inject-pluggable-components: "true"` and 3 more for components `dapr.io/component-container-image`, `dapr.io/component-container-env` and `dapr.io/component-container-volume-mounts`
298+
- [] Add the components container injector based on declared components

0 commit comments

Comments
 (0)