Merge "[AAI] Add model-loader tracing config"
[oom.git] / docs / sections / guides / development_guides / oom_dev_config_management.rst
1 .. This work is licensed under a Creative Commons Attribution 4.0
2 .. International License.
3 .. http://creativecommons.org/licenses/by/4.0
4 .. Copyright 2018-2020 Amdocs, Bell Canada, Orange, Samsung
5 .. Modification copyright (C) 2022 Nordix Foundation
6
7 .. Links
8
9 .. _oom_dev_config_management:
10
11
12 Configuration Management
13 ########################
14
15 ONAP is a large system composed of many components - each of which are complex
16 systems in themselves - that needs to be deployed in a number of different
17 ways.  For example, within a single operator's network there may be R&D
18 deployments under active development, pre-production versions undergoing system
19 testing and production systems that are operating live networks.  Each of these
20 deployments will differ in significant ways, such as the version of the
21 software images deployed.  In addition, there may be a number of application
22 specific configuration differences, such as operating system environment
23 variables.  The following describes how the Helm configuration management
24 system is used within the OOM project to manage both ONAP infrastructure
25 configuration as well as ONAP components configuration.
26
27 One of the artifacts that OOM/Kubernetes uses to deploy ONAP components is the
28 deployment specification, yet another yaml file.  Within these deployment specs
29 are a number of parameters as shown in the following example:
30
31 .. code-block:: yaml
32
33   apiVersion: apps/v1
34   kind: StatefulSet
35   metadata:
36     labels:
37       app.kubernetes.io/name: zookeeper
38       helm.sh/chart: zookeeper
39       app.kubernetes.io/component: server
40       app.kubernetes.io/managed-by: Tiller
41       app.kubernetes.io/instance: onap-oof
42     name: onap-oof-zookeeper
43     namespace: onap
44   spec:
45     <...>
46     replicas: 3
47     selector:
48       matchLabels:
49         app.kubernetes.io/name: zookeeper
50         app.kubernetes.io/component: server
51         app.kubernetes.io/instance: onap-oof
52     serviceName: onap-oof-zookeeper-headless
53     template:
54       metadata:
55         labels:
56           app.kubernetes.io/name: zookeeper
57           helm.sh/chart: zookeeper
58           app.kubernetes.io/component: server
59           app.kubernetes.io/managed-by: Tiller
60           app.kubernetes.io/instance: onap-oof
61       spec:
62         <...>
63         affinity:
64         containers:
65         - name: zookeeper
66           <...>
67           image: gcr.io/google_samples/k8szk:v3
68           imagePullPolicy: Always
69           <...>
70           ports:
71           - containerPort: 2181
72             name: client
73             protocol: TCP
74           - containerPort: 3888
75             name: election
76             protocol: TCP
77           - containerPort: 2888
78             name: server
79             protocol: TCP
80           <...>
81
82 Note that within the statefulset specification, one of the container arguments
83 is the key/value pair image: gcr.io/google_samples/k8szk:v3 which
84 specifies the version of the zookeeper software to deploy.  Although the
85 statefulset specifications greatly simplify statefulset, maintenance of the
86 statefulset specifications themselves become problematic as software versions
87 change over time or as different versions are required for different
88 statefulsets.  For example, if the R&D team needs to deploy a newer version of
89 mariadb than what is currently used in the production environment, they would
90 need to clone the statefulset specification and change this value.  Fortunately,
91 this problem has been solved with the templating capabilities of Helm.
92
93 The following example shows how the statefulset specifications are modified to
94 incorporate Helm templates such that key/value pairs can be defined outside of
95 the statefulset specifications and passed during instantiation of the component.
96
97 .. code-block:: yaml
98
99   apiVersion: apps/v1
100   kind: StatefulSet
101   metadata:
102     name: {{ include "common.fullname" . }}
103     namespace: {{ include "common.namespace" . }}
104     labels: {{- include "common.labels" . | nindent 4 }}
105   spec:
106     replicas: {{ .Values.replicaCount }}
107     selector:
108       matchLabels: {{- include "common.matchLabels" . | nindent 6 }}
109     # serviceName is only needed for StatefulSet
110     # put the postfix part only if you have add a postfix on the service name
111     serviceName: {{ include "common.servicename" . }}-{{ .Values.service.postfix }}
112     <...>
113     template:
114       metadata:
115         labels: {{- include "common.labels" . | nindent 8 }}
116         annotations: {{- include "common.tplValue" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }}
117         name: {{ include "common.name" . }}
118       spec:
119         <...>
120         containers:
121           - name: {{ include "common.name" . }}
122             image: {{ .Values.image }}
123             imagePullPolicy: {{ .Values.global.pullPolicy | default .Values.pullPolicy }}
124             ports:
125             {{- range $index, $port := .Values.service.ports }}
126               - containerPort: {{ $port.port }}
127                 name: {{ $port.name }}
128             {{- end }}
129             {{- range $index, $port := .Values.service.headlessPorts }}
130               - containerPort: {{ $port.port }}
131                 name: {{ $port.name }}
132             {{- end }}
133             <...>
134
135 This version of the statefulset specification has gone through the process of
136 templating values that are likely to change between statefulsets. Note that the
137 image is now specified as: image: {{ .Values.image }} instead of a
138 string used previously.  During the statefulset phase, Helm (actually the Helm
139 sub-component Tiller) substitutes the {{ .. }} entries with a variable defined
140 in a values.yaml file.  The content of this file is as follows:
141
142 .. code-block:: yaml
143
144   <...>
145   image: gcr.io/google_samples/k8szk:v3
146   replicaCount: 3
147   <...>
148
149
150 Within the values.yaml file there is an image key with the value
151 `gcr.io/google_samples/k8szk:v3` which is the same value used in
152 the non-templated version.  Once all of the substitutions are complete, the
153 resulting statefulset specification ready to be used by Kubernetes.
154
155 When creating a template consider the use of default values if appropriate.
156 Helm templating has built in support for DEFAULT values, here is
157 an example:
158
159 .. code-block:: yaml
160
161   imagePullSecrets:
162   - name: "{{ .Values.nsPrefix | default "onap" }}-docker-registry-key"
163
164 The pipeline operator ("|") used here hints at that power of Helm templates in
165 that much like an operating system command line the pipeline operator allow
166 over 60 Helm functions to be embedded directly into the template (note that the
167 Helm template language is a superset of the Go template language).  These
168 functions include simple string operations like upper and more complex flow
169 control operations like if/else.
170
171 OOM is mainly helm templating. In order to have consistent deployment of the
172 different components of ONAP, some rules must be followed.
173
174 Templates are provided in order to create Kubernetes resources (Secrets,
175 Ingress, Services, ...) or part of Kubernetes resources (names, labels,
176 resources requests and limits, ...).
177
178 a full list and simple description is done in
179 `kubernetes/common/common/documentation.rst`.
180
181 Service template
182 ----------------
183
184 In order to create a Service for a component, you have to create a file (with
185 `service` in the name.
186 For normal service, just put the following line:
187
188 .. code-block:: yaml
189
190   {{ include "common.service" . }}
191
192 For headless service, the line to put is the following:
193
194 .. code-block:: yaml
195
196   {{ include "common.headlessService" . }}
197
198 The configuration of the service is done in component `values.yaml`:
199
200 .. code-block:: yaml
201
202   service:
203    name: NAME-OF-THE-SERVICE
204    postfix: MY-POSTFIX
205    type: NodePort
206    annotations:
207      someAnnotationsKey: value
208    ports:
209    - name: tcp-MyPort
210      port: 5432
211      nodePort: 88
212    - name: http-api
213      port: 8080
214      nodePort: 89
215    - name: https-api
216      port: 9443
217      nodePort: 90
218
219 `annotations` and `postfix` keys are optional.
220 if `service.type` is `NodePort`, then you have to give `nodePort` value for your
221 service ports (which is the end of the computed nodePort, see example).
222
223 It would render the following Service Resource (for a component named
224 `name-of-my-component`, with version `x.y.z`, helm deployment name
225 `my-deployment` and `global.nodePortPrefix` `302`):
226
227 .. code-block:: yaml
228
229   apiVersion: v1
230   kind: Service
231   metadata:
232     annotations:
233       someAnnotationsKey: value
234     name: NAME-OF-THE-SERVICE-MY-POSTFIX
235     labels:
236       app.kubernetes.io/name: name-of-my-component
237       helm.sh/chart: name-of-my-component-x.y.z
238       app.kubernetes.io/instance: my-deployment-name-of-my-component
239       app.kubernetes.io/managed-by: Tiller
240   spec:
241     ports:
242       - port: 5432
243         targetPort: tcp-MyPort
244         nodePort: 30288
245       - port: 8080
246         targetPort: http-api
247         nodePort: 30289
248       - port: 9443
249         targetPort: https-api
250         nodePort: 30290
251     selector:
252       app.kubernetes.io/name: name-of-my-component
253       app.kubernetes.io/instance:  my-deployment-name-of-my-component
254     type: NodePort
255
256 In the deployment or statefulSet file, you needs to set the good labels in
257 order for the service to match the pods.
258
259 here's an example to be sure it matches (for a statefulSet):
260
261 .. code-block:: yaml
262
263   apiVersion: apps/v1
264   kind: StatefulSet
265   metadata:
266     name: {{ include "common.fullname" . }}
267     namespace: {{ include "common.namespace" . }}
268     labels: {{- include "common.labels" . | nindent 4 }}
269   spec:
270     selector:
271       matchLabels: {{- include "common.matchLabels" . | nindent 6 }}
272     # serviceName is only needed for StatefulSet
273     # put the postfix part only if you have add a postfix on the service name
274     serviceName: {{ include "common.servicename" . }}-{{ .Values.service.postfix }}
275     <...>
276     template:
277       metadata:
278         labels: {{- include "common.labels" . | nindent 8 }}
279         annotations: {{- include "common.tplValue" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }}
280         name: {{ include "common.name" . }}
281       spec:
282        <...>
283        containers:
284          - name: {{ include "common.name" . }}
285            ports:
286            {{- range $index, $port := .Values.service.ports }}
287            - containerPort: {{ $port.port }}
288              name: {{ $port.name }}
289            {{- end }}
290            {{- range $index, $port := .Values.service.headlessPorts }}
291            - containerPort: {{ $port.port }}
292              name: {{ $port.name }}
293            {{- end }}
294            <...>
295
296 The configuration of the service is done in component `values.yaml`:
297
298 .. code-block:: yaml
299
300   service:
301    name: NAME-OF-THE-SERVICE
302    headless:
303      postfix: NONE
304      annotations:
305        anotherAnnotationsKey : value
306      publishNotReadyAddresses: true
307    headlessPorts:
308    - name: tcp-MyPort
309      port: 5432
310    - name: http-api
311      port: 8080
312    - name: https-api
313      port: 9443
314
315 `headless.annotations`, `headless.postfix` and
316 `headless.publishNotReadyAddresses` keys are optional.
317
318 If `headless.postfix` is not set, then we'll add `-headless` at the end of the
319 service name.
320
321 If it set to `NONE`, there will be not postfix.
322
323 And if set to something, it will add `-something` at the end of the service
324 name.
325
326 It would render the following Service Resource (for a component named
327 `name-of-my-component`, with version `x.y.z`, helm deployment name
328 `my-deployment` and `global.nodePortPrefix` `302`):
329
330 .. code-block:: yaml
331
332   apiVersion: v1
333   kind: Service
334   metadata:
335     annotations:
336       anotherAnnotationsKey: value
337     name: NAME-OF-THE-SERVICE
338     labels:
339       app.kubernetes.io/name: name-of-my-component
340       helm.sh/chart: name-of-my-component-x.y.z
341       app.kubernetes.io/instance: my-deployment-name-of-my-component
342       app.kubernetes.io/managed-by: Tiller
343   spec:
344     clusterIP: None
345     ports:
346       - port: 5432
347         targetPort: tcp-MyPort
348         nodePort: 30288
349       - port: 8080
350         targetPort: http-api
351         nodePort: 30289
352       - port: 9443
353         targetPort: https-api
354         nodePort: 30290
355     publishNotReadyAddresses: true
356     selector:
357       app.kubernetes.io/name: name-of-my-component
358       app.kubernetes.io/instance:  my-deployment-name-of-my-component
359     type: ClusterIP
360
361 Previous example of StatefulSet would also match (except for the `postfix` part
362 obviously).
363
364 Creating Deployment or StatefulSet
365 ----------------------------------
366
367 Deployment and StatefulSet should use the `apps/v1` (which has appeared in
368 v1.9).
369 As seen on the service part, the following parts are mandatory:
370
371 .. code-block:: yaml
372
373   apiVersion: apps/v1
374   kind: StatefulSet
375   metadata:
376     name: {{ include "common.fullname" . }}
377     namespace: {{ include "common.namespace" . }}
378     labels: {{- include "common.labels" . | nindent 4 }}
379   spec:
380     selector:
381       matchLabels: {{- include "common.matchLabels" . | nindent 6 }}
382     # serviceName is only needed for StatefulSet
383     # put the postfix part only if you have add a postfix on the service name
384     serviceName: {{ include "common.servicename" . }}-{{ .Values.service.postfix }}
385     <...>
386     template:
387       metadata:
388         labels: {{- include "common.labels" . | nindent 8 }}
389         annotations: {{- include "common.tplValue" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }}
390         name: {{ include "common.name" . }}
391       spec:
392         <...>
393         containers:
394           - name: {{ include "common.name" . }}
395
396 Dependency Management
397 ---------------------
398 These Helm charts describe the desired state
399 of an ONAP deployment and instruct the Kubernetes container manager as to how
400 to maintain the deployment in this state.  These dependencies dictate the order
401 in-which the containers are started for the first time such that such
402 dependencies are always met without arbitrary sleep times between container
403 startups.  For example, the SDC back-end container requires the Elastic-Search,
404 Cassandra and Kibana containers within SDC to be ready and is also dependent on
405 DMaaP (or the message-router) to be ready - where ready implies the built-in
406 "readiness" probes succeeded - before becoming fully operational.  When an
407 initial deployment of ONAP is requested the current state of the system is NULL
408 so ONAP is deployed by the Kubernetes manager as a set of Docker containers on
409 one or more predetermined hosts.  The hosts could be physical machines or
410 virtual machines.  When deploying on virtual machines the resulting system will
411 be very similar to "Heat" based deployments, i.e. Docker containers running
412 within a set of VMs, the primary difference being that the allocation of
413 containers to VMs is done dynamically with OOM and statically with "Heat".
414 Example SO deployment descriptor file shows SO's dependency on its mariadb
415 data-base component:
416
417 SO deployment specification excerpt:
418
419 .. code-block:: yaml
420
421   apiVersion: apps/v1
422   kind: Deployment
423   metadata:
424     name: {{ include "common.fullname" . }}
425     namespace: {{ include "common.namespace" . }}
426     labels: {{- include "common.labels" . | nindent 4 }}
427   spec:
428     replicas: {{ .Values.replicaCount }}
429     selector:
430       matchLabels: {{- include "common.matchLabels" . | nindent 6 }}
431     template:
432       metadata:
433         labels:
434           app: {{ include "common.name" . }}
435           release: {{ .Release.Name }}
436       spec:
437         initContainers:
438         - command:
439           - /app/ready.py
440           args:
441           - --container-name
442           - so-mariadb
443           env:
444   ...