Subsystem Application
This Helm chart provides a streamlined and validated approach to deploying applications in Kubernetes, significantly reducing configuration effort compared to pure Helm.
It enforces best practices, automates naming conventions, and adds built-in validation, ensuring that deployments are consistent, reliable, and easy to manage.
An application is a standalone software unit that provides specific functionality within a subsystem. This chart ensures that applications are deployed in a structured, unified, and validated manner, reducing complexity and operational overhead.
Key Benefits:
- Less Configuration, Faster Deployment – Predefined defaults and structured settings eliminate the need for extensive customization.
- Built-in Validation – Prevents misconfigurations by enforcing Kubernetes naming constraints and required values.
- Consistent Naming Conventions – Automatically generates resource names to ensure clarity and avoid conflicts.
- Preconfigured Workloads – Supports microservices and web applications with minimal setup effort.
- Metadata-Driven Configuration – Injects key metadata into application pods as environment variables.
- Unified Deployments Across Applications – Ensures that all applications within a structured environment follow a consistent deployment pattern, improving maintainability.
Why This Chart?
In pure Helm, deployments require manual validation, careful naming, and extensive configuration to ensure consistency. This chart automates these aspects, providing smart defaults, validation mechanisms, and enforced unification, allowing applications to be deployed quickly, reliably, and with minimal manual input.
Getting Started with a Sample
If you prefer to start with a practical example, jump to the Real World Samples section to see Helm chart values in action. These examples provide ready-to-use configurations that demonstrate common deployment scenarios.
Installation
helm install \
oci://ghcr.io/exordis/helm-charts/cicd-subsystem-application \
--version 0.12.1
Values
Key Principles
- Single application deployment – There is only one workload for the application.
- Consistent Metadata – All generated k8s resources manifests have labels identifying the application .
- Entities based configuration – k8s resources are defined in
Values
as entities dictionaries where key isid
of the entity which is used for referencing withinValues
and expanded according to Naming Conventions to getmetadata.name
. - Reuse of native k8s manifest format – any entity may be defined with just
spec
field containing manifestspec
(in some cases wherespec
is not part of manifest same is applied to kind specific fields e.g.data
for ConfigMap) - Shortcuts rather than custom format – all chart specific configuration for entity is optional and acts as shortcut - e.g.
ingress.services
defines how to expose services and when processed it is transformed to patch for ingressspec
. While same may be achieved with manually writtenspec
. - Validation – when entity is configured in chart specific way all possible validations are applied to highlight misconfigurations to developer. Chart has json schema validation.
- Defaults – most common use case is default to minimize required configuration. Any default may be overridden. E.g. Service port defaults to point to application container port with the same name
Metadata
global:
product: Some Product
subsystem: cicd
environment: test
application: sample
applicationType: service
instance: docs
version: 1.0.0
All metadata values, except product
, are used to build resource names following Naming Conventions and are available in application pods as environment variables. To comply with Kubernetes naming constraints, they are validated using the regex ^[a-z]([-a-z0-9]*[a-z0-9])?$
.
product
is used only as a reference value and represents the mapping of a subsystem as a technical asset to an organization's business unit or marketing name.
product
value is not used for resource naming to prevent massive Kubernetes resource renaming in case of a subsystem
handover between business units or a marketing name change.
Note
Kubernetes has a 64-character limit for resource names, so metadata values should be as short as possible to avoid exceeding this limit while maintaining clear identification.
Global Metadata Values
The following values are defined within the global section as they belong to the subsystem context. If the application Helm chart is deployed as part of a subsystem, these values should be provided by the subsystem Helm chart and remain the same for all subsystem
applications:
subsystem
-
name of the
subsystem
owning the application.Mandatory
product
-
name of product owning the
subsystem
.Mandatory
environment
-
environment application is being deployed to.
Mandatory
Application Specific Metadata Values:
version
-
version of the application. Appears as value of
app.kubernetes.io/version
label of all manifests generated by the chart and used as default docker images versionAll containers defined in values are using root
version
,registry
,repository
as default values ofimage
fields.Mandatory
application
-
application name
Mandatory
applicationType
-
type of the application: -
service
- microservice -web
- web applicationDefault:
service
instance
-
name of the instance of the application. E.g. subsystem may deploy multiple application instances in sharding scenarios. If there is only one instance this field is ok to be omitted.
Default: null
Docker Default Values
registry: registry.gitlab.com
repository: cicd-unittests
registry
-
Default registry to pull application docker images from.
Default:
docker.io
repository
-
Default repository (image name)
Default: application canonical name
[Values.global.subsystem]-[Values.global.application]
(format may be changed with Naming Conventions ).
Envs config map
envs
configMap is always generated. It defines environment variables to be available in all containers of application.
Definition in values
envs:
VARIABLE: value
Metadata environment variables
Metadata variables are added to the envs
ConfigMap. If envs
is missing from values, the ConfigMap contains only metadata:
EXORDIS_PRODUCT
- Subsystem product nameEXORDIS_SUBSYSTEM
- Subsystem nameEXORDIS_APPLICATION
- Application canonical name as[Values.global.subsystem]-[Values.global.application]
EXORDIS_INSTANCE
- Instance nameEXORDIS_ENVIRONMENT
- Environment
Workload
workload:
kind: Deployment
replicas: 3
clusterRole: application
As the application is intended to be a microservice or web application, only one workload can be deployed.
(If multiple workloads are needed, each should be deployed independently within the same subsystem
, or the entire Helm chart may not be suitable for the use case.)
Application workload is configured with workload
:
enabled
-
Indicates whether to deploy application workload.
false
is degenerate case but may be useful in some cases e.g. if Resources deployed by the chart have independent lifecycle withinsubsystem
default:
true
kind
-
workload kind. Only Deployment is supported at the moment
default:
Deployment
replicas
-
number of pod replicas of workload.
default: 3
clusterRole
-
Workload cluster role. If value contains
id
of Cluster Role defined in.Values.clusterRoles
it is expanded to full name, otherwise value considered as pre-created Cluster Role and kept as is. IfclusterRole
is defined, ServiceAccount and binding to this role is created for application workloaddefault: null (no explicit cluster role to be assigned)
Note
Support of StatefulSet
workload kind to be added later
Entities
The remaining part of the values defines entities that are translated into Kubernetes manifests.
Entities are of the following types:
Resources
Standalone resources to be deployed.
Workload components
Components of workloads.
Application workload components are defined at the root level of values. If the workload.enable
value is set to false
, these components are ignored.
This ensures that only one application workload exists and allows changing the application workload type without significant changes in values
.
Batch workload components are defined as part of the corresponding workload.
Workloads
Application Workloads
Tuning of workload based on its type.
Batch Workloads
Batch workloads to be deployed.
Real world samples
It Tools
Deployment of it tools - set of IT tools like IPv4 address converter and Base64 string encoder/decoder.
# Metadata
global:
environment: prod
product: cicd
subsystem: samples
application: it-tools
# default registry and image
registry: docker.io
repository: corentinth/it-tools
# version of the application
# used as docker image version as not specified explicitly
version: 2024.10.22-7ca5933
# application workload main container
applicationContainer:
spec:
ports:
- containerPort: 80
name: http
protocol: TCP
services:
# Service with id 'ui'
# Will be expanded to 'samples-it-tools-ui' according to
# default naming conventions for metadata.name
ui:
ports:
# By default binds to application workload main container
# port with the same name and uses external port 80
http: {}
ingresses:
# Ingress wih id 'ui'.
# Will be expanded to 'samples-it-tools-ui' according to
# default naming conventions for metadata.name
ui:
services:
# bind to service with id 'ui'
ui:
hosts:
- it-tools.local
ports:
# expose service port 'http'.
# Path is overridden as default is /[service port name]
http:
- path: /
Homepage
Deployment of Homepage dashboard
# Metadata
global:
environment: prod
product: cicd
subsystem: samples
application: homepage
# default registry and image
registry: ghcr.io
repository: gethomepage/homepage
# version of the application
# used as docker image version as not specified explicitly
version: v0.10.9
# workload tuning
workload:
# Two replicas (default is 3)
replicas: 2
# custom cluster role. clusterRole with id 'discovery' is defined bellow
clusterRole: discovery
# additional environment variables to be added to all containers
# of the application.
envs:
# env vars to be used in homepage config
HOMEPAGE_VAR_UNIFI_PASSWORD: "passw0rd"
HOMEPAGE_VAR_UNIFI_USERNAME: "homepage"
clusterRoles:
# cluster role with id 'homepage'.
# Will be expanded to 'samples-prod-homepage-discovery' according to
# default naming conventions for metadata.name.
# Used for homepage to allow k8s discovery
discovery:
rules:
- apiGroups: [""]
resources: ["nodes", "pods","namespaces"]
verbs: ["get", "list"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list"]
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list"]
configMaps:
# ConfigMap wih id 'config'
# Will be expanded to 'samples-homepage-config' according to
# default naming conventions for metadata.name
# Holds homepage config.
config:
containers: []
data:
bookmarks.yaml: '{}'
docker.yaml: '{}'
kubernetes.yaml: |
mode: cluster
services.yaml: '{}'
settings.yaml: '{}'
widgets.yaml: |
- unifi_console:
url: https://unifi.local
password: '{{HOMEPAGE_VAR_UNIFI_PASSWORD}}'
username: '{{HOMEPAGE_VAR_UNIFI_USERNAME}}'
# application workload main container
applicationContainer:
spec:
ports:
- containerPort: 3000
name: http
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
initContainers:
# Application workload pod init container with id 'copy-config'
# Homepage reads configs and writes its data to the same dir so to have this dir RW:
# - ConfigMap with homepage config is mounted as 'config-ro' volume
# - Homepage is mounted as emptyDir volume
# - init container copies file from 'config-ro' to config dir volume
copy-config:
image:
registry: docker.io
repository: alpine
version: 3.21.3
spec:
command:
- sh
- '-c'
- |
cp -Lr /config-ro/* /config/
volumes:
# Application workload volume with id 'config'
# Holds homepage /app/config directory
config:
mounts:
application: /app/config
copy-config: /config
# Application workload volume with id 'config'
# Loads config files content from configmap with id 'config'
config-ro:
configMap: config
mounts:
copy-config: /config-ro
services:
# Service with id 'ui' (will be expanded to 'samples-homepage-ui' according to default naming conventions for metadata.name )
ui:
ports:
http: {}
ingresses:
# Ingress with id 'ui' (will be expanded to 'samples-homepage-ui' according to default naming conventions for metadata.name )
ui:
services:
# bind to service with id 'ui'
ui:
hosts:
- homepage.local
ports:
# expose service port 'http'. Path is overriden as default is /[service port name]
http:
- path: /